From a525dd0ff79aac53678172f4f48dd6a4135baf4e Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:35:44 -0500 Subject: [PATCH] add descriptive console errors when falling back to jsmpeg --- web/src/components/player/MsePlayer.tsx | 35 +++++++++++++++------- web/src/components/player/WebRTCPlayer.tsx | 18 +++++++++-- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index 7c831e596..f7a77393f 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -84,6 +84,17 @@ function MSEPlayer({ return `${baseUrl.replace(/^http/, "ws")}live/mse/api/ws?src=${camera}`; }, [camera]); + const handleError = useCallback( + (error: LivePlayerError, description: string = "Unknown error") => { + // eslint-disable-next-line no-console + console.error( + `${camera} - MSE error '${error}': ${description} See the documentation: https://docs.frigate.video/configuration/live`, + ); + onError?.(error); + }, + [camera, onError], + ); + const handleLoadedMetadata = useCallback(() => { if (videoRef.current && setFullResolution) { setFullResolution({ @@ -237,9 +248,9 @@ function MSEPlayer({ onDisconnect(); } if (isIOS || isSafari) { - onError?.("mse-decode"); + handleError("mse-decode", "Safari cannot open MediaSource."); } else { - onError?.("startup"); + handleError("startup", "Error opening MediaSource."); } }); }, @@ -267,9 +278,9 @@ function MSEPlayer({ onDisconnect(); } if (isIOS || isSafari) { - onError?.("mse-decode"); + handleError("mse-decode", "Safari cannot open MediaSource."); } else { - onError?.("startup"); + handleError("startup", "Error opening MediaSource."); } }); }, @@ -297,7 +308,7 @@ function MSEPlayer({ if (wsRef.current) { onDisconnect(); } - onError?.("mse-decode"); + handleError("mse-decode", "Safari reported InvalidStateError."); return; } else { throw e; // Re-throw if it's not the error we're handling @@ -424,7 +435,10 @@ function MSEPlayer({ (bufferThreshold > 10 || bufferTime > 10) ) { onDisconnect(); - onError?.("stalled"); + handleError( + "stalled", + "Buffer time (10 seconds) exceeded, browser may not be playing media correctly.", + ); } const playbackRate = calculateAdaptivePlaybackRate( @@ -470,7 +484,7 @@ function MSEPlayer({ videoRef.current ) { onDisconnect(); - onError("stalled"); + handleError("stalled", "Media playback has stalled."); } }, timeoutDuration), ); @@ -479,6 +493,7 @@ function MSEPlayer({ bufferTimeout, isPlaying, onDisconnect, + handleError, onError, onPlaying, playbackEnabled, @@ -663,7 +678,7 @@ function MSEPlayer({ if (wsRef.current) { onDisconnect(); } - onError?.("startup"); + handleError("startup", "Browser reported a network error."); } if ( @@ -674,7 +689,7 @@ function MSEPlayer({ if (wsRef.current) { onDisconnect(); } - onError?.("mse-decode"); + handleError("mse-decode", "Safari reported decoding errors."); } setErrorCount((prevCount) => prevCount + 1); @@ -683,7 +698,7 @@ function MSEPlayer({ onDisconnect(); if (errorCount >= 3) { // too many mse errors, try jsmpeg - onError?.("startup"); + handleError("startup", `Max error count ${errorCount} exceeded.`); } else { reconnect(5000); } diff --git a/web/src/components/player/WebRTCPlayer.tsx b/web/src/components/player/WebRTCPlayer.tsx index 81d6a72dd..dc3b24653 100644 --- a/web/src/components/player/WebRTCPlayer.tsx +++ b/web/src/components/player/WebRTCPlayer.tsx @@ -37,6 +37,18 @@ export default function WebRtcPlayer({ return `${baseUrl.replace(/^http/, "ws")}live/webrtc/api/ws?src=${camera}`; }, [camera]); + // error handler + const handleError = useCallback( + (error: LivePlayerError, description: string = "Unknown error") => { + // eslint-disable-next-line no-console + console.error( + `${camera} - WebRTC error '${error}': ${description} See the documentation: https://docs.frigate.video/configuration/live`, + ); + onError?.(error); + }, + [camera, onError], + ); + // camera states const pcRef = useRef(); @@ -212,7 +224,7 @@ export default function WebRtcPlayer({ useEffect(() => { videoLoadTimeoutRef.current = setTimeout(() => { - onError?.("stalled"); + handleError("stalled", "WebRTC connection timed out."); }, 5000); return () => { @@ -327,7 +339,7 @@ export default function WebRtcPlayer({ document.visibilityState === "visible" && pcRef.current != undefined ) { - onError("stalled"); + handleError("stalled", "WebRTC connection stalled."); } }, 3000), ); @@ -344,7 +356,7 @@ export default function WebRtcPlayer({ // @ts-expect-error code does exist e.target.error.code == MediaError.MEDIA_ERR_NETWORK ) { - onError?.("startup"); + handleError("startup", "Browser reported a network error."); } }} />