Ф3
This commit is contained in:
@@ -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
|
// Ensure remote stream is bound to video element when it becomes available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasRemoteVideo && remoteVideoRef.current && remoteStreamRef.current) {
|
if (callState === 'connected' && remoteVideoRef.current && remoteStreamRef.current) {
|
||||||
const video = remoteVideoRef.current;
|
const video = remoteVideoRef.current as any;
|
||||||
const stream = remoteStreamRef.current;
|
const stream = remoteStreamRef.current;
|
||||||
|
|
||||||
if (video.srcObject !== stream) {
|
const syncAndPlay = async (retryCount = 0) => {
|
||||||
console.log('[WebRTC] Effect: Binding stream to video element');
|
if (video._playPromise) return;
|
||||||
video.srcObject = stream;
|
|
||||||
video.muted = false;
|
try {
|
||||||
video.play().catch(e => {
|
if (video.srcObject !== stream) {
|
||||||
if (e.name === 'NotAllowedError') setNeedsInteraction(true);
|
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
|
// Setup common peer connection event handlers
|
||||||
const setupPeerHandlers = useCallback((pc: RTCPeerConnection) => {
|
const setupPeerHandlers = useCallback((pc: RTCPeerConnection) => {
|
||||||
@@ -246,11 +267,18 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
const streams = event.streams;
|
const streams = event.streams;
|
||||||
console.log(`[WebRTC] Received track: ${track.kind} state: ${track.readyState} streams: ${streams.length}`);
|
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
|
// Ensure we have a stable remote stream object
|
||||||
if (!remoteStreamRef.current) {
|
if (!remoteStreamRef.current) {
|
||||||
remoteStreamRef.current = streams[0] || new MediaStream([track]);
|
remoteStreamRef.current = streams[0] || new MediaStream();
|
||||||
} else if (!remoteStreamRef.current.getTracks().includes(track)) {
|
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);
|
remoteStreamRef.current.addTrack(track);
|
||||||
|
} else {
|
||||||
|
console.log(`[WebRTC] Track ${track.kind} already exists in remoteStreamRef`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stream = remoteStreamRef.current;
|
const stream = remoteStreamRef.current;
|
||||||
@@ -289,40 +317,30 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
if (remoteVideoRef.current) {
|
if (remoteVideoRef.current) {
|
||||||
const video = remoteVideoRef.current as any;
|
const video = remoteVideoRef.current as any;
|
||||||
|
|
||||||
const startPlayback = async () => {
|
const startPlayback = async (retryCount = 0) => {
|
||||||
// If a play request is already in progress, don't start a new one (as per Chrome docs)
|
|
||||||
if (video._playPromise) {
|
if (video._playPromise) {
|
||||||
console.log('[WebRTC] Play is already in progress, waiting...');
|
console.log('[WebRTC] Play in progress, skipping startPlayback call');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Small stabilization delay (100ms)
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
|
||||||
|
|
||||||
if (video.srcObject !== stream) {
|
if (video.srcObject !== stream) {
|
||||||
console.log('[WebRTC] Binding stream to video element');
|
|
||||||
video.srcObject = stream;
|
video.srcObject = stream;
|
||||||
}
|
}
|
||||||
video.muted = false;
|
video.muted = false;
|
||||||
|
|
||||||
console.log('[WebRTC] Calling video.play()...');
|
console.log('[WebRTC] ontrack: calling play()...');
|
||||||
video._playPromise = video.play();
|
video._playPromise = video.play();
|
||||||
|
|
||||||
await video._playPromise;
|
await video._playPromise;
|
||||||
|
|
||||||
console.log('[WebRTC] Playback confirmed');
|
|
||||||
video._playPromise = null;
|
video._playPromise = null;
|
||||||
setNeedsInteraction(false);
|
setNeedsInteraction(false);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
video._playPromise = null;
|
video._playPromise = null;
|
||||||
if (e.name === 'NotAllowedError') {
|
if (e.name === 'AbortError' && retryCount < 10) {
|
||||||
console.warn('[WebRTC] Autoplay blocked, showing UI');
|
console.log(`[WebRTC] ontrack: Aborted, retrying (${retryCount + 1})...`);
|
||||||
|
setTimeout(() => startPlayback(retryCount + 1), 200);
|
||||||
|
} else if (e.name === 'NotAllowedError') {
|
||||||
setNeedsInteraction(true);
|
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 }) => {
|
const onRenegotiate = async (data: { from: string; offer: RTCSessionDescriptionInit }) => {
|
||||||
if (!peerRef.current || data.from !== targetUserIdRef.current) return;
|
if (!peerRef.current || data.from !== targetUserIdRef.current) return;
|
||||||
try {
|
try {
|
||||||
|
console.log('[onRenegotiate] Received offer, setting remote description');
|
||||||
await peerRef.current.setRemoteDescription(new RTCSessionDescription(data.offer));
|
await peerRef.current.setRemoteDescription(new RTCSessionDescription(data.offer));
|
||||||
const answer = await peerRef.current.createAnswer();
|
const answer = await peerRef.current.createAnswer();
|
||||||
await peerRef.current.setLocalDescription(answer);
|
await peerRef.current.setLocalDescription(answer);
|
||||||
@@ -1346,6 +1365,22 @@ export default function CallModal({ isOpen, onClose, targetUser, callType: initi
|
|||||||
targetUserId: targetUserIdRef.current,
|
targetUserId: targetUserIdRef.current,
|
||||||
answer: peerRef.current.localDescription,
|
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) {
|
} catch (err) {
|
||||||
console.error('Renegotiation error:', 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>
|
<span className="text-xl text-white font-medium mb-1">{displayName}</span>
|
||||||
<div className="flex items-center gap-2 text-zinc-500">
|
<div className="flex items-center gap-2 text-zinc-500">
|
||||||
<div className="w-2 h-2 rounded-full bg-vortex-500 animate-pulse" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user