From d1355d52fc7b0c64b4fdb72b99e18d48538b042d Mon Sep 17 00:00:00 2001
From: Samuel Newman <mozzius@protonmail.com>
Date: Tue, 29 Oct 2024 15:35:43 +0200
Subject: Fix video quality for short videos (#5996)

* remove auto level capping

* flush first fragment on loop
---
 .../VideoEmbedInner/VideoEmbedInnerWeb.tsx         | 46 +++++++++++++++++-----
 1 file changed, 36 insertions(+), 10 deletions(-)

(limited to 'src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx')

diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
index fa2b7e3d3..ef989c4a4 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb.tsx
@@ -33,7 +33,6 @@ export function VideoEmbedInnerWeb({
   }
 
   const hlsRef = useHLS({
-    focused,
     playlist: embed.playlist,
     setHasSubtitleTrack,
     setError,
@@ -113,14 +112,12 @@ promiseForHls.then(Hls => {
 })
 
 function useHLS({
-  focused,
   playlist,
   setHasSubtitleTrack,
   setError,
   videoRef,
   setHlsLoading,
 }: {
-  focused: boolean
   playlist: string
   setHasSubtitleTrack: (v: boolean) => void
   setError: (v: Error | null) => void
@@ -155,8 +152,8 @@ function useHLS({
       if (!hlsRef.current) return
       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[] = []
 
         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(() => {
     if (!videoRef.current) return
     if (!Hls) return
@@ -197,16 +217,14 @@ function useHLS({
     hls.attachMedia(videoRef.current)
     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
     const abortController = new AbortController()
     const {signal} = abortController
     const videoNode = videoRef.current
     videoNode.addEventListener(
       'ended',
-      function () {
+      () => {
+        flushOnLoop()
         videoNode.currentTime = 0
         videoNode.play()
       },
@@ -248,7 +266,15 @@ function useHLS({
       hls.destroy()
       abortController.abort()
     }
-  }, [playlist, setError, setHasSubtitleTrack, videoRef, handleFragChange, Hls])
+  }, [
+    playlist,
+    setError,
+    setHasSubtitleTrack,
+    videoRef,
+    handleFragChange,
+    flushOnLoop,
+    Hls,
+  ])
 
   return hlsRef
 }
-- 
cgit 1.4.1

