Fix video quality for short videos (#5996)
* remove auto level capping * flush first fragment on loop
This commit is contained in:
@@ -33,7 +33,6 @@ export function VideoEmbedInnerWeb({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hlsRef = useHLS({
|
const hlsRef = useHLS({
|
||||||
focused,
|
|
||||||
playlist: embed.playlist,
|
playlist: embed.playlist,
|
||||||
setHasSubtitleTrack,
|
setHasSubtitleTrack,
|
||||||
setError,
|
setError,
|
||||||
@@ -113,14 +112,12 @@ promiseForHls.then(Hls => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function useHLS({
|
function useHLS({
|
||||||
focused,
|
|
||||||
playlist,
|
playlist,
|
||||||
setHasSubtitleTrack,
|
setHasSubtitleTrack,
|
||||||
setError,
|
setError,
|
||||||
videoRef,
|
videoRef,
|
||||||
setHlsLoading,
|
setHlsLoading,
|
||||||
}: {
|
}: {
|
||||||
focused: boolean
|
|
||||||
playlist: string
|
playlist: string
|
||||||
setHasSubtitleTrack: (v: boolean) => void
|
setHasSubtitleTrack: (v: boolean) => void
|
||||||
setError: (v: Error | null) => void
|
setError: (v: Error | null) => void
|
||||||
@@ -155,8 +152,8 @@ function useHLS({
|
|||||||
if (!hlsRef.current) return
|
if (!hlsRef.current) return
|
||||||
const hls = hlsRef.current
|
const hls = hlsRef.current
|
||||||
|
|
||||||
if (focused && hls.nextAutoLevel > 0) {
|
// if the current quality level goes above 0, flush the low quality segments
|
||||||
// if the current quality level goes above 0, flush the low quality segments
|
if (hls.nextAutoLevel > 0) {
|
||||||
const flushed: HlsTypes.Fragment[] = []
|
const flushed: HlsTypes.Fragment[] = []
|
||||||
|
|
||||||
for (const lowQualFrag of lowQualityFragments) {
|
for (const lowQualFrag of lowQualityFragments) {
|
||||||
@@ -179,6 +176,29 @@ 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
|
||||||
@@ -197,16 +217,14 @@ function useHLS({
|
|||||||
hls.attachMedia(videoRef.current)
|
hls.attachMedia(videoRef.current)
|
||||||
hls.loadSource(playlist)
|
hls.loadSource(playlist)
|
||||||
|
|
||||||
// initial value, later on it's managed by Controls
|
|
||||||
hls.autoLevelCapping = 0
|
|
||||||
|
|
||||||
// manually loop, so if we've flushed the first buffer it doesn't get confused
|
// manually loop, so if we've flushed the first buffer it doesn't get confused
|
||||||
const abortController = new AbortController()
|
const abortController = new AbortController()
|
||||||
const {signal} = abortController
|
const {signal} = abortController
|
||||||
const videoNode = videoRef.current
|
const videoNode = videoRef.current
|
||||||
videoNode.addEventListener(
|
videoNode.addEventListener(
|
||||||
'ended',
|
'ended',
|
||||||
function () {
|
() => {
|
||||||
|
flushOnLoop()
|
||||||
videoNode.currentTime = 0
|
videoNode.currentTime = 0
|
||||||
videoNode.play()
|
videoNode.play()
|
||||||
},
|
},
|
||||||
@@ -248,7 +266,15 @@ function useHLS({
|
|||||||
hls.destroy()
|
hls.destroy()
|
||||||
abortController.abort()
|
abortController.abort()
|
||||||
}
|
}
|
||||||
}, [playlist, setError, setHasSubtitleTrack, videoRef, handleFragChange, Hls])
|
}, [
|
||||||
|
playlist,
|
||||||
|
setError,
|
||||||
|
setHasSubtitleTrack,
|
||||||
|
videoRef,
|
||||||
|
handleFragChange,
|
||||||
|
flushOnLoop,
|
||||||
|
Hls,
|
||||||
|
])
|
||||||
|
|
||||||
return hlsRef
|
return hlsRef
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,13 +138,10 @@ export function Controls({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hlsRef.current) return
|
if (!hlsRef.current) return
|
||||||
if (focused) {
|
if (focused) {
|
||||||
// auto decide quality based on network conditions
|
|
||||||
hlsRef.current.autoLevelCapping = -1
|
|
||||||
// allow 30s of buffering
|
// allow 30s of buffering
|
||||||
hlsRef.current.config.maxMaxBufferLength = 30
|
hlsRef.current.config.maxMaxBufferLength = 30
|
||||||
} else {
|
} else {
|
||||||
// back to what we initially set
|
// back to what we initially set
|
||||||
hlsRef.current.autoLevelCapping = 0
|
|
||||||
hlsRef.current.config.maxMaxBufferLength = 10
|
hlsRef.current.config.maxMaxBufferLength = 10
|
||||||
}
|
}
|
||||||
}, [hlsRef, focused])
|
}, [hlsRef, focused])
|
||||||
|
|||||||
Reference in New Issue
Block a user