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
This commit is contained in:
Louis Sautier 2026-03-16 13:04:32 +01:00
parent a0b8271532
commit 66fefe5513
No known key found for this signature in database
GPG Key ID: A777716B30AE82E3

View File

@ -55,6 +55,7 @@ export default function WebRtcPlayer({
const videoRef = useRef<HTMLVideoElement | null>(null);
const [bufferTimeout, setBufferTimeout] = useState<NodeJS.Timeout>();
const videoLoadTimeoutRef = useRef<NodeJS.Timeout>(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