diff --git a/apps/web/src/components/CallModal.tsx b/apps/web/src/components/CallModal.tsx
index ae5ad9e..53467e5 100644
--- a/apps/web/src/components/CallModal.tsx
+++ b/apps/web/src/components/CallModal.tsx
@@ -249,7 +249,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
}
};
- // Ensure we have a stream object (even if browser didn't provide one)
+ // Ensure we have a stream object
if (!remoteStreamRef.current) {
remoteStreamRef.current = streams[0] || new MediaStream([track]);
} else if (!remoteStreamRef.current.getTracks().includes(track)) {
@@ -258,23 +258,22 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
const stream = remoteStreamRef.current;
- // Link to DOM elements immediately
- if (remoteVideoRef.current) {
- // IMPORTANT: If srcObject is the same, some browsers won't refresh the view.
- // We set it to the stream and call play() explicitly.
- if (remoteVideoRef.current.srcObject !== stream) {
- remoteVideoRef.current.srcObject = stream;
+ // Link to DOM elements
+ if (track.kind === 'video' && remoteVideoRef.current) {
+ remoteVideoRef.current.srcObject = stream;
+ // MUST be muted to autoplay in most browsers
+ remoteVideoRef.current.muted = true;
+ remoteVideoRef.current.play().catch(err => console.warn('[WebRTC] video play failed:', err));
+ checkVideo();
+ }
+
+ if (track.kind === 'audio' || (track.kind === 'video' && !remoteAudioRef.current?.srcObject)) {
+ if (remoteAudioRef?.current) {
+ remoteAudioRef.current.srcObject = stream;
+ remoteAudioRef.current.play().catch(err => {
+ console.warn('[WebRTC] Audio play failed, user gesture needed:', err);
+ });
}
- remoteVideoRef.current.play().catch(err => {
- console.warn('[WebRTC] Video play failed (retrying):', err);
- // Retry play on next tick if failed
- setTimeout(() => remoteVideoRef.current?.play().catch(() => {}), 500);
- });
- }
-
- if (track.kind === 'audio' && remoteAudioRef?.current) {
- remoteAudioRef.current.srcObject = stream;
- remoteAudioRef.current.play().catch(() => {});
}
track.onunmute = checkVideo;
@@ -1438,9 +1437,9 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
- {/* Hidden remote video to keep stream alive */}
+ {/* Hidden remote video/audio elements for background processing */}
-
+
) : (
@@ -1546,6 +1545,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
ref={remoteVideoRef}
autoPlay
playsInline
+ muted
className={`
bg-black transition-all duration-500
${showVideoArea