diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx index de3c130c26..ef6fa1cae0 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx @@ -169,11 +169,19 @@ function useHLS({ continue } + // Flush both video and audio buffers to keep them in sync. + // Flushing only video leaves stale audio data that can desync, + // particularly on Firefox. hls.trigger(Hls.Events.BUFFER_FLUSHING, { startOffset: lowQualFrag.start, endOffset: lowQualFrag.end, type: 'video', }) + hls.trigger(Hls.Events.BUFFER_FLUSHING, { + startOffset: lowQualFrag.start, + endOffset: lowQualFrag.end, + type: 'audio', + }) flushed.push(lowQualFrag) } @@ -269,6 +277,11 @@ function useHLS({ endOffset: lowQualFrag.end, type: 'video', }) + hls.trigger(Hls.Events.BUFFER_FLUSHING, { + startOffset: lowQualFrag.start, + endOffset: lowQualFrag.end, + type: 'audio', + }) setLowQualityFragments([]) } }) diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx index d6a5628bb4..d8414e9d35 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/VideoControls.tsx @@ -27,7 +27,7 @@ import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Paus import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' -import {IS_WEB_MOBILE_IOS, IS_WEB_TOUCH_DEVICE} from '#/env' +import {IS_WEB_FIREFOX, IS_WEB_MOBILE_IOS, IS_WEB_TOUCH_DEVICE} from '#/env' import {GifPresentationControls} from '../../GifPresentationControls' import {TimeIndicator} from '../TimeIndicator' import {ControlButton} from './ControlButton' @@ -196,7 +196,9 @@ export function Controls({ const onSeek = useCallback( (time: number) => { if (!videoRef.current) return - if (videoRef.current.fastSeek) { + // Avoid fastSeek on Firefox - it seeks video to the nearest keyframe + // but keeps audio at the precise time, causing audio/video desync + if (videoRef.current.fastSeek && !IS_WEB_FIREFOX) { videoRef.current.fastSeek(time) } else { videoRef.current.currentTime = time