From 66fefe5513353b6ab413705a05f3281eb31652f5 Mon Sep 17 00:00:00 2001 From: Louis Sautier Date: Mon, 16 Mar 2026 13:04:32 +0100 Subject: [PATCH] Temporarily mute WebRTC video at start to fix autoplay on mic toggle Fixes 2-way audio on the Home Assistant Android companion app: toggling the microphone causes the video feed to go black while audio continues working. When the mic is toggled, the player switches from MSE to WebRTC, which creates a new PeerConnection and video stream. By the time the async WebRTC setup completes, the user gesture from the mic button click has expired under Android WebView's transient activation model, causing unmuted autoplay to be blocked. Fix this by introducing an `awaitingPlayback` state that forces the video element to start muted (muted autoplay is always allowed). Once the `playing` event fires, the flag is cleared and the muted prop falls back to the normal `!audioEnabled` logic, preserving audio toggle functionality. Closes https://github.com/blakeblackshear/frigate-hass-addons/issues/274 --- web/src/components/player/WebRTCPlayer.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/web/src/components/player/WebRTCPlayer.tsx b/web/src/components/player/WebRTCPlayer.tsx index 147af43ea..bebe5521d 100644 --- a/web/src/components/player/WebRTCPlayer.tsx +++ b/web/src/components/player/WebRTCPlayer.tsx @@ -55,6 +55,7 @@ export default function WebRtcPlayer({ const videoRef = useRef(null); const [bufferTimeout, setBufferTimeout] = useState(); const videoLoadTimeoutRef = useRef(undefined); + const [awaitingPlayback, setAwaitingPlayback] = useState(true); const PeerConnection = useCallback( async (media: string) => { @@ -177,6 +178,8 @@ export default function WebRtcPlayer({ return; } + setAwaitingPlayback(true); + const aPc = PeerConnection( microphoneEnabled ? "video+audio+microphone" : "video+audio", ); @@ -241,6 +244,19 @@ export default function WebRtcPlayer({ clearTimeout(videoLoadTimeoutRef.current); } onPlaying?.(); + + // Start muted so autoplay isn't blocked when the user gesture has + // expired (e.g. mic toggle). Cleared on the "playing" event. + // https://github.com/blakeblackshear/frigate-hass-addons/issues/274 + if (awaitingPlayback && videoRef.current) { + videoRef.current.addEventListener( + "playing", + () => { + setAwaitingPlayback(false); + }, + { once: true }, + ); + } }; // stats @@ -319,7 +335,7 @@ export default function WebRtcPlayer({ controls={iOSCompatControls} autoPlay playsInline - muted={!audioEnabled} + muted={awaitingPlayback || !audioEnabled} onLoadedData={handleLoadedData} onProgress={ onError != undefined