werift 0.24.3 → 0.25.0-alpha.0
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/README.md +116 -113
- package/lib/common/src/event.js +2 -1
- package/lib/common/src/event.js.map +1 -1
- package/lib/common/src/transport.d.ts +22 -2
- package/lib/common/src/transport.js +84 -26
- package/lib/common/src/transport.js.map +1 -1
- package/lib/ice/src/exceptions.d.ts +9 -0
- package/lib/ice/src/exceptions.js +23 -1
- package/lib/ice/src/exceptions.js.map +1 -1
- package/lib/ice/src/ice.d.ts +40 -0
- package/lib/ice/src/ice.js +194 -28
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +26 -0
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/stun/const.d.ts +7 -0
- package/lib/ice/src/stun/const.js +8 -0
- package/lib/ice/src/stun/const.js.map +1 -1
- package/lib/ice/src/stun/protocol.js +1 -4
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpProtocol.d.ts +7 -1
- package/lib/ice/src/stun/tcpProtocol.js +65 -14
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -1
- package/lib/ice/src/stun/transaction.d.ts +12 -1
- package/lib/ice/src/stun/transaction.js +37 -4
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/protocol.d.ts +79 -22
- package/lib/ice/src/turn/protocol.js +282 -78
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +8 -0
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/index.mjs +5646 -3451
- package/lib/nonstandard/index.mjs +287 -1004
- package/lib/polyfill/dom.mjs +23794 -0
- package/lib/polyfill/index.mjs +23794 -0
- package/lib/rtp/src/extra/container/mp4/container.js +12 -4
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.js +8 -3
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4Callback.js +3 -1
- package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
- package/lib/sctp/src/chunk.d.ts +5 -0
- package/lib/sctp/src/chunk.js +16 -11
- package/lib/sctp/src/chunk.js.map +1 -1
- package/lib/sctp/src/index.d.ts +3 -1
- package/lib/sctp/src/index.js +10 -1
- package/lib/sctp/src/index.js.map +1 -1
- package/lib/sctp/src/sctp.d.ts +12 -3
- package/lib/sctp/src/sctp.js +51 -18
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/const.js +2 -0
- package/lib/webrtc/src/const.js.map +1 -1
- package/lib/webrtc/src/errors.d.ts +9 -1
- package/lib/webrtc/src/errors.js +19 -0
- package/lib/webrtc/src/errors.js.map +1 -1
- package/lib/webrtc/src/media/codec.d.ts +5 -0
- package/lib/webrtc/src/media/codec.js +21 -0
- package/lib/webrtc/src/media/codec.js.map +1 -1
- package/lib/webrtc/src/media/codecCompatibility.d.ts +23 -0
- package/lib/webrtc/src/media/codecCompatibility.js +138 -0
- package/lib/webrtc/src/media/codecCompatibility.js.map +1 -0
- package/lib/webrtc/src/media/index.d.ts +1 -0
- package/lib/webrtc/src/media/index.js +1 -0
- package/lib/webrtc/src/media/index.js.map +1 -1
- package/lib/webrtc/src/media/router.d.ts +2 -0
- package/lib/webrtc/src/media/router.js +23 -3
- package/lib/webrtc/src/media/router.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +12 -0
- package/lib/webrtc/src/media/rtpReceiver.js +35 -0
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +65 -4
- package/lib/webrtc/src/media/rtpSender.js +264 -35
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +42 -0
- package/lib/webrtc/src/media/rtpTransceiver.js +157 -4
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +29 -1
- package/lib/webrtc/src/media/track.js +164 -5
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/index.d.ts +0 -1
- package/lib/webrtc/src/nonstandard/index.js +0 -1
- package/lib/webrtc/src/nonstandard/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +2 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +1 -2
- package/lib/webrtc/src/nonstandard/userMedia.js +30 -14
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +127 -5
- package/lib/webrtc/src/peerConnection.js +629 -143
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/polyfill/api.d.ts +14 -0
- package/lib/webrtc/src/polyfill/api.js +23 -0
- package/lib/webrtc/src/polyfill/api.js.map +1 -0
- package/lib/webrtc/src/polyfill/browserIdentity.d.ts +11 -0
- package/lib/webrtc/src/polyfill/browserIdentity.js +59 -0
- package/lib/webrtc/src/polyfill/browserIdentity.js.map +1 -0
- package/lib/webrtc/src/polyfill/dom.d.ts +11 -0
- package/lib/webrtc/src/polyfill/dom.js +18 -0
- package/lib/webrtc/src/polyfill/dom.js.map +1 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.d.ts +2 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.js +22 -0
- package/lib/webrtc/src/polyfill/existingMediaDevices.js.map +1 -0
- package/lib/webrtc/src/polyfill/globals.d.ts +28 -0
- package/lib/webrtc/src/polyfill/globals.js +3 -0
- package/lib/webrtc/src/polyfill/globals.js.map +1 -0
- package/lib/webrtc/src/polyfill/index.d.ts +2 -0
- package/lib/webrtc/src/polyfill/index.js +19 -0
- package/lib/webrtc/src/polyfill/index.js.map +1 -0
- package/lib/webrtc/src/polyfill/install.d.ts +13 -0
- package/lib/webrtc/src/polyfill/install.js +266 -0
- package/lib/webrtc/src/polyfill/install.js.map +1 -0
- package/lib/webrtc/src/polyfill/mediaDevices.d.ts +38 -0
- package/lib/webrtc/src/polyfill/mediaDevices.js +228 -0
- package/lib/webrtc/src/polyfill/mediaDevices.js.map +1 -0
- package/lib/webrtc/src/polyfill/mediaRegister.d.ts +51 -0
- package/lib/webrtc/src/polyfill/mediaRegister.js +14 -0
- package/lib/webrtc/src/polyfill/mediaRegister.js.map +1 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.d.ts +3 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.js +52 -0
- package/lib/webrtc/src/polyfill/peerConnectionConfig.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/callback.d.ts +16 -0
- package/lib/webrtc/src/polyfill/registers/callback.js +57 -0
- package/lib/webrtc/src/polyfill/registers/callback.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/empty.d.ts +2 -0
- package/lib/webrtc/src/polyfill/registers/empty.js +28 -0
- package/lib/webrtc/src/polyfill/registers/empty.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.d.ts +36 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.js +242 -0
- package/lib/webrtc/src/polyfill/registers/mp4Webm.js.map +1 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.d.ts +17 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.js +143 -0
- package/lib/webrtc/src/polyfill/registers/rtpRtcp.js.map +1 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.d.ts +18 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.js +36 -0
- package/lib/webrtc/src/polyfill/rtcSessionDescription.js.map +1 -0
- package/lib/webrtc/src/polyfill/rtpCodec.d.ts +17 -0
- package/lib/webrtc/src/polyfill/rtpCodec.js +93 -0
- package/lib/webrtc/src/polyfill/rtpCodec.js.map +1 -0
- package/lib/webrtc/src/polyfill/selectSettings.d.ts +31 -0
- package/lib/webrtc/src/polyfill/selectSettings.js +156 -0
- package/lib/webrtc/src/polyfill/selectSettings.js.map +1 -0
- package/lib/webrtc/src/polyfill/sourceIo.d.ts +21 -0
- package/lib/webrtc/src/polyfill/sourceIo.js +298 -0
- package/lib/webrtc/src/polyfill/sourceIo.js.map +1 -0
- package/lib/webrtc/src/polyfill/trackStop.d.ts +4 -0
- package/lib/webrtc/src/polyfill/trackStop.js +16 -0
- package/lib/webrtc/src/polyfill/trackStop.js.map +1 -0
- package/lib/webrtc/src/sctpManager.d.ts +2 -1
- package/lib/webrtc/src/sctpManager.js +5 -2
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +1 -1
- package/lib/webrtc/src/sdp.js +5 -6
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +38 -1
- package/lib/webrtc/src/sdpManager.js +236 -30
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +43 -12
- package/lib/webrtc/src/secureTransportManager.js +123 -47
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +91 -6
- package/lib/webrtc/src/transceiverManager.js +477 -62
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +8 -0
- package/lib/webrtc/src/transport/ice.js +17 -1
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +3 -2
- package/lib/webrtc/src/transport/sctp.js +8 -2
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/package.json +14 -3
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RTCTrackEvent = exports.defaultPeerConfig = exports.findCodecByMimeType = exports.RTCPeerConnection = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
|
+
const src_1 = require("../../sctp/src");
|
|
5
6
|
const errors_1 = require("./errors");
|
|
6
7
|
const helper_1 = require("./helper");
|
|
7
8
|
const common_1 = require("./imports/common");
|
|
@@ -124,6 +125,40 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
124
125
|
writable: true,
|
|
125
126
|
value: false
|
|
126
127
|
});
|
|
128
|
+
/**同じ tick の複数の変更で negotiationneeded を重複発火しないための予約フラグ */
|
|
129
|
+
Object.defineProperty(this, "negotiationneededScheduled", {
|
|
130
|
+
enumerable: true,
|
|
131
|
+
configurable: true,
|
|
132
|
+
writable: true,
|
|
133
|
+
value: false
|
|
134
|
+
});
|
|
135
|
+
/**交渉が必要な変更の通し番号 */
|
|
136
|
+
Object.defineProperty(this, "negotiationChangeSeq", {
|
|
137
|
+
enumerable: true,
|
|
138
|
+
configurable: true,
|
|
139
|
+
writable: true,
|
|
140
|
+
value: 0
|
|
141
|
+
});
|
|
142
|
+
/**answer まで確定した local offer が反映している変更の通し番号 */
|
|
143
|
+
Object.defineProperty(this, "negotiatedChangeSeq", {
|
|
144
|
+
enumerable: true,
|
|
145
|
+
configurable: true,
|
|
146
|
+
writable: true,
|
|
147
|
+
value: 0
|
|
148
|
+
});
|
|
149
|
+
/**適用中の local offer が反映している変更の通し番号 (rollback で破棄) */
|
|
150
|
+
Object.defineProperty(this, "pendingOfferChangeSeq", {
|
|
151
|
+
enumerable: true,
|
|
152
|
+
configurable: true,
|
|
153
|
+
writable: true,
|
|
154
|
+
value: void 0
|
|
155
|
+
});
|
|
156
|
+
Object.defineProperty(this, "lastCreatedOfferChangeSeq", {
|
|
157
|
+
enumerable: true,
|
|
158
|
+
configurable: true,
|
|
159
|
+
writable: true,
|
|
160
|
+
value: 0
|
|
161
|
+
});
|
|
127
162
|
Object.defineProperty(this, "needRestart", {
|
|
128
163
|
enumerable: true,
|
|
129
164
|
configurable: true,
|
|
@@ -256,31 +291,21 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
256
291
|
writable: true,
|
|
257
292
|
value: {}
|
|
258
293
|
});
|
|
294
|
+
/**交渉が必要な変更を記録し、negotiationneeded を予約する */
|
|
259
295
|
Object.defineProperty(this, "needNegotiation", {
|
|
260
296
|
enumerable: true,
|
|
261
297
|
configurable: true,
|
|
262
298
|
writable: true,
|
|
263
|
-
value:
|
|
264
|
-
this.
|
|
265
|
-
this.
|
|
266
|
-
if (this.negotiationneeded || this.signalingState !== "stable") {
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
this.shouldNegotiationneeded = false;
|
|
270
|
-
setImmediate(() => {
|
|
271
|
-
this.negotiationneeded = true;
|
|
272
|
-
this.onNegotiationneeded.execute();
|
|
273
|
-
if (this.onnegotiationneeded) {
|
|
274
|
-
this.onnegotiationneeded(new globalThis.Event("negotiationneeded"));
|
|
275
|
-
}
|
|
276
|
-
this.emit("negotiationneeded");
|
|
277
|
-
});
|
|
299
|
+
value: () => {
|
|
300
|
+
this.negotiationChangeSeq++;
|
|
301
|
+
this.scheduleNegotiationneeded();
|
|
278
302
|
}
|
|
279
303
|
});
|
|
280
304
|
this.setConfiguration(config);
|
|
281
305
|
this.sdpManager = new sdpManager_1.SDPManager({
|
|
282
306
|
cname: this.cname,
|
|
283
307
|
bundlePolicy: this.config.bundlePolicy,
|
|
308
|
+
mLineReuse: this.config.mLineReuse,
|
|
284
309
|
});
|
|
285
310
|
this.transceiverManager = new media_1.TransceiverManager(this.cname, this.config, this.router);
|
|
286
311
|
this.transceiverManager.onTransceiverAdded.pipe(this.onTransceiverAdded);
|
|
@@ -441,6 +466,15 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
441
466
|
normalizedConfig.bundlePolicy !== this.config.bundlePolicy) {
|
|
442
467
|
throw new Error("bundlePolicy cannot be changed");
|
|
443
468
|
}
|
|
469
|
+
if (normalizedConfig.mLineReuse !== undefined &&
|
|
470
|
+
!MLineReuseModes.includes(normalizedConfig.mLineReuse)) {
|
|
471
|
+
throw (0, errors_1.createWebRtcTypeError)(`mLineReuse must be one of ${MLineReuseModes.join(", ")}`);
|
|
472
|
+
}
|
|
473
|
+
if (isReconfiguration &&
|
|
474
|
+
normalizedConfig.mLineReuse !== undefined &&
|
|
475
|
+
normalizedConfig.mLineReuse !== this.config.mLineReuse) {
|
|
476
|
+
throw (0, errors_1.createWebRtcDomException)("InvalidModificationError", "mLineReuse cannot be changed");
|
|
477
|
+
}
|
|
444
478
|
if (isReconfiguration &&
|
|
445
479
|
normalizedConfig.rtcpMuxPolicy !== undefined &&
|
|
446
480
|
normalizedConfig.rtcpMuxPolicy !== this.config.rtcpMuxPolicy) {
|
|
@@ -460,7 +494,18 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
460
494
|
if ((normalizedConfig.iceCandidatePoolSize ?? 0) > 0) {
|
|
461
495
|
throw new Error("iceCandidatePoolSize > 0 is not supported");
|
|
462
496
|
}
|
|
497
|
+
if (normalizedConfig.sctp !== undefined) {
|
|
498
|
+
const requestedSctpMtu = normalizedConfig.sctp.mtu ?? src_1.DEFAULT_SCTP_MTU;
|
|
499
|
+
(0, src_1.validateSctpMtu)(requestedSctpMtu);
|
|
500
|
+
if (this.sctpManager?.sctpTransport &&
|
|
501
|
+
requestedSctpMtu !== this.config.sctp.mtu) {
|
|
502
|
+
throw new Error("sctp.mtu cannot be changed after SCTP transport creation");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
463
505
|
(0, utils_1.deepMerge)(this.config, normalizedConfig);
|
|
506
|
+
this.config.sctp = {
|
|
507
|
+
mtu: this.config.sctp?.mtu ?? src_1.DEFAULT_SCTP_MTU,
|
|
508
|
+
};
|
|
464
509
|
if (this.config.icePortRange) {
|
|
465
510
|
const [min, max] = this.config.icePortRange;
|
|
466
511
|
if (min === max)
|
|
@@ -476,37 +521,26 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
476
521
|
this.sctpManager.sctpTransport.maxMessageSize =
|
|
477
522
|
this.config.maxMessageSize;
|
|
478
523
|
}
|
|
479
|
-
|
|
480
|
-
...(this.config.codecs.audio || []),
|
|
481
|
-
...(this.config.codecs.video || []),
|
|
482
|
-
])) {
|
|
483
|
-
if (codecParams.payloadType != undefined) {
|
|
484
|
-
continue;
|
|
485
|
-
}
|
|
486
|
-
codecParams.payloadType = 96 + i;
|
|
487
|
-
switch (codecParams.name.toLowerCase()) {
|
|
488
|
-
case "rtx":
|
|
489
|
-
{
|
|
490
|
-
codecParams.parameters = `apt=${codecParams.payloadType - 1}`;
|
|
491
|
-
}
|
|
492
|
-
break;
|
|
493
|
-
case "red":
|
|
494
|
-
{
|
|
495
|
-
if (codecParams.contentType === "audio") {
|
|
496
|
-
const redundant = codecParams.payloadType + 1;
|
|
497
|
-
codecParams.parameters = `${redundant}/${redundant}`;
|
|
498
|
-
codecParams.payloadType = 63;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
524
|
+
assignDynamicPayloadTypes(this.config);
|
|
504
525
|
[
|
|
505
526
|
...(this.config.headerExtensions.audio || []),
|
|
506
527
|
...(this.config.headerExtensions.video || []),
|
|
507
528
|
].forEach((v, i) => {
|
|
508
529
|
v.id = 1 + i;
|
|
509
530
|
});
|
|
531
|
+
// Propagate ICE server changes only to transports still in gathering
|
|
532
|
+
// state "new". JSEP (RFC 8829 §4.1.18): STUN/TURN changes affect the next
|
|
533
|
+
// gathering phase; once gathering has started or finished, nothing is
|
|
534
|
+
// applied to the live Connection (WHIP: createOffer → setConfiguration →
|
|
535
|
+
// setLocalDescription). Per-transport state is used so a gatherer that
|
|
536
|
+
// was reset to "new" (e.g. after ICE restart) is not blocked by a stale
|
|
537
|
+
// manager aggregate of "complete".
|
|
538
|
+
if (isReconfiguration &&
|
|
539
|
+
this.secureManager &&
|
|
540
|
+
(normalizedConfig.iceServers !== undefined ||
|
|
541
|
+
normalizedConfig.turnUdpFamily !== undefined)) {
|
|
542
|
+
this.secureManager.updateIceServers();
|
|
543
|
+
}
|
|
510
544
|
}
|
|
511
545
|
getConfiguration() {
|
|
512
546
|
return clonePeerConfiguration(this.config);
|
|
@@ -518,7 +552,8 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
518
552
|
}
|
|
519
553
|
await this.secureManager.ensureCerts();
|
|
520
554
|
for (const transceiver of this.transceiverManager.getTransceivers()) {
|
|
521
|
-
if (transceiver.codecs.length === 0
|
|
555
|
+
if (transceiver.codecs.length === 0 ||
|
|
556
|
+
transceiver.codecPreferencesNeedResolution) {
|
|
522
557
|
this.transceiverManager.assignTransceiverCodecs(transceiver);
|
|
523
558
|
}
|
|
524
559
|
if (transceiver.headerExtensions.length === 0) {
|
|
@@ -529,10 +564,11 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
529
564
|
const description = this.sdpManager.buildOfferSdp(this.transceiverManager.getTransceivers(), this.sctpTransport);
|
|
530
565
|
const createdOffer = description.toJSON();
|
|
531
566
|
this.lastCreatedOffer = createdOffer;
|
|
567
|
+
this.lastCreatedOfferChangeSeq = this.negotiationChangeSeq;
|
|
532
568
|
return createdOffer;
|
|
533
569
|
}
|
|
534
570
|
createSctpTransport() {
|
|
535
|
-
const sctp = this.sctpManager.createSctpTransport(this.config.maxMessageSize);
|
|
571
|
+
const sctp = this.sctpManager.createSctpTransport(this.config.maxMessageSize, this.config.sctp);
|
|
536
572
|
const dtlsTransport = this.findOrCreateTransport();
|
|
537
573
|
sctp.setDtlsTransport(dtlsTransport);
|
|
538
574
|
return sctp;
|
|
@@ -553,8 +589,39 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
553
589
|
if (this.isClosed) {
|
|
554
590
|
throw (0, errors_1.createWebRtcDomException)("InvalidStateError", "peer closed");
|
|
555
591
|
}
|
|
556
|
-
this.transceiverManager.removeTrack(sender)
|
|
557
|
-
|
|
592
|
+
if (this.transceiverManager.removeTrack(sender)) {
|
|
593
|
+
this.needNegotiation();
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**未交渉の変更が残っていれば negotiationneeded を予約する (stable 復帰時の再判定にも使う) */
|
|
597
|
+
scheduleNegotiationneeded() {
|
|
598
|
+
this.invalidateLastCreatedDescriptions();
|
|
599
|
+
this.shouldNegotiationneeded = true;
|
|
600
|
+
if (this.negotiationneeded ||
|
|
601
|
+
this.negotiationneededScheduled ||
|
|
602
|
+
this.signalingState !== "stable") {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.shouldNegotiationneeded = false;
|
|
606
|
+
this.negotiationneededScheduled = true;
|
|
607
|
+
setImmediate(() => {
|
|
608
|
+
this.negotiationneededScheduled = false;
|
|
609
|
+
if (this.negotiatedChangeSeq >= this.negotiationChangeSeq) {
|
|
610
|
+
// 発火前に適用された local offer が変更をすべて含んでいる
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (this.signalingState !== "stable") {
|
|
614
|
+
// stable に戻った時点で改めて判定する
|
|
615
|
+
this.shouldNegotiationneeded = true;
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
this.negotiationneeded = true;
|
|
619
|
+
this.onNegotiationneeded.execute();
|
|
620
|
+
if (this.onnegotiationneeded) {
|
|
621
|
+
this.onnegotiationneeded(new globalThis.Event("negotiationneeded"));
|
|
622
|
+
}
|
|
623
|
+
this.emit("negotiationneeded");
|
|
624
|
+
});
|
|
558
625
|
}
|
|
559
626
|
invalidateLastCreatedDescriptions() {
|
|
560
627
|
this.lastCreatedAnswer = undefined;
|
|
@@ -569,7 +636,6 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
569
636
|
}
|
|
570
637
|
findOrCreateTransport() {
|
|
571
638
|
const existingDtlsTransport = this.dtlsTransports.find((transport) => transport.state !== "closed");
|
|
572
|
-
const existing = existingDtlsTransport?.iceTransport;
|
|
573
639
|
// Gather ICE candidates for only one track. If the remote endpoint is not bundle-aware, negotiate only one media track.
|
|
574
640
|
// https://w3c.github.io/webrtc-pc/#rtcbundlepolicy-enum
|
|
575
641
|
if (this.sdpManager.bundlePolicy === "max-bundle" ||
|
|
@@ -578,7 +644,16 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
578
644
|
return existingDtlsTransport;
|
|
579
645
|
}
|
|
580
646
|
}
|
|
581
|
-
|
|
647
|
+
return this.createDtlsTransport();
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* @param inheritCredentials false の場合は BUNDLE 外の独立 transport として
|
|
651
|
+
* 既存 transport と異なる ICE credentials を使う
|
|
652
|
+
*/
|
|
653
|
+
createDtlsTransport({ inheritCredentials = true } = {}) {
|
|
654
|
+
const dtlsTransport = this.secureManager.createTransport({
|
|
655
|
+
inheritCredentials,
|
|
656
|
+
});
|
|
582
657
|
dtlsTransport.onRtp.subscribe((rtp) => {
|
|
583
658
|
this.router.routeRtp(rtp);
|
|
584
659
|
});
|
|
@@ -607,21 +682,87 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
607
682
|
log("localDescription not found when ice candidate was gathered");
|
|
608
683
|
return;
|
|
609
684
|
}
|
|
685
|
+
const owner = this.resolveLocalCandidateOwner(iceTransport);
|
|
686
|
+
if (!owner) {
|
|
687
|
+
// 停止 / 拒否した m-line だけが使う transport の候補は通知しない
|
|
688
|
+
log("no accepted m-line owns the ice transport", iceTransport.id);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
610
691
|
this.secureManager.handleNewIceCandidate({
|
|
611
692
|
candidate,
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
media: this._localDescription.media[0],
|
|
615
|
-
transceiver: this.transceiverManager
|
|
616
|
-
.getTransceivers()
|
|
617
|
-
.find((t) => t?.dtlsTransport?.iceTransport.id === iceTransport.id),
|
|
618
|
-
sctpTransport: this.sctpTransport?.dtlsTransport.iceTransport.id === iceTransport.id
|
|
619
|
-
? this.sctpTransport
|
|
620
|
-
: undefined,
|
|
693
|
+
sdpMid: owner.mid,
|
|
694
|
+
sdpMLineIndex: owner.index,
|
|
621
695
|
});
|
|
622
696
|
});
|
|
623
697
|
return dtlsTransport;
|
|
624
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* local candidate の sdpMid / sdpMLineIndex を、その ICE transport を実際に
|
|
701
|
+
* 所有する受け入れ済み m-line (BUNDLE なら tag) に合わせる。
|
|
702
|
+
*/
|
|
703
|
+
resolveLocalCandidateOwner(iceTransport) {
|
|
704
|
+
const description = this._localDescription;
|
|
705
|
+
if (!description) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const owners = description.media
|
|
709
|
+
.map((media, index) => ({ media, index }))
|
|
710
|
+
.filter(({ media }) => {
|
|
711
|
+
if (media.port === 0) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
const mid = media.rtp.muxId;
|
|
715
|
+
if (media.kind === "application") {
|
|
716
|
+
return (!!this.sctpTransport &&
|
|
717
|
+
this.sctpTransport.mid === mid &&
|
|
718
|
+
this.sctpTransport.dtlsTransport?.iceTransport.id ===
|
|
719
|
+
iceTransport.id);
|
|
720
|
+
}
|
|
721
|
+
const transceiver = this.transceiverManager
|
|
722
|
+
.getTransceivers()
|
|
723
|
+
.find((t) => t.mid != undefined && t.mid === mid);
|
|
724
|
+
return (!!transceiver &&
|
|
725
|
+
!transceiver.stopped &&
|
|
726
|
+
transceiver.dtlsTransport?.iceTransport.id === iceTransport.id);
|
|
727
|
+
});
|
|
728
|
+
const tags = description.group
|
|
729
|
+
.filter((group) => group.semantic === "BUNDLE")
|
|
730
|
+
.map((group) => group.items[0]);
|
|
731
|
+
const owner = owners.find(({ media }) => tags.includes(media.rtp.muxId)) ?? owners[0];
|
|
732
|
+
if (!owner) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
return { mid: owner.media.rtp.muxId, index: owner.index };
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* answer 確定後の stopping transceiver を整理する。
|
|
739
|
+
* @returns port 0 を自分の offer で交渉すべき transceiver が残っているか
|
|
740
|
+
*/
|
|
741
|
+
settleStoppingTransceivers() {
|
|
742
|
+
const negotiatedMids = new Set((this.sdpManager.currentLocalDescription?.media ?? [])
|
|
743
|
+
.filter((media) => media.port !== 0 && media.rtp.muxId)
|
|
744
|
+
.map((media) => media.rtp.muxId));
|
|
745
|
+
const pending = this.transceiverManager.settleStoppingTransceivers(negotiatedMids);
|
|
746
|
+
this.closeIdleTransports();
|
|
747
|
+
return pending;
|
|
748
|
+
}
|
|
749
|
+
/**停止した transceiver の transport のうち、live な利用者がいないものを閉じる */
|
|
750
|
+
closeIdleTransports(candidates = this.transceiverManager
|
|
751
|
+
.getTransceivers()
|
|
752
|
+
.filter((t) => t.stopped)
|
|
753
|
+
.map((t) => t.dtlsTransport)) {
|
|
754
|
+
const live = new Set(this.dtlsTransports.map((t) => t.id));
|
|
755
|
+
for (const transport of new Set(candidates)) {
|
|
756
|
+
if (!transport ||
|
|
757
|
+
transport.state === "closed" ||
|
|
758
|
+
live.has(transport.id)) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
transport.stop().catch((error) => {
|
|
762
|
+
log("failed to close idle transport", error);
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
625
766
|
async setLocalDescription(sessionDescription) {
|
|
626
767
|
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription#type
|
|
627
768
|
const implicitOfferState = [
|
|
@@ -632,9 +773,13 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
632
773
|
await this.waitForPendingDescriptionTask();
|
|
633
774
|
if (sessionDescription?.type === "rollback") {
|
|
634
775
|
this.sdpManager.rollbackLocalDescription(this.signalingState);
|
|
776
|
+
for (const transceiver of this.transceiverManager.getTransceivers()) {
|
|
777
|
+
transceiver.pendingLocalOfferCodecs = undefined;
|
|
778
|
+
}
|
|
779
|
+
this.pendingOfferChangeSeq = undefined;
|
|
635
780
|
this.setSignalingState("stable");
|
|
636
781
|
if (this.shouldNegotiationneeded) {
|
|
637
|
-
this.
|
|
782
|
+
this.scheduleNegotiationneeded();
|
|
638
783
|
}
|
|
639
784
|
this.invalidateLastCreatedDescriptions();
|
|
640
785
|
return;
|
|
@@ -674,6 +819,11 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
674
819
|
});
|
|
675
820
|
// # update signaling state
|
|
676
821
|
if (description.type === "offer") {
|
|
822
|
+
// この offer は作成時点までの変更を含む。answer の適用で交渉済みにする
|
|
823
|
+
this.pendingOfferChangeSeq =
|
|
824
|
+
this.lastCreatedOffer?.sdp === sessionDescription.sdp
|
|
825
|
+
? this.lastCreatedOfferChangeSeq
|
|
826
|
+
: undefined;
|
|
677
827
|
this.setSignalingState("have-local-offer");
|
|
678
828
|
}
|
|
679
829
|
else if (description.type === "answer") {
|
|
@@ -682,14 +832,39 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
682
832
|
else if (description.type === "pranswer") {
|
|
683
833
|
this.setSignalingState("have-local-pranswer");
|
|
684
834
|
}
|
|
835
|
+
if (description.type === "answer") {
|
|
836
|
+
// answer の確定で拒否予定の m-line を停止し、RTP pipeline / 遊休 transport を解放する
|
|
837
|
+
this.transceiverManager.commitRemoteOffer();
|
|
838
|
+
// port 0 で答えた m-line (aggressive の inactive など) は offerer 側で拒否として停止が確定する。
|
|
839
|
+
// answerer 側も同じ位置を停止確定にし、offerer が新 MID で再利用できるようにする
|
|
840
|
+
for (const media of description.media) {
|
|
841
|
+
if (!["audio", "video"].includes(media.kind) || media.port !== 0) {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
const transceiver = this.transceiverManager
|
|
845
|
+
.getTransceivers()
|
|
846
|
+
.find((t) => t.mid === media.rtp.muxId && !t.stopped);
|
|
847
|
+
transceiver?.commitStopped({ rejected: true });
|
|
848
|
+
}
|
|
849
|
+
this.closeIdleTransports();
|
|
850
|
+
}
|
|
685
851
|
// # assign MID
|
|
686
852
|
for (const [i, media] of (0, helper_1.enumerate)(description.media)) {
|
|
687
853
|
const mid = media.rtp.muxId;
|
|
688
854
|
this.sdpManager.registerMid(mid);
|
|
689
|
-
if (["audio", "video"].includes(media.kind)) {
|
|
690
|
-
|
|
855
|
+
if (["audio", "video"].includes(media.kind) && media.port !== 0) {
|
|
856
|
+
// 停止済み、別 kind、別 MID の transceiver には割り当てない (MID の一意性を保つ)
|
|
857
|
+
const transceiver = this.transceiverManager
|
|
858
|
+
.getTransceivers()
|
|
859
|
+
.find((t) => t.mLineIndex === i &&
|
|
860
|
+
!t.stopped &&
|
|
861
|
+
t.kind === media.kind &&
|
|
862
|
+
(t.mid == null || t.mid === mid));
|
|
691
863
|
if (transceiver) {
|
|
692
864
|
transceiver.mid = mid;
|
|
865
|
+
if (description.type === "offer") {
|
|
866
|
+
transceiver.pendingLocalOfferCodecs = [...media.rtp.codecs];
|
|
867
|
+
}
|
|
693
868
|
}
|
|
694
869
|
}
|
|
695
870
|
if (media.kind === "application" && this.sctpTransport) {
|
|
@@ -699,14 +874,43 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
699
874
|
// setup ice,dtls role
|
|
700
875
|
const role = description.media.find((media) => media.dtlsParams)?.dtlsParams
|
|
701
876
|
?.role;
|
|
877
|
+
// transport ごとに、その transport を使う受け入れ済み m-line の a=setup を適用する
|
|
878
|
+
// (BUNDLE group 外の独立 transport は先頭 m-line と異なる role を持ちうる)
|
|
879
|
+
const roleOfTransport = new Map();
|
|
880
|
+
for (const media of description.media) {
|
|
881
|
+
const mid = media.rtp.muxId;
|
|
882
|
+
const mediaRole = media.dtlsParams?.role;
|
|
883
|
+
if (media.port === 0 || mid == undefined || !mediaRole) {
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
const owner = media.kind === "application"
|
|
887
|
+
? this.sctpTransport?.mid === mid
|
|
888
|
+
? this.sctpTransport
|
|
889
|
+
: undefined
|
|
890
|
+
: this.transceiverManager
|
|
891
|
+
.getTransceivers()
|
|
892
|
+
.find((t) => t.mid === mid && !t.stopped);
|
|
893
|
+
if (owner?.dtlsTransport && !roleOfTransport.has(owner.dtlsTransport)) {
|
|
894
|
+
roleOfTransport.set(owner.dtlsTransport, mediaRole);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
702
897
|
this.secureManager.setLocalRole({
|
|
703
898
|
type: description.type === "offer" ? "offer" : "answer",
|
|
704
899
|
role,
|
|
900
|
+
roleOfTransport,
|
|
705
901
|
});
|
|
706
902
|
// # configure direction
|
|
707
903
|
if (["answer", "pranswer"].includes(description.type)) {
|
|
708
|
-
for (const
|
|
709
|
-
const
|
|
904
|
+
for (const media of description.media) {
|
|
905
|
+
const t = this.transceiverManager
|
|
906
|
+
.getTransceivers()
|
|
907
|
+
.find((t) => t.mid != undefined && t.mid === media.rtp.muxId);
|
|
908
|
+
if (!t || t.stopped || t.pendingRejection) {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
const direction = t.stopping
|
|
912
|
+
? "inactive"
|
|
913
|
+
: (0, utils_1.andDirection)(t.direction, t.offerDirection);
|
|
710
914
|
t.setCurrentDirection(direction);
|
|
711
915
|
}
|
|
712
916
|
}
|
|
@@ -723,14 +927,16 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
723
927
|
});
|
|
724
928
|
}
|
|
725
929
|
this.sdpManager.setLocal(description, this.transceiverManager.getTransceivers(), this.sctpTransport);
|
|
726
|
-
|
|
727
|
-
|
|
930
|
+
// answerer の stop() は次の自分の offer で交渉する
|
|
931
|
+
const hasUnnegotiatedStop = description.type === "answer" && this.settleStoppingTransceivers();
|
|
932
|
+
if (this.shouldNegotiationneeded || hasUnnegotiatedStop) {
|
|
933
|
+
this.scheduleNegotiationneeded();
|
|
728
934
|
}
|
|
729
935
|
this.invalidateLastCreatedDescriptions();
|
|
730
936
|
return description;
|
|
731
937
|
}
|
|
732
938
|
async gatherCandidates() {
|
|
733
|
-
await this.secureManager.gatherCandidates(
|
|
939
|
+
await this.secureManager.gatherCandidates();
|
|
734
940
|
}
|
|
735
941
|
async addIceCandidate(candidateMessage = {}) {
|
|
736
942
|
if (this.isClosed) {
|
|
@@ -821,61 +1027,81 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
821
1027
|
sessionDescription = sessionDescription.toSdp();
|
|
822
1028
|
}
|
|
823
1029
|
await this.waitForPendingDescriptionTask();
|
|
1030
|
+
let codecPlan = new Map();
|
|
1031
|
+
if (sessionDescription.type &&
|
|
1032
|
+
sessionDescription.type !== "rollback" &&
|
|
1033
|
+
sessionDescription.sdp) {
|
|
1034
|
+
const preview = this.sdpManager.parseSdp({
|
|
1035
|
+
sdp: sessionDescription.sdp,
|
|
1036
|
+
isLocal: false,
|
|
1037
|
+
signalingState: this.signalingState,
|
|
1038
|
+
type: sessionDescription.type,
|
|
1039
|
+
});
|
|
1040
|
+
// 本適用と同じ対応付けで全 m-line の codec を副作用なしに解決する
|
|
1041
|
+
const planned = new Set();
|
|
1042
|
+
codecPlan = this.transceiverManager.planRemoteRtpCodecs(preview, (remoteMedia, index) => {
|
|
1043
|
+
const transceiver = this.findTransceiverForRemoteMedia(remoteMedia, index, planned);
|
|
1044
|
+
if (transceiver)
|
|
1045
|
+
planned.add(transceiver);
|
|
1046
|
+
return transceiver;
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
824
1049
|
const needsImplicitLocalRollback = sessionDescription.type === "offer" &&
|
|
825
1050
|
["have-local-offer", "have-local-pranswer"].includes(this.signalingState);
|
|
826
1051
|
if (needsImplicitLocalRollback) {
|
|
827
1052
|
this.sdpManager.rollbackLocalDescription(this.signalingState);
|
|
1053
|
+
for (const transceiver of this.transceiverManager.getTransceivers()) {
|
|
1054
|
+
transceiver.pendingLocalOfferCodecs = undefined;
|
|
1055
|
+
}
|
|
1056
|
+
this.pendingOfferChangeSeq = undefined;
|
|
828
1057
|
this.shouldNegotiationneeded = true;
|
|
829
1058
|
this.setSignalingState("stable");
|
|
830
1059
|
await Promise.resolve();
|
|
831
1060
|
}
|
|
832
1061
|
// # parse and validate description
|
|
1062
|
+
this.assertAnswerKeepsSharedTransports(sessionDescription);
|
|
833
1063
|
const remoteSdp = this.sdpManager.setRemoteDescription(sessionDescription, this.signalingState);
|
|
834
1064
|
if (!remoteSdp) {
|
|
1065
|
+
// 非対応 re-offer などの rollback では元の transceiver 対応を復元する
|
|
1066
|
+
const transportsBeforeRollback = this.transceiverManager
|
|
1067
|
+
.getTransceivers()
|
|
1068
|
+
.map((t) => t.dtlsTransport);
|
|
1069
|
+
this.transceiverManager.rollbackRemoteOffer();
|
|
1070
|
+
this.closeIdleTransports(transportsBeforeRollback);
|
|
835
1071
|
this.setSignalingState("stable");
|
|
836
1072
|
if (this.shouldNegotiationneeded) {
|
|
837
|
-
this.
|
|
1073
|
+
this.scheduleNegotiationneeded();
|
|
838
1074
|
}
|
|
839
1075
|
this.invalidateLastCreatedDescriptions();
|
|
840
1076
|
return;
|
|
841
1077
|
}
|
|
842
|
-
|
|
1078
|
+
if (remoteSdp.type === "offer") {
|
|
1079
|
+
this.transceiverManager.beginRemoteOffer();
|
|
1080
|
+
}
|
|
843
1081
|
// # apply description
|
|
844
|
-
const
|
|
845
|
-
[
|
|
846
|
-
|
|
847
|
-
|
|
1082
|
+
const bundleGroups = this.config.bundlePolicy === "disable"
|
|
1083
|
+
? []
|
|
1084
|
+
: remoteSdp.group.filter((group) => group.semantic === "BUNDLE");
|
|
1085
|
+
const groupOfMid = (mid) => mid == undefined
|
|
1086
|
+
? undefined
|
|
1087
|
+
: bundleGroups.find((group) => group.items.includes(mid));
|
|
1088
|
+
// ## associate m-lines with transceivers / sctp
|
|
1089
|
+
const associated = new Set();
|
|
1090
|
+
const entries = remoteSdp.media.map((remoteMedia, index) => {
|
|
848
1091
|
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
849
|
-
let transceiver = this.
|
|
850
|
-
.getTransceivers()
|
|
851
|
-
.find((t) => matchTransceiverWithMedia(t, remoteMedia));
|
|
1092
|
+
let transceiver = this.findTransceiverForRemoteMedia(remoteMedia, index, associated);
|
|
852
1093
|
if (!transceiver) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
});
|
|
857
|
-
transceiver.mid = remoteMedia.rtp.muxId ?? null;
|
|
858
|
-
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
859
|
-
}
|
|
860
|
-
else {
|
|
861
|
-
if (transceiver.direction === "inactive" && transceiver.stopping) {
|
|
862
|
-
transceiver.stopped = true;
|
|
863
|
-
if (sessionDescription.type === "answer") {
|
|
864
|
-
transceiver.setCurrentDirection("inactive");
|
|
865
|
-
}
|
|
866
|
-
return;
|
|
1094
|
+
if (remoteMedia.port === 0) {
|
|
1095
|
+
// 未知の MID の拒否済み m-line には transceiver を関連付けない
|
|
1096
|
+
return { remoteMedia, index };
|
|
867
1097
|
}
|
|
1098
|
+
transceiver = this.addRemoteTransceiver(remoteMedia, index);
|
|
868
1099
|
}
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
bundleTransport = transceiver.dtlsTransport;
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
transceiver.setDtlsTransport(bundleTransport);
|
|
875
|
-
}
|
|
1100
|
+
else if (transceiver.mid == null) {
|
|
1101
|
+
this.transceiverManager.associateMLine(transceiver, index);
|
|
876
1102
|
}
|
|
877
|
-
|
|
878
|
-
|
|
1103
|
+
associated.add(transceiver);
|
|
1104
|
+
return { remoteMedia, index, transceiver };
|
|
879
1105
|
}
|
|
880
1106
|
else if (remoteMedia.kind === "application") {
|
|
881
1107
|
let sctpTransport = this.sctpTransport;
|
|
@@ -883,32 +1109,126 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
883
1109
|
sctpTransport = this.createSctpTransport();
|
|
884
1110
|
sctpTransport.mid = remoteMedia.rtp.muxId;
|
|
885
1111
|
}
|
|
886
|
-
|
|
887
|
-
if (!bundleTransport) {
|
|
888
|
-
bundleTransport = sctpTransport.dtlsTransport;
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
sctpTransport.setDtlsTransport(bundleTransport);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
dtlsTransport = sctpTransport.dtlsTransport;
|
|
895
|
-
this.sctpManager.setRemoteSCTP(remoteMedia, i);
|
|
1112
|
+
return { remoteMedia, index, sctpTransport };
|
|
896
1113
|
}
|
|
897
1114
|
else {
|
|
898
1115
|
throw new Error("invalid media kind");
|
|
899
1116
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1117
|
+
});
|
|
1118
|
+
if (remoteSdp.type === "offer") {
|
|
1119
|
+
this.transceiverManager.releaseUnassociatedReservations(associated, remoteSdp.media.length);
|
|
1120
|
+
}
|
|
1121
|
+
const ownerOf = (entry) => entry.transceiver ?? entry.sctpTransport;
|
|
1122
|
+
// ## transport ownership
|
|
1123
|
+
// remote offer の所有関係は bundlePolicy によらず offer の BUNDLE group だけで決まる
|
|
1124
|
+
// (max-bundle は自分の offer にだけ効く)。group ごとに 1 つの transport を共有し、
|
|
1125
|
+
// 異なる group や group 外の m-line とは共有しない。
|
|
1126
|
+
// remote answer では offer で渡した ICE credentials を保つため transport を作り直さない
|
|
1127
|
+
// (共有 transport を分割する answer は assertAnswerKeepsSharedTransports で事前に拒否済み)
|
|
1128
|
+
const isRemoteOffer = remoteSdp.type === "offer";
|
|
1129
|
+
const liveTransport = (transport) => !!transport && transport.state !== "closed";
|
|
1130
|
+
const groupTransports = new Map();
|
|
1131
|
+
const claimed = new Set();
|
|
1132
|
+
for (const group of bundleGroups) {
|
|
1133
|
+
const members = entries.filter((entry) => !!ownerOf(entry) &&
|
|
1134
|
+
group.items.includes(entry.remoteMedia.rtp.muxId));
|
|
1135
|
+
if (members.length === 0) {
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
// 既存 member の transport を優先し、他の group が使っていれば新しい transport を作る
|
|
1139
|
+
let shared = members
|
|
1140
|
+
.map((entry) => ownerOf(entry).dtlsTransport)
|
|
1141
|
+
.find((transport) => liveTransport(transport) &&
|
|
1142
|
+
(!isRemoteOffer || !claimed.has(transport)));
|
|
1143
|
+
if (!shared) {
|
|
1144
|
+
shared = this.createDtlsTransport({ inheritCredentials: false });
|
|
1145
|
+
}
|
|
1146
|
+
groupTransports.set(group, shared);
|
|
1147
|
+
claimed.add(shared);
|
|
1148
|
+
for (const entry of members) {
|
|
1149
|
+
const owner = ownerOf(entry);
|
|
1150
|
+
if (owner.dtlsTransport !== shared) {
|
|
1151
|
+
owner.setDtlsTransport(shared);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
if (isRemoteOffer) {
|
|
1156
|
+
// group 外 (group のない offer を含む) で受け入れた m-line は独立した transport / ICE credentials を持つ
|
|
1157
|
+
for (const entry of entries) {
|
|
1158
|
+
const owner = ownerOf(entry);
|
|
1159
|
+
if (!owner ||
|
|
1160
|
+
entry.remoteMedia.port === 0 ||
|
|
1161
|
+
groupOfMid(entry.remoteMedia.rtp.muxId)) {
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (!liveTransport(owner.dtlsTransport) ||
|
|
1165
|
+
claimed.has(owner.dtlsTransport)) {
|
|
1166
|
+
owner.setDtlsTransport(this.createDtlsTransport({ inheritCredentials: false }));
|
|
1167
|
+
}
|
|
1168
|
+
claimed.add(owner.dtlsTransport);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
// ## apply RTP / SCTP (受け入れ判定)
|
|
1172
|
+
const acceptedEntries = new Set();
|
|
1173
|
+
for (const entry of entries) {
|
|
1174
|
+
const { remoteMedia, index } = entry;
|
|
1175
|
+
if (entry.transceiver) {
|
|
1176
|
+
if (this.transceiverManager.setRemoteRTP(entry.transceiver, remoteMedia, remoteSdp.type, index, codecPlan.get(index) ?? [])) {
|
|
1177
|
+
acceptedEntries.add(entry);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
else if (entry.sctpTransport) {
|
|
1181
|
+
this.sctpManager.setRemoteSCTP(remoteMedia, index);
|
|
1182
|
+
acceptedEntries.add(entry);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
// ICE / DTLS パラメータは group で最初に受け入れた member (tag) から適用する
|
|
1186
|
+
// (codec 不一致で拒否した member のパラメータは使わない)
|
|
1187
|
+
const groupParamSources = new Map();
|
|
1188
|
+
for (const group of bundleGroups) {
|
|
1189
|
+
for (const mid of group.items) {
|
|
1190
|
+
const entry = entries.find((e) => e.remoteMedia.rtp.muxId === mid &&
|
|
1191
|
+
e.remoteMedia.port !== 0 &&
|
|
1192
|
+
acceptedEntries.has(e));
|
|
1193
|
+
if (entry) {
|
|
1194
|
+
groupParamSources.set(group, entry);
|
|
1195
|
+
break;
|
|
908
1196
|
}
|
|
909
1197
|
}
|
|
910
|
-
|
|
911
|
-
|
|
1198
|
+
}
|
|
1199
|
+
// ## apply transport parameters
|
|
1200
|
+
for (const entry of entries) {
|
|
1201
|
+
const { remoteMedia } = entry;
|
|
1202
|
+
const owner = ownerOf(entry);
|
|
1203
|
+
if (!owner) {
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
const accepted = acceptedEntries.has(entry);
|
|
1207
|
+
const dtlsTransport = owner.dtlsTransport;
|
|
1208
|
+
const group = groupOfMid(remoteMedia.rtp.muxId);
|
|
1209
|
+
if (remoteMedia.port === 0 ||
|
|
1210
|
+
!dtlsTransport ||
|
|
1211
|
+
dtlsTransport.state === "closed" ||
|
|
1212
|
+
// group 外の拒否 section の transport は使わない
|
|
1213
|
+
(!accepted && !group)) {
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
const iceTransport = dtlsTransport.iceTransport;
|
|
1217
|
+
const ownsTransportParams = !group || groupParamSources.get(group) === entry;
|
|
1218
|
+
if (ownsTransportParams) {
|
|
1219
|
+
if (remoteMedia.iceParams) {
|
|
1220
|
+
const renomination = !!this.sdpManager.inactiveRemoteMedia;
|
|
1221
|
+
iceTransport.setRemoteParams(remoteMedia.iceParams, renomination);
|
|
1222
|
+
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
1223
|
+
// RFC 8445 S6.1.1
|
|
1224
|
+
if (remoteMedia.iceParams.iceLite &&
|
|
1225
|
+
!iceTransport.connection.iceLite) {
|
|
1226
|
+
iceTransport.connection.iceControlling = true;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
if (remoteMedia.dtlsParams) {
|
|
1230
|
+
dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
|
|
1231
|
+
}
|
|
912
1232
|
}
|
|
913
1233
|
// # add ICE candidates
|
|
914
1234
|
remoteMedia.iceCandidates.forEach(iceTransport.addRemoteCandidate);
|
|
@@ -920,21 +1240,17 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
920
1240
|
dtlsTransport.role =
|
|
921
1241
|
remoteMedia.dtlsParams.role === "client" ? "server" : "client";
|
|
922
1242
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
for (const transceiver of removedTransceivers) {
|
|
933
|
-
// todo: handle answer side transceiver removal work.
|
|
934
|
-
// event should trigger to notify media source to stop.
|
|
935
|
-
transceiver.stop();
|
|
936
|
-
transceiver.stopped = true;
|
|
1243
|
+
}
|
|
1244
|
+
if (remoteSdp.type === "answer") {
|
|
1245
|
+
// answer に含まれない既存 m-line は remote 起因の停止として確定する
|
|
1246
|
+
for (const transceiver of this.transceiverManager.getTransceivers()) {
|
|
1247
|
+
if (!associated.has(transceiver) &&
|
|
1248
|
+
transceiver.mid != undefined &&
|
|
1249
|
+
!transceiver.stopped) {
|
|
1250
|
+
transceiver.commitStopped({ rejected: true });
|
|
1251
|
+
}
|
|
937
1252
|
}
|
|
1253
|
+
this.closeIdleTransports();
|
|
938
1254
|
}
|
|
939
1255
|
if (remoteSdp.type === "offer") {
|
|
940
1256
|
this.setSignalingState("have-remote-offer");
|
|
@@ -955,11 +1271,96 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
955
1271
|
});
|
|
956
1272
|
}
|
|
957
1273
|
this.negotiationneeded = false;
|
|
958
|
-
if (
|
|
959
|
-
this.
|
|
1274
|
+
if (remoteSdp.type === "answer" &&
|
|
1275
|
+
this.pendingOfferChangeSeq != undefined) {
|
|
1276
|
+
this.negotiatedChangeSeq = Math.max(this.negotiatedChangeSeq, this.pendingOfferChangeSeq);
|
|
1277
|
+
this.pendingOfferChangeSeq = undefined;
|
|
1278
|
+
}
|
|
1279
|
+
const hasUnnegotiatedStop = remoteSdp.type === "answer" && this.settleStoppingTransceivers();
|
|
1280
|
+
if (this.shouldNegotiationneeded || hasUnnegotiatedStop) {
|
|
1281
|
+
this.scheduleNegotiationneeded();
|
|
960
1282
|
}
|
|
961
1283
|
this.invalidateLastCreatedDescriptions();
|
|
962
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* remote answer / pranswer の検証 (offerer 側、状態を変更する前に実行する)。
|
|
1287
|
+
* local offer で 1 つの transport を共有した m-line を、answer が同じ BUNDLE group に置かずに
|
|
1288
|
+
* 異なる ICE credentials で受け入れることはできない (RFC 8843 7.3.2)。共有 transport は分割できず、
|
|
1289
|
+
* 新しい transport を作ると offer で渡した ICE credentials とも一致しなくなる。
|
|
1290
|
+
* group の記述が MID と一致しなくても remote の ICE credentials が同じなら、同じ transport のまま扱う。
|
|
1291
|
+
*/
|
|
1292
|
+
assertAnswerKeepsSharedTransports(sessionDescription) {
|
|
1293
|
+
if (!["answer", "pranswer"].includes(sessionDescription.type ?? "") ||
|
|
1294
|
+
!["have-local-offer", "have-local-pranswer"].includes(this.signalingState) ||
|
|
1295
|
+
!sessionDescription.sdp ||
|
|
1296
|
+
this.config.bundlePolicy === "disable") {
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
const answer = sdp_1.SessionDescription.parse(sessionDescription.sdp);
|
|
1300
|
+
const answerGroups = answer.group.filter((g) => g.semantic === "BUNDLE");
|
|
1301
|
+
const byTransport = new Map();
|
|
1302
|
+
for (const media of answer.media) {
|
|
1303
|
+
const mid = media.rtp.muxId;
|
|
1304
|
+
if (media.port === 0 || mid == undefined) {
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
const owner = media.kind === "application"
|
|
1308
|
+
? this.sctpTransport?.mid === mid
|
|
1309
|
+
? this.sctpTransport
|
|
1310
|
+
: undefined
|
|
1311
|
+
: this.transceiverManager
|
|
1312
|
+
.getTransceivers()
|
|
1313
|
+
.find((t) => t.mid === mid && !t.stopped);
|
|
1314
|
+
const transport = owner?.dtlsTransport;
|
|
1315
|
+
if (!transport) {
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
const current = {
|
|
1319
|
+
group: answerGroups.find((g) => g.items.includes(mid)),
|
|
1320
|
+
credentials: `${media.iceParams?.usernameFragment}:${media.iceParams?.password}`,
|
|
1321
|
+
};
|
|
1322
|
+
const first = byTransport.get(transport);
|
|
1323
|
+
if (!first) {
|
|
1324
|
+
byTransport.set(transport, current);
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
const sameGroup = !!current.group && current.group === first.group;
|
|
1328
|
+
if (!sameGroup && current.credentials !== first.credentials) {
|
|
1329
|
+
throw (0, errors_1.createWebRtcDomException)("InvalidAccessError", `mid=${mid} shares a transport in the local offer but the remote ${sessionDescription.type} moves it out of that BUNDLE group`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* remote m-line に対応する transceiver を探す。MID の一致を優先し、
|
|
1335
|
+
* 未関連付け transceiver は同じ offer 内で異なる m-line に割り当てる。
|
|
1336
|
+
*/
|
|
1337
|
+
findTransceiverForRemoteMedia(remoteMedia, index, associated) {
|
|
1338
|
+
const candidates = this.transceiverManager
|
|
1339
|
+
.getTransceivers()
|
|
1340
|
+
.filter((t) => !associated.has(t) && t.kind === remoteMedia.kind);
|
|
1341
|
+
const mid = remoteMedia.rtp.muxId;
|
|
1342
|
+
const byMid = mid != undefined ? candidates.find((t) => t.mid === mid) : undefined;
|
|
1343
|
+
if (byMid) {
|
|
1344
|
+
return byMid;
|
|
1345
|
+
}
|
|
1346
|
+
if (remoteMedia.port === 0) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const unassociated = candidates.filter((t) => t.mid == null && !t.stopping && !t.stopped);
|
|
1350
|
+
return unassociated.find((t) => t.mLineIndex === index) ?? unassociated[0];
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* remote offer / answer の m-line 用に transceiver を作る。
|
|
1354
|
+
* 同じ位置に停止済みの旧 transceiver があれば、それを復活させず新しい receiver/router で置き換える。
|
|
1355
|
+
*/
|
|
1356
|
+
addRemoteTransceiver(remoteMedia, index) {
|
|
1357
|
+
const dtlsTransport = this.findOrCreateTransport();
|
|
1358
|
+
const transceiver = this.transceiverManager.addTransceiver(remoteMedia.kind, dtlsTransport, { direction: "recvonly" }, { remoteMLineIndex: index });
|
|
1359
|
+
transceiver.mid = remoteMedia.rtp.muxId ?? null;
|
|
1360
|
+
this.secureManager.updateIceConnectionState();
|
|
1361
|
+
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
1362
|
+
return transceiver;
|
|
1363
|
+
}
|
|
963
1364
|
addTransceiver(trackOrKind, options = {}) {
|
|
964
1365
|
const dtlsTransport = this.findOrCreateTransport();
|
|
965
1366
|
const transceiver = this.transceiverManager.addTransceiver(trackOrKind, dtlsTransport, options);
|
|
@@ -983,6 +1384,38 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
983
1384
|
async createAnswer() {
|
|
984
1385
|
this.assertNotClosed();
|
|
985
1386
|
await this.secureManager.ensureCerts();
|
|
1387
|
+
// setCodecPreferences() で無効化された transceiver を再解決する。
|
|
1388
|
+
// offer 側と異なり answer は remote offer との交渉結果を使い、
|
|
1389
|
+
// sender/receiver の codec 状態も新しい negotiated codec に同期する。
|
|
1390
|
+
for (const transceiver of this.transceiverManager.getTransceivers()) {
|
|
1391
|
+
if (transceiver.codecs.length !== 0 &&
|
|
1392
|
+
!transceiver.codecPreferencesNeedResolution) {
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
// 拒否予定 / 停止済みの m-line は port 0 で answer するため codec を解決しない
|
|
1396
|
+
if (transceiver.pendingRejection ||
|
|
1397
|
+
transceiver.stopping ||
|
|
1398
|
+
transceiver.stopped) {
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
const remoteDescription = this.sdpManager._remoteDescription;
|
|
1402
|
+
const remoteMedia = remoteDescription?.media.find((media) => media.rtp.muxId != undefined &&
|
|
1403
|
+
media.rtp.muxId === transceiver.mid &&
|
|
1404
|
+
media.kind === transceiver.kind) ??
|
|
1405
|
+
(transceiver.mLineIndex != undefined
|
|
1406
|
+
? remoteDescription?.media[transceiver.mLineIndex]
|
|
1407
|
+
: undefined);
|
|
1408
|
+
if (remoteMedia && ["audio", "video"].includes(remoteMedia.kind)) {
|
|
1409
|
+
this.transceiverManager.resyncAnswerCodecs(transceiver, remoteMedia);
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
this.transceiverManager.assignTransceiverCodecs(transceiver);
|
|
1413
|
+
}
|
|
1414
|
+
if (transceiver.headerExtensions.length === 0) {
|
|
1415
|
+
transceiver.headerExtensions =
|
|
1416
|
+
this.config.headerExtensions[transceiver.kind] ?? [];
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
986
1419
|
const description = this.sdpManager.buildAnswerSdp({
|
|
987
1420
|
transceivers: this.transceiverManager.getTransceivers(),
|
|
988
1421
|
sctpTransport: this.sctpTransport,
|
|
@@ -1045,29 +1478,68 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
1045
1478
|
this.pendingRemoteCandidates.length = 0;
|
|
1046
1479
|
this.setSignalingState("closed");
|
|
1047
1480
|
this.transceiverManager.close();
|
|
1048
|
-
|
|
1481
|
+
// SCTP ABORT は DTLS/ICE が生きている間に送る(close は abrupt であり SHUTDOWN ではない)
|
|
1049
1482
|
await this.sctpManager.close();
|
|
1050
|
-
this.
|
|
1051
|
-
|
|
1052
|
-
this.
|
|
1053
|
-
this.signalingStateChange.allUnsubscribe();
|
|
1054
|
-
this.onTransceiverAdded.allUnsubscribe();
|
|
1055
|
-
this.onRemoteTransceiverAdded.allUnsubscribe();
|
|
1056
|
-
this.onIceCandidate.allUnsubscribe();
|
|
1483
|
+
await this.secureManager.close();
|
|
1484
|
+
// 公開 Event を完了させ、購読者・クロージャが PeerConnection を保持し続けないようにする
|
|
1485
|
+
this.completePeerEvents();
|
|
1057
1486
|
log("peerConnection closed");
|
|
1058
1487
|
}
|
|
1488
|
+
completePeerEvents() {
|
|
1489
|
+
const events = [
|
|
1490
|
+
this.onDataChannel,
|
|
1491
|
+
this.iceGatheringStateChange,
|
|
1492
|
+
this.iceConnectionStateChange,
|
|
1493
|
+
this.signalingStateChange,
|
|
1494
|
+
this.connectionStateChange,
|
|
1495
|
+
this.onTransceiverAdded,
|
|
1496
|
+
this.onRemoteTransceiverAdded,
|
|
1497
|
+
this.onIceCandidate,
|
|
1498
|
+
this.onNegotiationneeded,
|
|
1499
|
+
];
|
|
1500
|
+
for (const event of events) {
|
|
1501
|
+
if (!event.ended) {
|
|
1502
|
+
event.complete();
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1059
1506
|
}
|
|
1060
1507
|
exports.RTCPeerConnection = RTCPeerConnection;
|
|
1508
|
+
const MLineReuseModes = ["compatible", "aggressive"];
|
|
1061
1509
|
const findCodecByMimeType = (codecs, target) => codecs.find((localCodec) => localCodec.mimeType.toLowerCase() === target.mimeType.toLowerCase())
|
|
1062
1510
|
? target
|
|
1063
1511
|
: undefined;
|
|
1064
1512
|
exports.findCodecByMimeType = findCodecByMimeType;
|
|
1513
|
+
function assignDynamicPayloadTypes(config) {
|
|
1514
|
+
for (const [i, codecParams] of (0, helper_1.enumerate)([
|
|
1515
|
+
...(config.codecs.audio || []),
|
|
1516
|
+
...(config.codecs.video || []),
|
|
1517
|
+
])) {
|
|
1518
|
+
if (codecParams.payloadType != undefined) {
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
codecParams.payloadType = 96 + i;
|
|
1522
|
+
switch (codecParams.name.toLowerCase()) {
|
|
1523
|
+
case "rtx":
|
|
1524
|
+
{
|
|
1525
|
+
codecParams.parameters = `apt=${codecParams.payloadType - 1}`;
|
|
1526
|
+
}
|
|
1527
|
+
break;
|
|
1528
|
+
case "red":
|
|
1529
|
+
{
|
|
1530
|
+
if (codecParams.contentType === "audio") {
|
|
1531
|
+
const redundant = codecParams.payloadType + 1;
|
|
1532
|
+
codecParams.parameters = `${redundant}/${redundant}`;
|
|
1533
|
+
codecParams.payloadType = 63;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
break;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1065
1540
|
function generateDefaultPeerConfig() {
|
|
1066
1541
|
return {
|
|
1067
|
-
codecs:
|
|
1068
|
-
audio: [(0, media_1.useOPUS)(), (0, media_1.usePCMU)()],
|
|
1069
|
-
video: [(0, media_1.useVP8)()],
|
|
1070
|
-
},
|
|
1542
|
+
codecs: (0, media_1.defaultCodecs)(),
|
|
1071
1543
|
headerExtensions: {
|
|
1072
1544
|
audio: [],
|
|
1073
1545
|
video: [],
|
|
@@ -1081,8 +1553,12 @@ function generateDefaultPeerConfig() {
|
|
|
1081
1553
|
iceUseIpv4: true,
|
|
1082
1554
|
iceUseIpv6: true,
|
|
1083
1555
|
iceUseTcp: false,
|
|
1556
|
+
iceTcpPassive: true,
|
|
1557
|
+
iceStunGatherTimeout: undefined,
|
|
1558
|
+
iceTurnConnectTimeout: undefined,
|
|
1084
1559
|
turnTransport: undefined,
|
|
1085
1560
|
turnTlsOptions: undefined,
|
|
1561
|
+
turnUdpFamily: undefined,
|
|
1086
1562
|
iceFilterStunResponse: undefined,
|
|
1087
1563
|
iceFilterCandidatePair: undefined,
|
|
1088
1564
|
icePasswordPrefix: undefined,
|
|
@@ -1096,12 +1572,18 @@ function generateDefaultPeerConfig() {
|
|
|
1096
1572
|
midSuffix: false,
|
|
1097
1573
|
forceTurnTCP: false,
|
|
1098
1574
|
maxMessageSize: sctp_1.DEFAULT_MAX_MESSAGE_SIZE,
|
|
1575
|
+
sctp: { mtu: src_1.DEFAULT_SCTP_MTU },
|
|
1576
|
+
pendingRtp: false,
|
|
1577
|
+
mLineReuse: "compatible",
|
|
1099
1578
|
};
|
|
1100
1579
|
}
|
|
1101
1580
|
exports.defaultPeerConfig = generateDefaultPeerConfig();
|
|
1102
1581
|
function normalizePeerConfiguration(config) {
|
|
1103
1582
|
const input = Object(config ?? {});
|
|
1104
1583
|
const normalizedConfig = { ...input };
|
|
1584
|
+
if (input.sctp !== undefined) {
|
|
1585
|
+
normalizedConfig.sctp = { ...input.sctp };
|
|
1586
|
+
}
|
|
1105
1587
|
if (input.bundlePolicy === "balanced") {
|
|
1106
1588
|
normalizedConfig.bundlePolicy = "max-compat";
|
|
1107
1589
|
}
|
|
@@ -1164,6 +1646,10 @@ function clonePeerConfiguration(config) {
|
|
|
1164
1646
|
dtls: { ...config.dtls },
|
|
1165
1647
|
certificates: [...config.certificates],
|
|
1166
1648
|
debug: { ...config.debug },
|
|
1649
|
+
sctp: { ...config.sctp },
|
|
1650
|
+
pendingRtp: typeof config.pendingRtp === "object" && config.pendingRtp != undefined
|
|
1651
|
+
? { ...config.pendingRtp }
|
|
1652
|
+
: config.pendingRtp,
|
|
1167
1653
|
};
|
|
1168
1654
|
}
|
|
1169
1655
|
class RTCTrackEvent {
|