diff --git a/apps/web/src/components/CallModal.tsx b/apps/web/src/components/CallModal.tsx
index 660076d..ec06e13 100644
--- a/apps/web/src/components/CallModal.tsx
+++ b/apps/web/src/components/CallModal.tsx
@@ -114,6 +114,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
const [isVideoOff, setIsVideoOff] = useState(false);
const [isScreenSharing, setIsScreenSharing] = useState(false);
const [hasRemoteVideo, setHasRemoteVideo] = useState(false);
+ const hasRemoteVideoRef = useRef(false);
const [isFullscreen, setIsFullscreen] = useState(false);
const [isMinimized, setIsMinimized] = useState(false);
const [duration, setDuration] = useState(0);
@@ -211,6 +212,23 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
scheduleClose();
}, [cleanup, scheduleClose]);
+ // Ensure remote stream is bound to video element when it becomes available
+ useEffect(() => {
+ if (hasRemoteVideo && remoteVideoRef.current && remoteStreamRef.current) {
+ const video = remoteVideoRef.current;
+ const stream = remoteStreamRef.current;
+
+ if (video.srcObject !== stream) {
+ console.log('[WebRTC] Effect: Binding stream to video element');
+ video.srcObject = stream;
+ video.muted = false;
+ video.play().catch(e => {
+ if (e.name === 'NotAllowedError') setNeedsInteraction(true);
+ });
+ }
+ }
+ }, [hasRemoteVideo]);
+
// Setup common peer connection event handlers
const setupPeerHandlers = useCallback((pc: RTCPeerConnection) => {
pc.onicecandidate = (e) => {
@@ -255,7 +273,11 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
`${t.label} state=${t.readyState} enabled=${t.enabled} muted=${t.muted}`
), '→ hasRemoteVideo:', hasVideo);
- setHasRemoteVideo(hasVideo);
+ if (hasVideo !== hasRemoteVideoRef.current) {
+ hasRemoteVideoRef.current = hasVideo;
+ setHasRemoteVideo(hasVideo);
+ }
+
if (hasVideo && callType !== 'video') {
setCallType('video');
}
@@ -1597,7 +1619,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
ref={remoteVideoRef}
autoPlay
playsInline
- className="w-full h-full object-contain"
+ className={`w-full h-full object-contain transition-opacity duration-300 ${hasRemoteVideo ? 'opacity-100' : 'opacity-0'}`}
onContextMenu={(e) => { e.preventDefault(); setShowVolumeSlider(true); }}
/>
@@ -1616,7 +1638,10 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi