diff --git a/apps/web/src/components/CallModal.tsx b/apps/web/src/components/CallModal.tsx index 9a986ac..032a8bd 100644 --- a/apps/web/src/components/CallModal.tsx +++ b/apps/web/src/components/CallModal.tsx @@ -249,42 +249,41 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi } }; - // Always merge tracks into a single remote stream + // Ensure we have a stream object (even if browser didn't provide one) if (!remoteStreamRef.current) { remoteStreamRef.current = streams[0] || new MediaStream([track]); - } else if (streams[0]) { - streams[0].getTracks().forEach(t => { - if (!remoteStreamRef.current?.getTracks().includes(t)) { - remoteStreamRef.current?.addTrack(t); - } - }); } else if (!remoteStreamRef.current.getTracks().includes(track)) { remoteStreamRef.current.addTrack(track); } const stream = remoteStreamRef.current; - // Bind to DOM elements - if (track.kind === 'video' && remoteVideoRef.current) { - remoteVideoRef.current.srcObject = stream; - remoteVideoRef.current.play().catch(err => console.warn('[WebRTC] video play failed:', err)); - checkVideo(); - } else if (track.kind === 'audio') { - if (remoteAudioRef?.current) { - remoteAudioRef.current.srcObject = stream; - remoteAudioRef.current.play().catch(err => console.warn('[WebRTC] audio play failed:', err)); - } else if (remoteVideoRef.current) { - remoteVideoRef.current.srcObject = stream; + // 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; } + 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; track.onmute = checkVideo; track.onended = checkVideo; - // Extra checks for slow browsers + checkVideo(); setTimeout(checkVideo, 500); - setTimeout(checkVideo, 1500); + setTimeout(checkVideo, 2000); }; pc.onconnectionstatechange = () => { @@ -1549,7 +1548,6 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi ref={remoteVideoRef} autoPlay playsInline - muted className={`absolute inset-0 w-full h-full object-contain bg-black transition-opacity duration-300 ${hasRemoteVideo ? 'opacity-100' : 'opacity-0'}`} onContextMenu={(e) => { e.preventDefault(); setShowVolumeSlider(true); }} /> @@ -1669,7 +1667,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi )} {/* Hidden remote video element */} -