Compare commits

...

2 Commits

Author SHA1 Message Date
Alex P cd7a098f76 fix: track audio autoplay status separately to handle Safari blocking audio while allowing video 2025-10-28 15:47:36 +02:00
Alex P ef86af8afc fix: enable audio playback via manual start stream button
Browser autoplay policy blocks audio without user interaction.
Store audio elements in ref and trigger playback when user clicks
the existing 'Manually start stream' button.
2025-10-28 14:28:30 +02:00
1 changed files with 22 additions and 7 deletions

View File

@ -26,8 +26,10 @@ import { m } from "@localizations/messages.js";
export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssues: boolean }) {
// Video and stream related refs and states
const videoElm = useRef<HTMLVideoElement>(null);
const audioElementsRef = useRef<HTMLAudioElement[]>([]);
const { mediaStream, peerConnectionState } = useRTCStore();
const [isPlaying, setIsPlaying] = useState(false);
const [audioAutoplayBlocked, setAudioAutoplayBlocked] = useState(false);
const [isPointerLockActive, setIsPointerLockActive] = useState(false);
const [isKeyboardLockActive, setIsKeyboardLockActive] = useState(false);
@ -330,7 +332,6 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
if (!peerConnection) return;
const abortController = new AbortController();
const signal = abortController.signal;
const audioElements: HTMLAudioElement[] = [];
peerConnection.addEventListener(
"track",
@ -339,11 +340,17 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
addStreamToVideoElm(e.streams[0]);
} else if (e.track.kind === "audio") {
const audioElm = document.createElement("audio");
audioElm.autoplay = true;
audioElm.srcObject = e.streams[0];
audioElm.style.display = "none";
document.body.appendChild(audioElm);
audioElements.push(audioElm);
audioElementsRef.current.push(audioElm);
audioElm.play().then(() => {
setAudioAutoplayBlocked(false);
}).catch(() => {
console.debug("[Audio] Autoplay blocked, will be started by user interaction");
setAudioAutoplayBlocked(true);
});
}
},
{ signal },
@ -351,10 +358,12 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
return () => {
abortController.abort();
audioElements.forEach((audioElm) => {
audioElementsRef.current.forEach((audioElm) => {
audioElm.srcObject = null;
audioElm.remove();
});
audioElementsRef.current = [];
setAudioAutoplayBlocked(false);
};
},
[addStreamToVideoElm, peerConnection],
@ -468,11 +477,12 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
const hasNoAutoPlayPermissions = useMemo(() => {
if (peerConnection?.connectionState !== "connected") return false;
if (isPlaying) return false;
if (hdmiError) return false;
if (videoHeight === 0 || videoWidth === 0) return false;
return true;
}, [hdmiError, isPlaying, peerConnection?.connectionState, videoHeight, videoWidth]);
if (!isPlaying) return true;
if (audioAutoplayBlocked) return true;
return false;
}, [audioAutoplayBlocked, hdmiError, isPlaying, peerConnection?.connectionState, videoHeight, videoWidth]);
const showPointerLockBar = useMemo(() => {
if (settings.mouseMode !== "relative") return false;
@ -564,6 +574,11 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
show={hasNoAutoPlayPermissions}
onPlayClick={() => {
videoElm.current?.play();
audioElementsRef.current.forEach(audioElm => {
audioElm.play().then(() => {
setAudioAutoplayBlocked(false);
}).catch(() => undefined);
});
}}
/>
</div>