From ae01440a1590544f24c9fc6252d7e229c38da441 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Tue, 10 Jun 2025 13:58:34 -0500 Subject: [PATCH] Better error messaging for unsupported codecs in the web player. Also don't block controls with error messages. --- frontend/src/components/FloatingVideo.jsx | 202 ++++++++++++---------- 1 file changed, 115 insertions(+), 87 deletions(-) diff --git a/frontend/src/components/FloatingVideo.jsx b/frontend/src/components/FloatingVideo.jsx index 46c191eb..7f1e1c53 100644 --- a/frontend/src/components/FloatingVideo.jsx +++ b/frontend/src/components/FloatingVideo.jsx @@ -73,72 +73,109 @@ export default function FloatingVideo() { console.log("Attempting to play stream:", streamUrl); try { - // If the browser supports MSE for live playback, initialize mpegts.js - if (mpegts.getFeatureList().mseLivePlayback) { - // Set loading flag - setIsLoading(true); - - const player = mpegts.createPlayer({ - type: 'mpegts', // MPEG-TS format - url: streamUrl, - isLive: true, - enableWorker: true, - enableStashBuffer: false, // Try disabling stash buffer for live streams - liveBufferLatencyChasing: true, - liveSync: true, - cors: true, // Enable CORS for cross-domain requests - // Add error recovery options - autoCleanupSourceBuffer: true, - autoCleanupMaxBackwardDuration: 10, - autoCleanupMinBackwardDuration: 5, - reuseRedirectedURL: true, - }); - - player.attachMediaElement(videoRef.current); - - // Add events to track loading state - player.on(mpegts.Events.LOADING_COMPLETE, () => { - setIsLoading(false); - }); - - player.on(mpegts.Events.METADATA_ARRIVED, () => { - setIsLoading(false); - }); - - // Add error event handler - player.on(mpegts.Events.ERROR, (errorType, errorDetail) => { - setIsLoading(false); - - // Filter out aborted errors - if (errorType !== 'NetworkError' || !errorDetail?.includes('aborted')) { - console.error('Player error:', errorType, errorDetail); - setLoadError(`Error: ${errorType}${errorDetail ? ` - ${errorDetail}` : ''}`); - } - }); - - player.load(); - - // Don't auto-play until we've loaded properly - player.on(mpegts.Events.MEDIA_INFO, () => { - setIsLoading(false); - try { - player.play().catch(e => { - console.log("Auto-play prevented:", e); - setLoadError("Auto-play was prevented. Click play to start."); - }); - } catch (e) { - console.log("Error during play:", e); - setLoadError(`Playback error: ${e.message}`); - } - }); - - // Store player instance so we can clean up later - playerRef.current = player; + // Check for MSE support first + if (!mpegts.getFeatureList().mseLivePlayback) { + setIsLoading(false); + setLoadError("Your browser doesn't support live video streaming. Please try Chrome or Edge."); + return; } + + // Check for basic codec support + const video = document.createElement('video'); + const h264Support = video.canPlayType('video/mp4; codecs="avc1.42E01E"'); + const aacSupport = video.canPlayType('audio/mp4; codecs="mp4a.40.2"'); + + console.log("Browser codec support - H264:", h264Support, "AAC:", aacSupport); + + // If the browser supports MSE for live playback, initialize mpegts.js + setIsLoading(true); + + const player = mpegts.createPlayer({ + type: 'mpegts', + url: streamUrl, + isLive: true, + enableWorker: true, + enableStashBuffer: false, + liveBufferLatencyChasing: true, + liveSync: true, + cors: true, + autoCleanupSourceBuffer: true, + autoCleanupMaxBackwardDuration: 10, + autoCleanupMinBackwardDuration: 5, + reuseRedirectedURL: true, + }); + + player.attachMediaElement(videoRef.current); + + // Add events to track loading state + player.on(mpegts.Events.LOADING_COMPLETE, () => { + setIsLoading(false); + }); + + player.on(mpegts.Events.METADATA_ARRIVED, () => { + setIsLoading(false); + }); + + // Enhanced error event handler with codec-specific messages + player.on(mpegts.Events.ERROR, (errorType, errorDetail) => { + setIsLoading(false); + + // Filter out aborted errors + if (errorType !== 'NetworkError' || !errorDetail?.includes('aborted')) { + console.error('Player error:', errorType, errorDetail); + + // Provide specific error messages based on error type + let errorMessage = `Error: ${errorType}`; + + if (errorType === 'MediaError') { + // Try to determine if it's an audio or video codec issue + const errorString = errorDetail?.toLowerCase() || ''; + + if (errorString.includes('audio') || errorString.includes('ac3') || errorString.includes('ac-3')) { + errorMessage = "Audio codec not supported by your browser. Try Chrome or Edge for better audio codec support."; + } else if (errorString.includes('video') || errorString.includes('h264') || errorString.includes('h.264')) { + errorMessage = "Video codec not supported by your browser. Try Chrome or Edge for better video codec support."; + } else if (errorString.includes('mse')) { + errorMessage = "Your browser doesn't support the codecs used in this stream. Try Chrome or Edge for better compatibility."; + } else { + errorMessage = "Media codec not supported by your browser. This may be due to unsupported audio (AC3) or video codecs. Try Chrome or Edge."; + } + } else if (errorDetail) { + errorMessage += ` - ${errorDetail}`; + } + + setLoadError(errorMessage); + } + }); + + player.load(); + + // Don't auto-play until we've loaded properly + player.on(mpegts.Events.MEDIA_INFO, () => { + setIsLoading(false); + try { + player.play().catch(e => { + console.log("Auto-play prevented:", e); + setLoadError("Auto-play was prevented. Click play to start."); + }); + } catch (e) { + console.log("Error during play:", e); + setLoadError(`Playback error: ${e.message}`); + } + }); + + // Store player instance so we can clean up later + playerRef.current = player; } catch (error) { setIsLoading(false); - setLoadError(`Initialization error: ${error.message}`); console.error("Error initializing player:", error); + + // Provide helpful error message based on the error + if (error.message?.includes('codec') || error.message?.includes('format')) { + setLoadError("Codec not supported by your browser. Please try a different browser (Chrome/Edge recommended)."); + } else { + setLoadError(`Initialization error: ${error.message}`); + } } // Cleanup when component unmounts or streamUrl changes @@ -191,7 +228,7 @@ export default function FloatingVideo() { style={{ width: '100%', height: '180px', backgroundColor: '#000' }} /> - {/* Loading overlay */} + {/* Loading overlay - only show when loading */} {isLoading && ( )} - - {/* Error message overlay */} - {!isLoading && loadError && ( - - - {loadError} - - - )} + + {/* Error message below video - doesn't block controls */} + {!isLoading && loadError && ( + + + {loadError} + + + )} );