From 08e0fa9206a66b783b9abf59c8a1ee3fe2fe924fb6ac00d613e6fb38ed37cf78 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Mon, 10 Aug 2026 18:20:35 +0400 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=B5=D1=80=D0=BD=D1=83=D1=82=D1=8C=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?flow=20=D0=B2=D0=B8=D0=B4=D0=B5=D0=BE=D0=B7=D0=B2=D0=BE=D0=BD?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.properties | 2 +- shine-UI/js/services/call-service.js | 119 ++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 23 deletions(-) diff --git a/VERSION.properties b/VERSION.properties index 12542726..9536743b 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.5.25 +client.version=1.5.26 server.version=1.4.10 diff --git a/shine-UI/js/services/call-service.js b/shine-UI/js/services/call-service.js index c38cfac4..c470ee07 100644 --- a/shine-UI/js/services/call-service.js +++ b/shine-UI/js/services/call-service.js @@ -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);