Skip to content

Commit

Permalink
fix(useVideoTexture): lazy initialize hls.js when supported (#1919)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyJasonBennett authored Apr 13, 2024
1 parent 4a5add6 commit 153607d
Showing 1 changed file with 27 additions and 12 deletions.
39 changes: 27 additions & 12 deletions src/core/useVideoTexture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as THREE from 'three'
import { useEffect, useRef } from 'react'
import { useThree } from '@react-three/fiber'
import { suspend } from 'suspend-react'
import Hls, { HlsConfig } from 'hls.js'
import type { HlsConfig, default as Hls } from 'hls.js'

interface VideoTextureProps extends HTMLVideoElement {
unsuspend?: 'canplay' | 'canplaythrough' | 'loadstart' | 'loadedmetadata'
Expand All @@ -14,6 +14,23 @@ interface HLSConfiguration {
hls: HlsConfig
}

const IS_BROWSER =
typeof window !== 'undefined' &&
typeof window.document?.createElement === 'function' &&
typeof window.navigator?.userAgent === 'string'

let _HLSModule: typeof import('hls.js') | null = null
async function getHLS(url: URL, config: Partial<HlsConfig>): Promise<Hls | null> {
if (IS_BROWSER && url.pathname.endsWith('.m3u8')) {
_HLSModule ??= await import('hls.js')
if (_HLSModule.default.isSupported()) {
return new _HLSModule.default({ ...config })
}
}

return null
}

export function useVideoTexture(src: string | MediaStream, props?: Partial<VideoTextureProps>) {
const { unsuspend, start, crossOrigin, muted, loop, hls, ...rest } = {
unsuspend: 'loadedmetadata',
Expand All @@ -27,14 +44,13 @@ export function useVideoTexture(src: string | MediaStream, props?: Partial<Video
}

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 hlsRef = useRef<Hls | null>(null)
const videoRef = useRef<HTMLVideoElement | null>(null)
const gl = useThree((state) => state.gl)

const texture = suspend(
() =>
new Promise((res, rej) => {
new Promise(async (res, rej) => {
const video = Object.assign(document.createElement('video'), {
src: (typeof src === 'string' && src) || undefined,
srcObject: (src instanceof MediaStream && src) || undefined,
Expand All @@ -47,14 +63,13 @@ export function useVideoTexture(src: string | MediaStream, props?: Partial<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)
})
}
const _hls = (hlsRef.current = await getHLS(url, hls))

if (_hls) {
_hls.attachMedia(video)
_hls.on('hlsMediaAttached' as typeof Hls.Events.MEDIA_ATTACHED, () => {
_hls.loadSource(src)
})
} else {
video.src = src
}
Expand Down

0 comments on commit 153607d

Please sign in to comment.