arnacon-webrtc-service 0.1.113 → 0.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -139,65 +139,69 @@ function patchRouterForDynamicSsrc(pc, logger = console) {
|
|
|
139
139
|
if (!router || router._ssrcPatchApplied) return false;
|
|
140
140
|
const origRouteRtp = router.routeRtp.bind(router);
|
|
141
141
|
router._rtpInCount = 0;
|
|
142
|
+
router._inboundRtpSubscribers = router._inboundRtpSubscribers || new Set();
|
|
142
143
|
router._ssrcPatchApplied = true;
|
|
143
144
|
router.routeRtp = (packet) => {
|
|
144
145
|
router._rtpInCount++;
|
|
145
146
|
const incomingSsrc = packet.header.ssrc;
|
|
146
147
|
if (router.ssrcTable && router.ssrcTable[incomingSsrc]) {
|
|
147
148
|
origRouteRtp(packet);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (oldSsrc !== incomingSsrc) {
|
|
169
|
-
if (router.ssrcTable) {
|
|
170
|
-
delete router.ssrcTable[oldSsrc];
|
|
171
|
-
router.ssrcTable[incomingSsrc] = recv;
|
|
149
|
+
} else {
|
|
150
|
+
const recvs = pc.getReceivers ? pc.getReceivers() : [];
|
|
151
|
+
for (const recv of recvs) {
|
|
152
|
+
const bySsrc = recv.trackBySSRC || {};
|
|
153
|
+
if (bySsrc[incomingSsrc]) break;
|
|
154
|
+
|
|
155
|
+
const tracks = recv.tracks || [];
|
|
156
|
+
if (tracks.length === 1) {
|
|
157
|
+
// Keep using the already-created track object so existing RTP subscriptions survive.
|
|
158
|
+
const existingTrack = tracks[0];
|
|
159
|
+
const oldSsrc = existingTrack.ssrc;
|
|
160
|
+
const oldNum = Number(oldSsrc);
|
|
161
|
+
const canLateBind =
|
|
162
|
+
!Number.isFinite(oldNum) ||
|
|
163
|
+
oldNum <= 0 ||
|
|
164
|
+
oldNum === 1;
|
|
165
|
+
if (!canLateBind) {
|
|
166
|
+
continue;
|
|
172
167
|
}
|
|
173
|
-
if (
|
|
174
|
-
|
|
168
|
+
if (oldSsrc !== incomingSsrc) {
|
|
169
|
+
if (router.ssrcTable) {
|
|
170
|
+
delete router.ssrcTable[oldSsrc];
|
|
171
|
+
router.ssrcTable[incomingSsrc] = recv;
|
|
172
|
+
}
|
|
173
|
+
if (recv.trackBySSRC && recv.trackBySSRC[oldSsrc] === existingTrack) {
|
|
174
|
+
delete recv.trackBySSRC[oldSsrc];
|
|
175
|
+
}
|
|
176
|
+
existingTrack.ssrc = incomingSsrc;
|
|
177
|
+
if (recv.trackBySSRC) recv.trackBySSRC[incomingSsrc] = existingTrack;
|
|
178
|
+
logger.log(`[SSRC-FIX] Rebound existing track: ssrc ${oldSsrc} -> ${incomingSsrc}`);
|
|
175
179
|
}
|
|
176
|
-
|
|
177
|
-
if (recv.trackBySSRC) recv.trackBySSRC[incomingSsrc] = existingTrack;
|
|
178
|
-
logger.log(`[SSRC-FIX] Rebound existing track: ssrc ${oldSsrc} -> ${incomingSsrc}`);
|
|
180
|
+
break;
|
|
179
181
|
}
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
// Legacy placeholder behavior: receivers that start with SSRC=1.
|
|
184
|
+
if (tracks.length > 0 && tracks.every((t) => t.ssrc === 1)) {
|
|
185
|
+
const existingTrack = recv.trackBySSRC?.[1];
|
|
186
|
+
if (existingTrack) {
|
|
187
|
+
const oldSsrc = 1;
|
|
188
|
+
if (router.ssrcTable) {
|
|
189
|
+
delete router.ssrcTable[oldSsrc];
|
|
190
|
+
router.ssrcTable[incomingSsrc] = recv;
|
|
191
|
+
}
|
|
192
|
+
existingTrack.ssrc = incomingSsrc;
|
|
193
|
+
delete recv.trackBySSRC[oldSsrc];
|
|
194
|
+
recv.trackBySSRC[incomingSsrc] = existingTrack;
|
|
195
|
+
logger.log(`[SSRC-FIX] Rebound existing track: ssrc ${oldSsrc} -> ${incomingSsrc}`);
|
|
196
|
+
break;
|
|
191
197
|
}
|
|
192
|
-
existingTrack.ssrc = incomingSsrc;
|
|
193
|
-
delete recv.trackBySSRC[oldSsrc];
|
|
194
|
-
recv.trackBySSRC[incomingSsrc] = existingTrack;
|
|
195
|
-
logger.log(`[SSRC-FIX] Rebound existing track: ssrc ${oldSsrc} -> ${incomingSsrc}`);
|
|
196
|
-
break;
|
|
197
198
|
}
|
|
198
199
|
}
|
|
200
|
+
origRouteRtp(packet);
|
|
201
|
+
}
|
|
202
|
+
for (const fn of router._inboundRtpSubscribers || []) {
|
|
203
|
+
try { fn(packet); } catch (_) {}
|
|
199
204
|
}
|
|
200
|
-
origRouteRtp(packet);
|
|
201
205
|
};
|
|
202
206
|
logger.log("[SSRC-FIX] Router patched for late SSRC binding");
|
|
203
207
|
return true;
|
|
@@ -306,6 +310,9 @@ function createPeerConnectionFactory({
|
|
|
306
310
|
let clientToKamTranscoded = 0;
|
|
307
311
|
let kamToClientTranscoded = 0;
|
|
308
312
|
let kamUnsubscribe = null;
|
|
313
|
+
let kamTrackPackets = 0;
|
|
314
|
+
let kamRouterFallbackPackets = 0;
|
|
315
|
+
let lastKamTrackRtpAt = 0;
|
|
309
316
|
|
|
310
317
|
if (pc1 && session.sipLocalAudioTrack) {
|
|
311
318
|
for (const t of pc1.getTransceivers()) {
|
|
@@ -329,6 +336,8 @@ function createPeerConnectionFactory({
|
|
|
329
336
|
|
|
330
337
|
const kamHandler = (rtp) => {
|
|
331
338
|
if (!session.mediaRelayActive) return;
|
|
339
|
+
kamTrackPackets++;
|
|
340
|
+
lastKamTrackRtpAt = Date.now();
|
|
332
341
|
if (!Number.isFinite(sipPayloadType)) sipPayloadType = Number(rtp?.header?.payloadType);
|
|
333
342
|
if (!kamSourceNotified && session.localAudioTrack) {
|
|
334
343
|
kamSourceNotified = true;
|
|
@@ -345,6 +354,27 @@ function createPeerConnectionFactory({
|
|
|
345
354
|
}
|
|
346
355
|
};
|
|
347
356
|
|
|
357
|
+
const kamRouterFallbackHandler = (rtp) => {
|
|
358
|
+
if (!session.mediaRelayActive || !session.localAudioTrack || !rtp?.header) return;
|
|
359
|
+
// werift can keep counting inbound RTP while a receiver track subscription
|
|
360
|
+
// stops firing after late SSRC binding. Use router packets only when the
|
|
361
|
+
// normal track path has gone quiet, to avoid duplicate audio.
|
|
362
|
+
if (lastKamTrackRtpAt && Date.now() - lastKamTrackRtpAt < 500) return;
|
|
363
|
+
if (!Number.isFinite(sipPayloadType)) sipPayloadType = Number(rtp.header.payloadType);
|
|
364
|
+
if (!kamSourceNotified) {
|
|
365
|
+
kamSourceNotified = true;
|
|
366
|
+
session.localAudioTrack.onSourceChanged.execute({
|
|
367
|
+
sequenceNumber: rtp.header.sequenceNumber,
|
|
368
|
+
timestamp: rtp.header.timestamp,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const outgoing = adaptG711RtpPacket(rtp, clientPayloadType);
|
|
372
|
+
if (outgoing !== rtp) kamToClientTranscoded++;
|
|
373
|
+
kamRouterFallbackPackets++;
|
|
374
|
+
kamToClient++;
|
|
375
|
+
session.localAudioTrack.writeRtp(outgoing);
|
|
376
|
+
};
|
|
377
|
+
|
|
348
378
|
const subscribeKamTrack = (track) => {
|
|
349
379
|
if (!track || track.kind !== "audio" || !session.localAudioTrack || !session.mediaRelayActive) return;
|
|
350
380
|
if (kamUnsubscribe) {
|
|
@@ -373,6 +403,12 @@ function createPeerConnectionFactory({
|
|
|
373
403
|
pc2.onTrack.subscribe((track) => {
|
|
374
404
|
if (track.kind === "audio") subscribeKamTrack(track);
|
|
375
405
|
});
|
|
406
|
+
if (pc2.router?._inboundRtpSubscribers) {
|
|
407
|
+
pc2.router._inboundRtpSubscribers.add(kamRouterFallbackHandler);
|
|
408
|
+
session._relayDisposers.push(() => {
|
|
409
|
+
try { pc2.router._inboundRtpSubscribers.delete(kamRouterFallbackHandler); } catch (_) {}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
376
412
|
}
|
|
377
413
|
// Temporary diagnostics: verify whether RTP is flowing both directions.
|
|
378
414
|
// Remove after media-path issue is resolved.
|
|
@@ -383,6 +419,7 @@ function createPeerConnectionFactory({
|
|
|
383
419
|
`[${sessionId}] RTP-STATS pc1_in=${pc1RtpIn} pc2_in=${pc2RtpIn} client_to_kam=${clientToKam} kam_to_client=${kamToClient} ` +
|
|
384
420
|
`client_pt=${clientPayloadType ?? "?"} sip_pt=${sipPayloadType ?? "?"} ` +
|
|
385
421
|
`c2k_xcode=${clientToKamTranscoded} k2c_xcode=${kamToClientTranscoded} ` +
|
|
422
|
+
`kam_track=${kamTrackPackets} kam_fallback=${kamRouterFallbackPackets} ` +
|
|
386
423
|
`pc2_present=${!!pc2} kam_pipe_active=${kamPipeActive}`
|
|
387
424
|
);
|
|
388
425
|
}, 2000);
|
|
@@ -51,6 +51,12 @@ function createSignalingHandlers({
|
|
|
51
51
|
const payload = msg.payload;
|
|
52
52
|
|
|
53
53
|
if (action === "end-call" && payload) {
|
|
54
|
+
if (sess && sess.phase === "ringing") {
|
|
55
|
+
handleEndCallRenegotiation(sessionId, payload).catch((err) => {
|
|
56
|
+
logger.error(`[${sessionId}] Immediate end-call failed: ${err.message}`);
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
54
60
|
enqueueSignaling(sessionId, "end-call", () => handleEndCallRenegotiation(sessionId, payload));
|
|
55
61
|
return;
|
|
56
62
|
}
|
|
@@ -84,10 +90,22 @@ function createSignalingHandlers({
|
|
|
84
90
|
if (msgType === "call") {
|
|
85
91
|
const action = msg.action;
|
|
86
92
|
if (action === "end") {
|
|
93
|
+
if (sess && sess.phase === "ringing") {
|
|
94
|
+
handleCallEnd(sessionId, "client-initiated").catch((err) => {
|
|
95
|
+
logger.error(`[${sessionId}] Immediate call-end failed: ${err.message}`);
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
87
99
|
enqueueSignaling(sessionId, "call-end", () => handleCallEnd(sessionId, "client-initiated"));
|
|
88
100
|
return;
|
|
89
101
|
}
|
|
90
102
|
if (action === "reject") {
|
|
103
|
+
if (sess && sess.phase === "ringing") {
|
|
104
|
+
handleCallEnd(sessionId, "client-reject").catch((err) => {
|
|
105
|
+
logger.error(`[${sessionId}] Immediate call-reject failed: ${err.message}`);
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
91
109
|
enqueueSignaling(sessionId, "call-reject", () => handleCallEnd(sessionId, "client-reject"));
|
|
92
110
|
return;
|
|
93
111
|
}
|