diff --git a/VERSION.properties b/VERSION.properties index cea4b5e..a05e542 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.30 +client.version=1.2.32 server.version=1.2.26 diff --git a/shine-UI/js/services/call-service.js b/shine-UI/js/services/call-service.js index 64ac2c3..821c213 100644 --- a/shine-UI/js/services/call-service.js +++ b/shine-UI/js/services/call-service.js @@ -1029,13 +1029,24 @@ async function ensurePeerConnection(call) { } async function onAccept(call) { + if (!call) return; + if (call.initialOfferInProgress || call.initialOfferSent) { + await emitDebug(call, 'warn', 'accept_duplicate_ignored', `phase=${call.phase || ''}`); + return; + } + call.initialOfferInProgress = true; cleanupTimers(call); setStatus(call, 'Соединяем…', 'connecting'); - const pc = await ensurePeerConnection(call); - const offer = await pc.createOffer(); - await pc.setLocalDescription(offer); - await sendSignal(call, TYPES.OFFER, JSON.stringify(offer)); - await emitDebug(call, 'info', 'offer_sent', 'offer created and sent'); + try { + const pc = await ensurePeerConnection(call); + const offer = await pc.createOffer(); + await pc.setLocalDescription(offer); + call.initialOfferSent = true; + await sendSignal(call, TYPES.OFFER, JSON.stringify(offer)); + await emitDebug(call, 'info', 'offer_sent', 'offer created and sent'); + } finally { + call.initialOfferInProgress = false; + } } function ensureIncomingNotification(peerLogin) { @@ -1145,10 +1156,14 @@ export async function startDebugConnectionAsResponder({ runId, callId, peerLogin debugRunId: String(runId || '').trim(), debugRole: 'responder', pendingRemoteIceCandidates: [], + initialOfferInProgress: false, + initialOfferSent: false, }; calls.set(cleanCallId, call); } if (!Array.isArray(call.pendingRemoteIceCandidates)) call.pendingRemoteIceCandidates = []; + if (typeof call.initialOfferInProgress !== 'boolean') call.initialOfferInProgress = false; + if (typeof call.initialOfferSent !== 'boolean') call.initialOfferSent = false; activeCallId = cleanCallId; await emitDebug(call, 'info', 'debug_prepare_responder', `peerSessionId=${cleanPeerSessionId}`); @@ -1180,6 +1195,8 @@ export async function startDebugConnectionAsInitiator({ runId, callId, peerLogin debugRunId: String(runId || '').trim(), debugRole: 'initiator', pendingRemoteIceCandidates: [], + initialOfferInProgress: false, + initialOfferSent: false, }; calls.set(cleanCallId, call); @@ -1225,6 +1242,8 @@ export async function startOutgoingCall(peerLogin) { debugRunId: '', debugRole: '', pendingRemoteIceCandidates: [], + initialOfferInProgress: false, + initialOfferSent: false, }; calls.set(callId, call); activeCallId = callId; @@ -1295,6 +1314,8 @@ export async function handleIncomingCallInvite(evt) { debugRunId: '', debugRole: '', pendingRemoteIceCandidates: [], + initialOfferInProgress: false, + initialOfferSent: false, }; calls.set(callId, call); } @@ -1355,6 +1376,10 @@ export async function handleIncomingCallSignal(evt) { } if (type === TYPES.ACCEPT) { + if (call.direction !== 'out') { + await emitDebug(call, 'warn', 'accept_ignored_for_non_outgoing_call', `direction=${call.direction || ''}`); + return; + } call.phase = 'connecting'; setStatus(call, 'Соединяем…', 'connecting'); await onAccept(call); @@ -1403,7 +1428,20 @@ export async function handleIncomingCallSignal(evt) { if (type === TYPES.ANSWER) { try { - const pc = await ensurePeerConnection(call); + if (call.direction !== 'out') { + await emitDebug(call, 'warn', 'answer_ignored_for_non_outgoing_call', `direction=${call.direction || ''}`); + return; + } + if (!call.pc) { + await emitDebug(call, 'warn', 'answer_ignored_without_pc', 'no local peer connection'); + return; + } + const pc = call.pc; + const localType = String(pc.localDescription?.type || '').trim().toLowerCase(); + if (localType !== 'offer') { + await emitDebug(call, 'warn', 'answer_ignored_without_local_offer', `localType=${localType || 'none'}`); + return; + } if (pc.signalingState === 'stable' && pc.remoteDescription) { await emitDebug(call, 'warn', 'answer_duplicate_ignored', 'remote description already set'); return; @@ -1421,8 +1459,14 @@ export async function handleIncomingCallSignal(evt) { if (type === TYPES.ICE) { try { - const pc = await ensurePeerConnection(call); const candidate = JSON.parse(data); + if (!call.pc) { + if (!Array.isArray(call.pendingRemoteIceCandidates)) call.pendingRemoteIceCandidates = []; + call.pendingRemoteIceCandidates.push(candidate); + await emitDebug(call, 'info', 'ice_queued_before_pc', `queue=${call.pendingRemoteIceCandidates.length}`); + return; + } + const pc = call.pc; if (!pc.remoteDescription) { if (!Array.isArray(call.pendingRemoteIceCandidates)) call.pendingRemoteIceCandidates = []; call.pendingRemoteIceCandidates.push(candidate);