Вернуть стабильный flow видеозвонка

This commit is contained in:
AidarKC
2026-08-10 18:20:35 +04:00
parent ed9e6ce676
commit 08e0fa9206
2 changed files with 98 additions and 23 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
client.version=1.5.25
client.version=1.5.26
server.version=1.4.10
+97 -22
View File
@@ -1333,6 +1333,12 @@ async function finalizeCall(call, {
&& Boolean(call.remoteSessionId)
&& String(localReasonCode || '') !== 'completed'
&& String(debugReason || '') !== 'remote_hangup';
const shouldBroadcastPreAcceptHangup =
!suppressRemoteSignal
&& notifyRemoteHangup
&& !call.remoteSessionId
&& call.direction === 'out'
&& !call.connectedAtMs;
if ((notifyRemoteHangup || shouldNotifyRemoteFailure) && call.remoteSessionId) {
try {
@@ -1342,6 +1348,11 @@ async function finalizeCall(call, {
await sendSignal(call, TYPES.HANGUP, dataValue);
} catch {}
}
if (shouldBroadcastPreAcceptHangup) {
try {
await broadcastSignal(call, TYPES.HANGUP, 'caller_cancelled_before_accept');
} catch {}
}
await closeMedia(call);
if (String(localReasonCode || '') === 'completed') {
@@ -1722,7 +1733,20 @@ async function ensurePeerConnection(call) {
if (call.videoTransportMode === VIDEO_TRANSPORT_SLOT) {
const blackTrack = ensureBlackVideoTrack(call);
if (blackTrack) {
await ensureVideoSenderSlot(call, { sourceTrack: blackTrack, profile: 'placeholder' });
const existingVideoTracks = stream.getVideoTracks?.() || [];
existingVideoTracks.forEach((track) => {
if (track.id !== blackTrack.id) {
try { stream.removeTrack(track); } catch {}
}
});
if (!existingVideoTracks.some((track) => track.id === blackTrack.id)) {
stream.addTrack(blackTrack);
}
const sender = pc.addTrack(blackTrack, stream);
call.videoSender = sender;
call.videoTransceiver = (pc.getTransceivers?.() || []).find((tr) => tr?.sender === sender) || null;
bindVideoSenderStream(call, sender);
await applyVideoSenderEncodingProfile(sender, 'placeholder');
} else {
ensureReservedVideoTransceiver(call);
}
@@ -2154,30 +2178,79 @@ async function applyCameraState(call) {
call.videoSender = videoTransceiver?.sender || call.videoSender || null;
}
if (call.cameraEnabled) {
if (call.videoTransportMode !== VIDEO_TRANSPORT_SLOT) {
call.videoTransportMode = VIDEO_TRANSPORT_SLOT;
}
const videoTrack = await ensureCameraTrack(call);
await ensureVideoSenderSlot(call, { sourceTrack: videoTrack, profile: 'camera' });
videoTransceiver = call.videoTransceiver || videoTransceiver;
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
if (hadSlotBefore) {
const videoTrack = await ensureCameraTrack(call);
if (!videoTransceiver) {
videoTransceiver = pc.addTransceiver('video', { direction: 'sendrecv' });
call.videoTransceiver = videoTransceiver;
call.videoSender = videoTransceiver?.sender || null;
}
} catch {}
videoTrack.enabled = true;
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
}
} catch {}
if (videoTransceiver?.sender) {
await videoTransceiver.sender.replaceTrack(videoTrack);
call.videoSender = videoTransceiver.sender;
bindVideoSenderStream(call, videoTransceiver.sender);
await applyVideoSenderEncodingProfile(videoTransceiver.sender, 'camera');
} else {
const sender = pc.addTrack(videoTrack, call.localStream);
call.videoSender = sender;
bindVideoSenderStream(call, sender);
await applyVideoSenderEncodingProfile(sender, 'camera');
}
videoTrack.enabled = true;
} else {
call.videoTransportMode = VIDEO_TRANSPORT_SLOT;
const videoTrack = await ensureCameraTrack(call);
await ensureVideoSenderSlot(call, { sourceTrack: videoTrack, profile: 'camera' });
videoTransceiver = call.videoTransceiver || videoTransceiver;
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
}
} catch {}
videoTrack.enabled = true;
}
} else {
const blackTrack = ensureBlackVideoTrack(call);
if (call.localStream && blackTrack) {
syncLocalStreamVideoTrack(call, blackTrack, { stopRemoved: false });
}
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
if (hadSlotBefore) {
if (call.localStream && blackTrack) {
const existingVideoTracks = call.localStream.getVideoTracks?.() || [];
existingVideoTracks.forEach((track) => {
if (track.id !== blackTrack.id) {
try { call.localStream.removeTrack(track); } catch {}
}
});
if (!existingVideoTracks.some((track) => track.id === blackTrack.id)) {
call.localStream.addTrack(blackTrack);
}
}
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
}
} catch {}
if (videoTransceiver?.sender) {
try { await videoTransceiver.sender.replaceTrack(blackTrack || null); } catch {}
call.videoSender = videoTransceiver.sender;
bindVideoSenderStream(call, videoTransceiver.sender);
await applyVideoSenderEncodingProfile(videoTransceiver.sender, 'placeholder');
}
} else {
if (call.localStream && blackTrack) {
syncLocalStreamVideoTrack(call, blackTrack, { stopRemoved: false });
}
try {
if (videoTransceiver && videoTransceiver.direction !== 'sendrecv') {
videoTransceiver.direction = 'sendrecv';
}
} catch {}
if (call.videoTransportMode === VIDEO_TRANSPORT_SLOT && blackTrack) {
await ensureVideoSenderSlot(call, { sourceTrack: blackTrack, profile: 'placeholder' });
}
} catch {}
if (call.videoTransportMode === VIDEO_TRANSPORT_SLOT && blackTrack) {
await ensureVideoSenderSlot(call, { sourceTrack: blackTrack, profile: 'placeholder' });
}
await disableCameraTrack(call);
}
@@ -2707,7 +2780,9 @@ export async function handleIncomingCallSignal(evt) {
if (hasVideoSectionInSdp(incomingOffer?.sdp || '')) {
call.videoTransportMode = VIDEO_TRANSPORT_SLOT;
ensureReservedVideoTransceiver(call);
await ensureVideoSenderSlot(call, { sourceTrack: ensureBlackVideoTrack(call), profile: 'placeholder' });
if (!call.videoSender) {
await ensureVideoSenderSlot(call, { sourceTrack: ensureBlackVideoTrack(call), profile: 'placeholder' });
}
if (call.autoEnableCameraOnConnect && call.cameraEnabled) {
try {
await applyCameraState(call);