Skip to content

Instantly share code, notes, and snippets.

@netgfx
Created October 11, 2023 07:35
Show Gist options
  • Save netgfx/889116d1ca6e58fe840e8375169d41eb to your computer and use it in GitHub Desktop.
Save netgfx/889116d1ca6e58fe840e8375169d41eb to your computer and use it in GitHub Desktop.
R3F HLS Video texture
import * as THREE from 'three'
import { useEffect, useRef } from 'react'
import { useThree } from '@react-three/fiber'
import { suspend, preload, clear } from 'suspend-react'
import Hls from 'hls.js'
export function useHLSVideoTexture(src, props) {
const { unsuspend, start, crossOrigin, muted, loop, ...rest } = {
unsuspend: 'loadedmetadata',
crossOrigin: 'Anonymous',
muted: true,
loop: true,
start: true,
playsInline: true,
...props
}
const videoRef = useRef()
const gl = useThree((state) => state.gl)
const createHLSBinding = () => {
if (Hls.isSupported()) {
var video = videoRef.current
console.log(video)
// If you are using the ESM version of the library (hls.mjs), you
// should specify the "workerPath" config option here if you want
// web workers to be used. Note that bundlers (such as webpack)
// will likely use the ESM version by default.
var hls = new Hls()
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
console.log('manifest loaded, found ' + data.levels.length + ' quality level')
})
hls.attachMedia(video)
hls.loadSource(src)
// If you are using the ESM version of the library (hls.mjs), you
// should specify the "workerPath" config option here if you want
// web workers to be used. Note that bundlers (such as webpack)
// will likely use the ESM version by default.)
// MEDIA_ATTACHED event is fired by hls object once MediaSource is ready
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
console.log('video and hls.js are now bound together !')
})
}
}
const texture = suspend(
() =>
new Promise((res, rej) => {
const video = Object.assign(document.createElement('video'), {
src: (typeof src === 'string' && src) || undefined,
srcObject: (src instanceof MediaStream && src) || undefined,
crossOrigin,
loop,
muted,
...rest
})
videoRef.current = video
createHLSBinding()
const texture = new THREE.VideoTexture(video)
if ('colorSpace' in texture) texture.colorSpace = gl.outputColorSpace
else texture.encoding = gl.outputEncoding
// flip video
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
texture.repeat.x = -1
video.addEventListener(unsuspend, () => res(texture))
}),
[src]
)
useEffect(() => {
start && texture.image.play()
}, [texture, start])
return texture
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment