Skip to content

Commit

Permalink
feat: useVideoTexture enable hls support (#1847)
Browse files Browse the repository at this point in the history
* feat: added optional Sprite or Plane geometry for SpriteAnimator

* fix: spriteanimator proper scale of instances

* feat: added support for hls to useVideoTexture

* fix: added cleanup for HLS object
  • Loading branch information
netgfx committed Mar 30, 2024
1 parent c147c2b commit 47d94d7
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 7 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3272,6 +3272,17 @@ function VideoMaterial({ src }) {

NB: It's important to wrap `VideoMaterial` into `React.Suspense` since, `useVideoTexture(src)` here will be suspended until the user shares its screen.

HLS - useVideoTexture supports .m3u8 HLS manifest via (https://github.com/video-dev/hls.js).

You can fine-tune via the hls configuration:

```
const texture = useVideoTexture('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8', {
hls: { abrEwmaFastLive: 1.0, abrEwmaSlowLive: 3.0, enableWorker: true }
})
```
>Available options: https://github.com/video-dev/hls.js/blob/master/docs/API.md#fine-tuning

#### useTrailTexture

<p>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
"tunnel-rat": "^0.1.2",
"utility-types": "^3.10.0",
"uuid": "^9.0.1",
"zustand": "^3.7.1"
"zustand": "^3.7.1",
"hls.js": "1.3.5"
},
"devDependencies": {
"@babel/core": "^7.14.3",
Expand Down
50 changes: 44 additions & 6 deletions src/core/useVideoTexture.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import * as THREE from 'three'
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import { useThree } from '@react-three/fiber'
import { suspend, preload, clear } from 'suspend-react'
import { suspend } from 'suspend-react'
import Hls, { HlsConfig } from 'hls.js'

interface VideoTextureProps extends HTMLVideoElement {
unsuspend?: 'canplay' | 'canplaythrough' | 'loadstart' | 'loadedmetadata'
start?: boolean
hls?: HLSConfiguration
}

interface HLSConfiguration {
hls: HlsConfig
}

export function useVideoTexture(src: string | MediaStream, props?: Partial<VideoTextureProps>) {
const { unsuspend, start, crossOrigin, muted, loop, ...rest } = {
const { unsuspend, start, crossOrigin, muted, loop, hls, ...rest } = {
unsuspend: 'loadedmetadata',
crossOrigin: 'Anonymous',
muted: true,
loop: true,
start: true,
playsInline: true,
hls: {},
...props,
}

const url = new URL(typeof src === 'string' ? src : '', window.location.href)
const shouldUseHLS = url.pathname.endsWith('.m3u8') && Hls.isSupported()
const hlsRef = useRef(shouldUseHLS ? new Hls({ ...hls }) : null)
const videoRef = useRef<HTMLVideoElement | null>(null)
const gl = useThree((state) => state.gl)

const texture = suspend(
() =>
new Promise((res, rej) => {
Expand All @@ -30,6 +43,25 @@ export function useVideoTexture(src: string | MediaStream, props?: Partial<Video
muted,
...rest,
})
videoRef.current = video

// hlsjs extension
if (typeof src === 'string') {
if (shouldUseHLS) {
const _hls: Hls | null = hlsRef.current
if (_hls) {
_hls.attachMedia(video)
_hls.on(Hls.Events.MEDIA_ATTACHED, () => {
_hls.loadSource(src)
})
}
} else {
video.src = src
}
} else if (src instanceof MediaStream) {
video.srcObject = src
}

const texture = new THREE.VideoTexture(video)
if ('colorSpace' in texture) (texture as any).colorSpace = (gl as any).outputColorSpace
else texture.encoding = gl.outputEncoding
Expand All @@ -38,11 +70,17 @@ export function useVideoTexture(src: string | MediaStream, props?: Partial<Video
}),
[src]
) as THREE.VideoTexture

useEffect(() => {
if (start) {
texture.image.play()
return () => texture.image.pause()
start && texture.image.play()

return () => {
if (hlsRef.current) {
hlsRef.current.destroy()
hlsRef.current = null
}
}
}, [texture, start])

return texture
}

0 comments on commit 47d94d7

Please sign in to comment.