This commit is contained in:
Халимов Рустам
2026-03-11 17:33:04 +03:00
parent 8c7ca3b869
commit 1da4b26f5b

View File

@@ -214,20 +214,41 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
// Ensure remote stream is bound to video element when it becomes available
useEffect(() => {
if (hasRemoteVideo && remoteVideoRef.current && remoteStreamRef.current) {
const video = remoteVideoRef.current;
if (callState === 'connected' && remoteVideoRef.current && remoteStreamRef.current) {
const video = remoteVideoRef.current as any;
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);
});
}
const syncAndPlay = async (retryCount = 0) => {
if (video._playPromise) return;
try {
if (video.srcObject !== stream) {
console.log('[WebRTC] Sync: Binding stream source');
video.srcObject = stream;
}
video.muted = false;
if (stream.getTracks().length > 0) {
video._playPromise = video.play();
await video._playPromise;
video._playPromise = null;
setNeedsInteraction(false);
console.log('[WebRTC] Sync: Playback successful');
}
} catch (e: any) {
video._playPromise = null;
if (e.name === 'AbortError' && retryCount < 10) {
console.log(`[WebRTC] Sync: Aborted, retrying (${retryCount + 1})...`);
setTimeout(() => syncAndPlay(retryCount + 1), 200);
} else if (e.name === 'NotAllowedError') {
setNeedsInteraction(true);
}
}
};
syncAndPlay();
}
}, [hasRemoteVideo]);
}, [hasRemoteVideo, callState]);
// Setup common peer connection event handlers
const setupPeerHandlers = useCallback((pc: RTCPeerConnection) => {
@@ -246,11 +267,18 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
const streams = event.streams;
console.log(`[WebRTC] Received track: ${track.kind} state: ${track.readyState} streams: ${streams.length}`);
// Ensure we have a stable remote stream object
// Ensure we have a stable remote stream object
if (!remoteStreamRef.current) {
remoteStreamRef.current = streams[0] || new MediaStream([track]);
} else if (!remoteStreamRef.current.getTracks().includes(track)) {
remoteStreamRef.current = streams[0] || new MediaStream();
console.log('[WebRTC] Created new remoteStreamRef.current');
}
if (!remoteStreamRef.current.getTracks().includes(track)) {
console.log(`[WebRTC] Adding track ${track.kind} to remoteStreamRef`);
remoteStreamRef.current.addTrack(track);
} else {
console.log(`[WebRTC] Track ${track.kind} already exists in remoteStreamRef`);
}
const stream = remoteStreamRef.current;
@@ -289,40 +317,30 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
if (remoteVideoRef.current) {
const video = remoteVideoRef.current as any;
const startPlayback = async () => {
// If a play request is already in progress, don't start a new one (as per Chrome docs)
const startPlayback = async (retryCount = 0) => {
if (video._playPromise) {
console.log('[WebRTC] Play is already in progress, waiting...');
console.log('[WebRTC] Play in progress, skipping startPlayback call');
return;
}
try {
// Small stabilization delay (100ms)
await new Promise(r => setTimeout(r, 100));
if (video.srcObject !== stream) {
console.log('[WebRTC] Binding stream to video element');
video.srcObject = stream;
}
video.muted = false;
console.log('[WebRTC] Calling video.play()...');
console.log('[WebRTC] ontrack: calling play()...');
video._playPromise = video.play();
await video._playPromise;
console.log('[WebRTC] Playback confirmed');
video._playPromise = null;
setNeedsInteraction(false);
} catch (e: any) {
video._playPromise = null;
if (e.name === 'NotAllowedError') {
console.warn('[WebRTC] Autoplay blocked, showing UI');
if (e.name === 'AbortError' && retryCount < 10) {
console.log(`[WebRTC] ontrack: Aborted, retrying (${retryCount + 1})...`);
setTimeout(() => startPlayback(retryCount + 1), 200);
} else if (e.name === 'NotAllowedError') {
setNeedsInteraction(true);
} else if (e.name === 'AbortError') {
console.log('[WebRTC] Play aborted (likely concurrent track), will be retried automatically');
} else {
console.error('[WebRTC] Fatal playback error:', e);
}
}
};
@@ -1339,6 +1357,7 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
const onRenegotiate = async (data: { from: string; offer: RTCSessionDescriptionInit }) => {
if (!peerRef.current || data.from !== targetUserIdRef.current) return;
try {
console.log('[onRenegotiate] Received offer, setting remote description');
await peerRef.current.setRemoteDescription(new RTCSessionDescription(data.offer));
const answer = await peerRef.current.createAnswer();
await peerRef.current.setLocalDescription(answer);
@@ -1346,6 +1365,22 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
targetUserId: targetUserIdRef.current,
answer: peerRef.current.localDescription,
});
// Nudge video check after renegotiation
setTimeout(() => {
console.log('[onRenegotiate] Nudging video check');
// pc.ontrack should have fired by now, calling checkVideo via setHasRemoteVideo
// but we can trigger it manually if we find the stream
if (remoteStreamRef.current) {
const hasVideo = remoteStreamRef.current.getVideoTracks().some(t => t.readyState === 'live');
console.log('[onRenegotiate] Manually found video tracks:', hasVideo);
if (hasVideo && !hasRemoteVideoRef.current) {
hasRemoteVideoRef.current = true;
setHasRemoteVideo(true);
setCallType('video');
}
}
}, 1000);
} catch (err) {
console.error('Renegotiation error:', err);
}
@@ -1640,7 +1675,9 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
<span className="text-xl text-white font-medium mb-1">{displayName}</span>
<div className="flex items-center gap-2 text-zinc-500">
<div className="w-2 h-2 rounded-full bg-vortex-500 animate-pulse" />
<span className="text-sm animate-pulse">Ожидание потока...</span>
<span className="text-sm animate-pulse">
{isScreenSharing ? 'Вы транслируете экран...' : 'Ожидание потока...'}
</span>
</div>
</div>
)}