From d09e1e55124b708e471ba34b1bf0895201c8e4d3 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:31:12 -0500 Subject: [PATCH] jump to live on safari/iOS --- web/src/components/player/MsePlayer.tsx | 47 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index e07783c4e..678e1583b 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -216,7 +216,6 @@ function MSEPlayer({ type: "mse", // @ts-expect-error for typing value: codecs(MediaSource.isTypeSupported), - duration: Number.POSITIVE_INFINITY, // https://stackoverflow.com/questions/74461792/mediasource-api-safari-pauses-video-on-buffer-underflow }, 3000, ).catch(() => { @@ -248,7 +247,6 @@ function MSEPlayer({ { type: "mse", value: codecs(MediaSource.isTypeSupported), - duration: Number.POSITIVE_INFINITY, // https://stackoverflow.com/questions/74461792/mediasource-api-safari-pauses-video-on-buffer-underflow }, 3000, ).catch(() => { @@ -266,17 +264,24 @@ function MSEPlayer({ }, { once: true }, ); - videoRef.current!.src = URL.createObjectURL(msRef.current!); - videoRef.current!.srcObject = null; + if (videoRef.current && msRef.current) { + videoRef.current.src = URL.createObjectURL(msRef.current); + videoRef.current.srcObject = null; + } } play(); onmessageRef.current["mse"] = (msg) => { if (msg.type !== "mse") return; + console.log(msg.value); + let sb: SourceBuffer | undefined; try { sb = msRef.current?.addSourceBuffer(msg.value); + if (sb?.mode) { + sb.mode = "segments"; + } } catch (e) { // Safari sometimes throws this error if (e instanceof DOMException && e.name === "InvalidStateError") { @@ -337,11 +342,32 @@ function MSEPlayer({ return video.buffered.end(video.buffered.length - 1) - video.currentTime; }; + const jumpToLive = () => { + if (!videoRef.current) return; + + const now = Date.now(); + + // we either recently just started streaming or recently + // jumped to live, so don't jump to live again right away + if (now - lastJumpTimeRef.current < BUFFERING_COOLDOWN_TIMEOUT) { + return; + } + + const buffered = videoRef.current.buffered; + if (buffered.length > 0) { + const liveEdge = buffered.end(buffered.length - 1); + // Jump to the live edge + videoRef.current.currentTime = liveEdge; + lastJumpTimeRef.current = now; + console.log(camera, "jumped to live"); + } + }; + const calculateAdaptiveBufferThreshold = () => { const filledEntries = bufferTimes.current.length; const sum = bufferTimes.current.reduce((a, b) => a + b, 0); const averageBufferTime = filledEntries ? sum / filledEntries : 0; - return averageBufferTime * 1.5; + return averageBufferTime * (isSafari || isIOS ? 2 : 1.5); }; const calculateAdaptivePlaybackRate = ( @@ -496,14 +522,21 @@ function MSEPlayer({ // if we're above our rolling average threshold or have > 3 seconds of // buffered data and we're playing, we may have drifted from actual live - // time, so increase playback rate to compensate + // time, so increase playback rate to compensate - non safari/ios only if ( videoRef.current && isPlaying && playbackEnabled && Date.now() - lastJumpTimeRef.current > BUFFERING_COOLDOWN_TIMEOUT ) { - videoRef.current.playbackRate = playbackRate; + // Jump to live on Safari/iOS due to a change of playback rate causing re-buffering + if (isSafari || isIOS) { + if (bufferTime > bufferThreshold) { + jumpToLive(); + } + } else { + videoRef.current.playbackRate = playbackRate; + } } console.log(