Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix video quality for short videos (#5996)

* remove auto level capping

* flush first fragment on loop

authored by

Samuel Newman and committed by
GitHub
d1355d52 80c5f23d

+36 -13
+36 -10
src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
··· 33 33 } 34 34 35 35 const hlsRef = useHLS({ 36 - focused, 37 36 playlist: embed.playlist, 38 37 setHasSubtitleTrack, 39 38 setError, ··· 113 112 }) 114 113 115 114 function useHLS({ 116 - focused, 117 115 playlist, 118 116 setHasSubtitleTrack, 119 117 setError, 120 118 videoRef, 121 119 setHlsLoading, 122 120 }: { 123 - focused: boolean 124 121 playlist: string 125 122 setHasSubtitleTrack: (v: boolean) => void 126 123 setError: (v: Error | null) => void ··· 155 152 if (!hlsRef.current) return 156 153 const hls = hlsRef.current 157 154 158 - if (focused && hls.nextAutoLevel > 0) { 159 - // if the current quality level goes above 0, flush the low quality segments 155 + // if the current quality level goes above 0, flush the low quality segments 156 + if (hls.nextAutoLevel > 0) { 160 157 const flushed: HlsTypes.Fragment[] = [] 161 158 162 159 for (const lowQualFrag of lowQualityFragments) { ··· 179 176 }, 180 177 ) 181 178 179 + const flushOnLoop = useNonReactiveCallback(() => { 180 + if (!Hls) return 181 + if (!hlsRef.current) return 182 + const hls = hlsRef.current 183 + // the above callback will catch most stale frags, but there's a corner case - 184 + // if there's only one segment in the video, it won't get flushed because it avoids 185 + // flushing the currently active segment. Therefore, we have to catch it when we loop 186 + if ( 187 + hls.nextAutoLevel > 0 && 188 + lowQualityFragments.length === 1 && 189 + lowQualityFragments[0].start === 0 190 + ) { 191 + const lowQualFrag = lowQualityFragments[0] 192 + 193 + hls.trigger(Hls.Events.BUFFER_FLUSHING, { 194 + startOffset: lowQualFrag.start, 195 + endOffset: lowQualFrag.end, 196 + type: 'video', 197 + }) 198 + setLowQualityFragments([]) 199 + } 200 + }) 201 + 182 202 useEffect(() => { 183 203 if (!videoRef.current) return 184 204 if (!Hls) return ··· 197 217 hls.attachMedia(videoRef.current) 198 218 hls.loadSource(playlist) 199 219 200 - // initial value, later on it's managed by Controls 201 - hls.autoLevelCapping = 0 202 - 203 220 // manually loop, so if we've flushed the first buffer it doesn't get confused 204 221 const abortController = new AbortController() 205 222 const {signal} = abortController 206 223 const videoNode = videoRef.current 207 224 videoNode.addEventListener( 208 225 'ended', 209 - function () { 226 + () => { 227 + flushOnLoop() 210 228 videoNode.currentTime = 0 211 229 videoNode.play() 212 230 }, ··· 248 266 hls.destroy() 249 267 abortController.abort() 250 268 } 251 - }, [playlist, setError, setHasSubtitleTrack, videoRef, handleFragChange, Hls]) 269 + }, [ 270 + playlist, 271 + setError, 272 + setHasSubtitleTrack, 273 + videoRef, 274 + handleFragChange, 275 + flushOnLoop, 276 + Hls, 277 + ]) 252 278 253 279 return hlsRef 254 280 }
-3
src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx
··· 138 138 useEffect(() => { 139 139 if (!hlsRef.current) return 140 140 if (focused) { 141 - // auto decide quality based on network conditions 142 - hlsRef.current.autoLevelCapping = -1 143 141 // allow 30s of buffering 144 142 hlsRef.current.config.maxMaxBufferLength = 30 145 143 } else { 146 144 // back to what we initially set 147 - hlsRef.current.autoLevelCapping = 0 148 145 hlsRef.current.config.maxMaxBufferLength = 10 149 146 } 150 147 }, [hlsRef, focused])