only do the "manual loop" when absolutely necessary
This commit is contained in:
@@ -21,7 +21,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
active: boolean
|
active: boolean
|
||||||
setActive: () => void
|
setActive: () => void
|
||||||
onScreen: boolean
|
onScreen: boolean
|
||||||
lastKnownTime: React.MutableRefObject<number | undefined>
|
lastKnownTime: React.RefObject<number | undefined>
|
||||||
}) {
|
}) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
@@ -37,7 +37,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
const hlsRef = useHLS({
|
const {hlsRef, loop} = useHLS({
|
||||||
playlist: embed.playlist,
|
playlist: embed.playlist,
|
||||||
setHasSubtitleTrack,
|
setHasSubtitleTrack,
|
||||||
setError,
|
setError,
|
||||||
@@ -69,6 +69,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
onTimeUpdate={e => {
|
onTimeUpdate={e => {
|
||||||
lastKnownTime.current = e.currentTarget.currentTime
|
lastKnownTime.current = e.currentTarget.currentTime
|
||||||
}}
|
}}
|
||||||
|
loop={loop}
|
||||||
/>
|
/>
|
||||||
{embed.alt && (
|
{embed.alt && (
|
||||||
<figcaption
|
<figcaption
|
||||||
@@ -193,29 +194,6 @@ function useHLS({
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const flushOnLoop = useNonReactiveCallback(() => {
|
|
||||||
if (!Hls) return
|
|
||||||
if (!hlsRef.current) return
|
|
||||||
const hls = hlsRef.current
|
|
||||||
// the above callback will catch most stale frags, but there's a corner case -
|
|
||||||
// if there's only one segment in the video, it won't get flushed because it avoids
|
|
||||||
// flushing the currently active segment. Therefore, we have to catch it when we loop
|
|
||||||
if (
|
|
||||||
hls.nextAutoLevel > 0 &&
|
|
||||||
lowQualityFragments.length === 1 &&
|
|
||||||
lowQualityFragments[0].start === 0
|
|
||||||
) {
|
|
||||||
const lowQualFrag = lowQualityFragments[0]
|
|
||||||
|
|
||||||
hls.trigger(Hls.Events.BUFFER_FLUSHING, {
|
|
||||||
startOffset: lowQualFrag.start,
|
|
||||||
endOffset: lowQualFrag.end,
|
|
||||||
type: 'video',
|
|
||||||
})
|
|
||||||
setLowQualityFragments([])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
if (!Hls) return
|
if (!Hls) return
|
||||||
@@ -243,20 +221,6 @@ function useHLS({
|
|||||||
hls.attachMedia(videoRef.current)
|
hls.attachMedia(videoRef.current)
|
||||||
hls.loadSource(playlist)
|
hls.loadSource(playlist)
|
||||||
|
|
||||||
// manually loop, so if we've flushed the first buffer it doesn't get confused
|
|
||||||
const abortController = new AbortController()
|
|
||||||
const {signal} = abortController
|
|
||||||
const videoNode = videoRef.current
|
|
||||||
videoNode.addEventListener(
|
|
||||||
'ended',
|
|
||||||
() => {
|
|
||||||
flushOnLoop()
|
|
||||||
videoNode.currentTime = 0
|
|
||||||
videoNode.play()
|
|
||||||
},
|
|
||||||
{signal},
|
|
||||||
)
|
|
||||||
|
|
||||||
hls.on(Hls.Events.FRAG_LOADED, () => {
|
hls.on(Hls.Events.FRAG_LOADED, () => {
|
||||||
BandwidthEstimate.set(hls.bandwidthEstimate)
|
BandwidthEstimate.set(hls.bandwidthEstimate)
|
||||||
})
|
})
|
||||||
@@ -294,17 +258,65 @@ function useHLS({
|
|||||||
hlsRef.current = undefined
|
hlsRef.current = undefined
|
||||||
hls.detachMedia()
|
hls.detachMedia()
|
||||||
hls.destroy()
|
hls.destroy()
|
||||||
|
}
|
||||||
|
}, [playlist, setError, setHasSubtitleTrack, videoRef, handleFragChange, Hls])
|
||||||
|
|
||||||
|
const flushOnLoop = useNonReactiveCallback(() => {
|
||||||
|
if (!Hls) return
|
||||||
|
if (!hlsRef.current) return
|
||||||
|
const hls = hlsRef.current
|
||||||
|
// the above callback will catch most stale frags, but there's a corner case -
|
||||||
|
// if there's only one segment in the video, it won't get flushed because it avoids
|
||||||
|
// flushing the currently active segment. Therefore, we have to catch it when we loop
|
||||||
|
if (
|
||||||
|
hls.nextAutoLevel > 0 &&
|
||||||
|
lowQualityFragments.length === 1 &&
|
||||||
|
lowQualityFragments[0].start === 0
|
||||||
|
) {
|
||||||
|
const lowQualFrag = lowQualityFragments[0]
|
||||||
|
|
||||||
|
hls.trigger(Hls.Events.BUFFER_FLUSHING, {
|
||||||
|
startOffset: lowQualFrag.start,
|
||||||
|
endOffset: lowQualFrag.end,
|
||||||
|
type: 'video',
|
||||||
|
})
|
||||||
|
setLowQualityFragments([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// manually loop, so if we've flushed the first buffer it doesn't get confused
|
||||||
|
const hasLowQualityFragmentAtStart = lowQualityFragments.some(
|
||||||
|
frag => frag.start === 0,
|
||||||
|
)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!videoRef.current) return
|
||||||
|
|
||||||
|
// use `loop` prop on `<video>` element if the starting frag is high quality.
|
||||||
|
// otherwise, we need to do it with an event listener as we may need to manually flush the frag
|
||||||
|
if (!hasLowQualityFragmentAtStart) return
|
||||||
|
|
||||||
|
const abortController = new AbortController()
|
||||||
|
const {signal} = abortController
|
||||||
|
const videoNode = videoRef.current
|
||||||
|
videoNode.addEventListener(
|
||||||
|
'ended',
|
||||||
|
() => {
|
||||||
|
flushOnLoop()
|
||||||
|
videoNode.currentTime = 0
|
||||||
|
const maybePromise = videoNode.play() as Promise<void> | undefined
|
||||||
|
if (maybePromise) {
|
||||||
|
maybePromise.catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{signal},
|
||||||
|
)
|
||||||
|
return () => {
|
||||||
abortController.abort()
|
abortController.abort()
|
||||||
}
|
}
|
||||||
}, [
|
}, [videoRef, flushOnLoop, hasLowQualityFragmentAtStart])
|
||||||
playlist,
|
|
||||||
setError,
|
|
||||||
setHasSubtitleTrack,
|
|
||||||
videoRef,
|
|
||||||
handleFragChange,
|
|
||||||
flushOnLoop,
|
|
||||||
Hls,
|
|
||||||
])
|
|
||||||
|
|
||||||
return hlsRef
|
return {
|
||||||
|
hlsRef,
|
||||||
|
loop: !hasLowQualityFragmentAtStart,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user