werift 0.14.4-debug0 → 0.15.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/lib/common/src/binary.d.ts +0 -1
- package/lib/common/src/binary.js +1 -2
- package/lib/common/src/binary.js.map +1 -1
- package/lib/dtls/src/context/cipher.js +5 -2
- package/lib/dtls/src/context/cipher.js.map +1 -1
- package/lib/dtls/src/handshake/extensions/useSrtp.js +5 -2
- package/lib/dtls/src/handshake/extensions/useSrtp.js.map +1 -1
- package/lib/ice/src/candidate.js +5 -2
- package/lib/ice/src/candidate.js.map +1 -1
- package/lib/ice/src/ice.d.ts +2 -2
- package/lib/ice/src/ice.js +6 -5
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/stun/attributes.js +5 -2
- package/lib/ice/src/stun/attributes.js.map +1 -1
- package/lib/rtp/src/rtcp/rr.js +5 -2
- package/lib/rtp/src/rtcp/rr.js.map +1 -1
- package/lib/rtp/src/rtcp/rtpfb/nack.js +6 -3
- package/lib/rtp/src/rtcp/rtpfb/nack.js.map +1 -1
- package/lib/rtp/src/rtcp/rtpfb/twcc.js +6 -6
- package/lib/rtp/src/rtcp/rtpfb/twcc.js.map +1 -1
- package/lib/rtp/src/rtcp/sr.js +5 -2
- package/lib/rtp/src/rtcp/sr.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/ctr.js +5 -2
- package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/gcm.js +6 -3
- package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -1
- package/lib/sctp/src/param.js +5 -2
- package/lib/sctp/src/param.js.map +1 -1
- package/lib/sctp/src/sctp.js +5 -5
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/media/receiver/nack.js +2 -2
- package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +3 -2
- package/lib/webrtc/src/media/rtpReceiver.js +4 -2
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +4 -2
- package/lib/webrtc/src/media/rtpSender.js +15 -7
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +7 -6
- package/lib/webrtc/src/media/rtpTransceiver.js +8 -3
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +10 -10
- package/lib/webrtc/src/peerConnection.js +324 -188
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sdp.js +5 -2
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +3 -2
- package/lib/webrtc/src/transport/dtls.js +3 -0
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +5 -3
- package/lib/webrtc/src/transport/sctp.js +46 -34
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/package.json +2 -2
- package/src/media/receiver/nack.ts +1 -1
- package/src/media/rtpReceiver.ts +6 -5
- package/src/media/rtpSender.ts +18 -9
- package/src/media/rtpTransceiver.ts +13 -6
- package/src/nonstandard/recorder/writer/webm.ts +1 -1
- package/src/peerConnection.ts +386 -223
- package/src/sdp.ts +1 -1
- package/src/transport/dtls.ts +4 -1
- package/src/transport/sctp.ts +53 -33
package/src/peerConnection.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import
|
|
2
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
3
|
+
import isEqual from "lodash/isEqual";
|
|
3
4
|
import Event from "rx.mini";
|
|
4
5
|
import * as uuid from "uuid";
|
|
5
6
|
|
|
@@ -61,8 +62,6 @@ const log = debug("werift:packages/webrtc/src/peerConnection.ts");
|
|
|
61
62
|
|
|
62
63
|
export class RTCPeerConnection extends EventTarget {
|
|
63
64
|
readonly cname = uuid.v4();
|
|
64
|
-
iceTransport: RTCIceTransport;
|
|
65
|
-
dtlsTransport: RTCDtlsTransport;
|
|
66
65
|
sctpTransport?: RTCSctpTransport;
|
|
67
66
|
masterTransportEstablished = false;
|
|
68
67
|
configuration: Required<PeerConfig> =
|
|
@@ -73,17 +72,13 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
73
72
|
signalingState: RTCSignalingState = "stable";
|
|
74
73
|
negotiationneeded = false;
|
|
75
74
|
readonly transceivers: RTCRtpTransceiver[] = [];
|
|
75
|
+
|
|
76
76
|
readonly iceGatheringStateChange = new Event<[IceGathererState]>();
|
|
77
77
|
readonly iceConnectionStateChange = new Event<[RTCIceConnectionState]>();
|
|
78
78
|
readonly signalingStateChange = new Event<[RTCSignalingState]>();
|
|
79
79
|
readonly connectionStateChange = new Event<[ConnectionState]>();
|
|
80
80
|
readonly onDataChannel = new Event<[RTCDataChannel]>();
|
|
81
81
|
readonly onRemoteTransceiverAdded = new Event<[RTCRtpTransceiver]>();
|
|
82
|
-
/**
|
|
83
|
-
* should use onRemoteTransceiverAdded
|
|
84
|
-
* @deprecated
|
|
85
|
-
*/
|
|
86
|
-
readonly onTransceiver = new Event<[RTCRtpTransceiver]>();
|
|
87
82
|
readonly onTransceiverAdded = new Event<[RTCRtpTransceiver]>();
|
|
88
83
|
readonly onIceCandidate = new Event<[RTCIceCandidate]>();
|
|
89
84
|
readonly onNegotiationneeded = new Event<[]>();
|
|
@@ -106,6 +101,22 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
106
101
|
private isClosed = false;
|
|
107
102
|
private shouldNegotiationneeded = false;
|
|
108
103
|
|
|
104
|
+
get dtlsTransports() {
|
|
105
|
+
const transports = this.transceivers.map((t) => t.dtlsTransport);
|
|
106
|
+
if (this.sctpTransport) {
|
|
107
|
+
transports.push(this.sctpTransport.dtlsTransport);
|
|
108
|
+
}
|
|
109
|
+
return transports.reduce((acc: RTCDtlsTransport[], cur) => {
|
|
110
|
+
if (!acc.map((d) => d.id).includes(cur.id)) {
|
|
111
|
+
acc.push(cur);
|
|
112
|
+
}
|
|
113
|
+
return acc;
|
|
114
|
+
}, []);
|
|
115
|
+
}
|
|
116
|
+
get iceTransports() {
|
|
117
|
+
return this.dtlsTransports.map((d) => d.iceTransport);
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
constructor({
|
|
110
121
|
codecs,
|
|
111
122
|
headerExtensions,
|
|
@@ -113,6 +124,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
113
124
|
iceTransportPolicy,
|
|
114
125
|
icePortRange,
|
|
115
126
|
dtls,
|
|
127
|
+
bundlePolicy,
|
|
116
128
|
}: Partial<PeerConfig> = {}) {
|
|
117
129
|
super();
|
|
118
130
|
|
|
@@ -179,13 +191,9 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
179
191
|
break;
|
|
180
192
|
}
|
|
181
193
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
186
|
-
]);
|
|
187
|
-
this.iceTransport = iceTransport;
|
|
188
|
-
this.dtlsTransport = dtlsTransport;
|
|
194
|
+
if (bundlePolicy) {
|
|
195
|
+
this.configuration.bundlePolicy = bundlePolicy;
|
|
196
|
+
}
|
|
189
197
|
}
|
|
190
198
|
|
|
191
199
|
get localDescription() {
|
|
@@ -217,9 +225,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
217
225
|
}
|
|
218
226
|
|
|
219
227
|
async createOffer() {
|
|
220
|
-
|
|
221
|
-
await this.dtlsTransport.setupCertificate();
|
|
222
|
-
}
|
|
228
|
+
await this.ensureCerts();
|
|
223
229
|
|
|
224
230
|
this.transceivers.forEach((transceiver) => {
|
|
225
231
|
transceiver.codecs = this.configuration.codecs[transceiver.kind];
|
|
@@ -337,7 +343,8 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
337
343
|
protocol: settings.protocol,
|
|
338
344
|
});
|
|
339
345
|
|
|
340
|
-
|
|
346
|
+
const channel = new RTCDataChannel(this.sctpTransport, parameters);
|
|
347
|
+
return channel;
|
|
341
348
|
}
|
|
342
349
|
|
|
343
350
|
removeTrack(sender: RTCRtpSender) {
|
|
@@ -380,20 +387,31 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
380
387
|
};
|
|
381
388
|
|
|
382
389
|
private createTransport(srtpProfiles: Profile[] = []) {
|
|
390
|
+
const [existing] = this.iceTransports;
|
|
391
|
+
|
|
392
|
+
if (this.configuration.bundlePolicy === "max-bundle") {
|
|
393
|
+
if (existing) {
|
|
394
|
+
return this.dtlsTransports[0];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
383
398
|
const iceGatherer = new RTCIceGatherer({
|
|
384
399
|
...parseIceServers(this.configuration.iceServers),
|
|
385
400
|
forceTurn: this.configuration.iceTransportPolicy === "relay",
|
|
386
401
|
portRange: this.configuration.icePortRange,
|
|
387
402
|
});
|
|
388
|
-
|
|
389
|
-
|
|
403
|
+
if (existing) {
|
|
404
|
+
iceGatherer.connection.localUserName = existing.connection.localUserName;
|
|
405
|
+
iceGatherer.connection.localPassword = existing.connection.localPassword;
|
|
406
|
+
}
|
|
407
|
+
iceGatherer.onGatheringStateChange.subscribe(() => {
|
|
408
|
+
this.updateIceGatheringState();
|
|
390
409
|
});
|
|
391
|
-
this.updateIceGatheringState(
|
|
410
|
+
this.updateIceGatheringState();
|
|
392
411
|
const iceTransport = new RTCIceTransport(iceGatherer);
|
|
393
|
-
iceTransport.onStateChange.subscribe((
|
|
394
|
-
this.updateIceConnectionState(
|
|
412
|
+
iceTransport.onStateChange.subscribe(() => {
|
|
413
|
+
this.updateIceConnectionState();
|
|
395
414
|
});
|
|
396
|
-
this.updateIceConnectionState(iceTransport.state);
|
|
397
415
|
|
|
398
416
|
iceTransport.iceGather.onIceCandidate = (candidate) => {
|
|
399
417
|
if (!this.localDescription) return;
|
|
@@ -405,7 +423,6 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
405
423
|
}
|
|
406
424
|
candidate.sdpMLineIndex = 0;
|
|
407
425
|
candidate.sdpMid = media.rtp.muxId;
|
|
408
|
-
// for chrome & firefox & maybe others
|
|
409
426
|
candidate.foundation = "candidate:" + candidate.foundation;
|
|
410
427
|
|
|
411
428
|
this.onIceCandidate.execute(candidate.toJSON());
|
|
@@ -421,11 +438,16 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
421
438
|
srtpProfiles
|
|
422
439
|
);
|
|
423
440
|
|
|
424
|
-
return
|
|
441
|
+
return dtlsTransport;
|
|
425
442
|
}
|
|
426
443
|
|
|
427
444
|
private createSctpTransport() {
|
|
428
|
-
const
|
|
445
|
+
const dtlsTransport = this.createTransport([
|
|
446
|
+
SRTP_PROFILE.SRTP_AEAD_AES_128_GCM, // prefer
|
|
447
|
+
SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
448
|
+
]);
|
|
449
|
+
const sctp = new RTCSctpTransport();
|
|
450
|
+
sctp.setDtlsTransport(dtlsTransport);
|
|
429
451
|
sctp.mid = undefined;
|
|
430
452
|
|
|
431
453
|
sctp.onDataChannel.subscribe((channel) => {
|
|
@@ -436,6 +458,8 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
436
458
|
this.emit("datachannel", event);
|
|
437
459
|
});
|
|
438
460
|
|
|
461
|
+
this.updateIceConnectionState();
|
|
462
|
+
|
|
439
463
|
return sctp;
|
|
440
464
|
}
|
|
441
465
|
|
|
@@ -457,10 +481,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
457
481
|
|
|
458
482
|
// # assign MID
|
|
459
483
|
description.media.forEach((media, i) => {
|
|
460
|
-
const mid = media.rtp.muxId
|
|
461
|
-
if (!mid) {
|
|
462
|
-
throw new Error("mid not exist");
|
|
463
|
-
}
|
|
484
|
+
const mid = media.rtp.muxId!;
|
|
464
485
|
this.seenMid.add(mid);
|
|
465
486
|
if (["audio", "video"].includes(media.kind)) {
|
|
466
487
|
const transceiver = this.getTransceiverByMLineIndex(i);
|
|
@@ -473,26 +494,31 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
473
494
|
}
|
|
474
495
|
});
|
|
475
496
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
this.iceTransport.connection.iceControlling = true;
|
|
479
|
-
} else {
|
|
480
|
-
this.iceTransport.connection.iceControlling = false;
|
|
481
|
-
}
|
|
482
|
-
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
483
|
-
// RFC 8445 S6.1.1
|
|
484
|
-
if (this.iceTransport.connection.remoteIsLite) {
|
|
485
|
-
this.iceTransport.connection.iceControlling = true;
|
|
486
|
-
}
|
|
497
|
+
const setupRole = (dtlsTransport: RTCDtlsTransport) => {
|
|
498
|
+
const iceTransport = dtlsTransport.iceTransport;
|
|
487
499
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
this.dtlsTransport.role = role;
|
|
500
|
+
// # set ICE role
|
|
501
|
+
if (description.type === "offer") {
|
|
502
|
+
iceTransport.connection.iceControlling = true;
|
|
503
|
+
} else {
|
|
504
|
+
iceTransport.connection.iceControlling = false;
|
|
494
505
|
}
|
|
495
|
-
|
|
506
|
+
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
507
|
+
// RFC 8445 S6.1.1
|
|
508
|
+
if (iceTransport.connection.remoteIsLite) {
|
|
509
|
+
iceTransport.connection.iceControlling = true;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// # set DTLS role for mediasoup
|
|
513
|
+
if (description.type === "answer") {
|
|
514
|
+
const role = description.media.find((media) => media.dtlsParams)
|
|
515
|
+
?.dtlsParams?.role;
|
|
516
|
+
if (role) {
|
|
517
|
+
dtlsTransport.role = role;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
this.dtlsTransports.forEach((d) => setupRole(d));
|
|
496
522
|
|
|
497
523
|
// # configure direction
|
|
498
524
|
this.transceivers.forEach((t) => {
|
|
@@ -506,10 +532,18 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
506
532
|
this.setLocal(description);
|
|
507
533
|
|
|
508
534
|
// # gather candidates
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
535
|
+
for (const iceTransport of this.iceTransports) {
|
|
536
|
+
await iceTransport.iceGather.gather();
|
|
537
|
+
}
|
|
538
|
+
description.media
|
|
539
|
+
.filter((m) => ["audio", "video"].includes(m.kind))
|
|
540
|
+
.forEach((m, i) => {
|
|
541
|
+
addTransportDescription(m, this.transceivers[i].dtlsTransport);
|
|
542
|
+
});
|
|
543
|
+
const sctpMedia = description.media.find((m) => m.kind === "application");
|
|
544
|
+
if (this.sctpTransport && sctpMedia) {
|
|
545
|
+
addTransportDescription(sctpMedia, this.sctpTransport.dtlsTransport);
|
|
546
|
+
}
|
|
513
547
|
|
|
514
548
|
this.setLocal(description);
|
|
515
549
|
|
|
@@ -540,39 +574,41 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
540
574
|
|
|
541
575
|
async addIceCandidate(candidateMessage: RTCIceCandidate) {
|
|
542
576
|
const candidate = IceCandidate.fromJSON(candidateMessage);
|
|
543
|
-
|
|
577
|
+
for (const iceTransport of this.iceTransports) {
|
|
578
|
+
await iceTransport.addRemoteCandidate(candidate);
|
|
579
|
+
}
|
|
544
580
|
}
|
|
545
581
|
|
|
546
582
|
private async connect() {
|
|
547
583
|
if (this.masterTransportEstablished) return;
|
|
548
584
|
|
|
549
|
-
const dtlsTransport = this.dtlsTransport;
|
|
550
|
-
const iceTransport = dtlsTransport.iceTransport;
|
|
551
|
-
|
|
552
585
|
this.setConnectionState("connecting");
|
|
553
586
|
|
|
554
|
-
await
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
587
|
+
await Promise.all(
|
|
588
|
+
this.dtlsTransports.map(async (dtlsTransport) => {
|
|
589
|
+
const { iceTransport } = dtlsTransport;
|
|
590
|
+
await iceTransport.start().catch((err) => {
|
|
591
|
+
log("iceTransport.start failed", err);
|
|
592
|
+
throw err;
|
|
593
|
+
});
|
|
594
|
+
await dtlsTransport.start().catch((err) => {
|
|
595
|
+
log("dtlsTransport.start failed", err);
|
|
596
|
+
throw err;
|
|
597
|
+
});
|
|
598
|
+
})
|
|
599
|
+
);
|
|
564
600
|
|
|
565
601
|
if (this.sctpTransport && this.sctpRemotePort) {
|
|
566
602
|
await this.sctpTransport.start(this.sctpRemotePort);
|
|
567
603
|
await this.sctpTransport.sctp.stateChanged.connected.asPromise();
|
|
604
|
+
log("sctp connected");
|
|
568
605
|
}
|
|
569
|
-
log("sctp connected");
|
|
570
606
|
|
|
571
607
|
this.masterTransportEstablished = true;
|
|
572
608
|
this.setConnectionState("connected");
|
|
573
609
|
}
|
|
574
610
|
|
|
575
|
-
private
|
|
611
|
+
private getLocalRtpParams(transceiver: RTCRtpTransceiver): RTCRtpParameters {
|
|
576
612
|
if (transceiver.mid == undefined) throw new Error("mid not assigned");
|
|
577
613
|
|
|
578
614
|
const rtp: RTCRtpParameters = {
|
|
@@ -584,15 +620,10 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
584
620
|
return rtp;
|
|
585
621
|
}
|
|
586
622
|
|
|
587
|
-
private
|
|
588
|
-
|
|
623
|
+
private getRemoteRtpParams(
|
|
624
|
+
media: MediaDescription,
|
|
589
625
|
transceiver: RTCRtpTransceiver
|
|
590
626
|
): RTCRtpReceiveParameters {
|
|
591
|
-
if (transceiver.mLineIndex == undefined)
|
|
592
|
-
throw new Error("mLineIndex not assigned");
|
|
593
|
-
const media = remoteDescription.media[transceiver.mLineIndex];
|
|
594
|
-
if (!media) throw new Error("media line not exist");
|
|
595
|
-
|
|
596
627
|
const receiveParameters: RTCRtpReceiveParameters = {
|
|
597
628
|
muxId: media.rtp.muxId,
|
|
598
629
|
rtcp: media.rtp.rtcp,
|
|
@@ -632,152 +663,83 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
632
663
|
remoteSdp.type = sessionDescription.type;
|
|
633
664
|
this.validateDescription(remoteSdp, false);
|
|
634
665
|
|
|
666
|
+
const bundle = remoteSdp.group.find((g) => g.semantic === "BUNDLE");
|
|
667
|
+
let bundleTransport: RTCDtlsTransport | undefined;
|
|
668
|
+
|
|
635
669
|
// # apply description
|
|
636
670
|
for (const [i, remoteMedia] of enumerate(remoteSdp.media)) {
|
|
637
|
-
|
|
638
|
-
const transceiver =
|
|
639
|
-
this.transceivers.find(
|
|
640
|
-
(t) =>
|
|
641
|
-
t.kind === remoteMedia.kind &&
|
|
642
|
-
[undefined, remoteMedia.rtp.muxId].includes(t.mid)
|
|
643
|
-
) ||
|
|
644
|
-
(() => {
|
|
645
|
-
// create remote transceiver
|
|
646
|
-
const transceiver = this.addTransceiver(remoteMedia.kind, {
|
|
647
|
-
direction: "recvonly",
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
651
|
-
this.onTransceiver.execute(transceiver);
|
|
671
|
+
let dtlsTransport: RTCDtlsTransport | undefined;
|
|
652
672
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
transceiver.mLineIndex = i;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// # negotiate codecs
|
|
662
|
-
transceiver.codecs = remoteMedia.rtp.codecs.filter((remoteCodec) => {
|
|
663
|
-
const localCodecs = this.configuration.codecs[remoteMedia.kind] || [];
|
|
664
|
-
|
|
665
|
-
const existCodec = findCodecByMimeType(localCodecs, remoteCodec);
|
|
666
|
-
if (!existCodec) return false;
|
|
667
|
-
|
|
668
|
-
if (existCodec?.name.toLowerCase() === "rtx") {
|
|
669
|
-
const params = codecParametersFromString(
|
|
670
|
-
existCodec.parameters ?? ""
|
|
671
|
-
);
|
|
672
|
-
const pt = params["apt"];
|
|
673
|
-
const origin = remoteMedia.rtp.codecs.find(
|
|
674
|
-
(c) => c.payloadType === pt
|
|
675
|
-
);
|
|
676
|
-
if (!origin) return false;
|
|
677
|
-
return !!findCodecByMimeType(localCodecs, origin);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
return true;
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
log("negotiated codecs", transceiver.codecs);
|
|
684
|
-
if (transceiver.codecs.length === 0) {
|
|
685
|
-
throw new Error("negotiate codecs failed.");
|
|
686
|
-
}
|
|
687
|
-
transceiver.headerExtensions = remoteMedia.rtp.headerExtensions.filter(
|
|
688
|
-
(extension) =>
|
|
689
|
-
(
|
|
690
|
-
this.configuration.headerExtensions[
|
|
691
|
-
remoteMedia.kind as "video" | "audio"
|
|
692
|
-
] || []
|
|
693
|
-
).find((v) => v.uri === extension.uri)
|
|
673
|
+
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
674
|
+
let transceiver = this.transceivers.find(
|
|
675
|
+
(t) =>
|
|
676
|
+
t.kind === remoteMedia.kind &&
|
|
677
|
+
[undefined, remoteMedia.rtp.muxId].includes(t.mid)
|
|
694
678
|
);
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (["answer", "pranswer"].includes(remoteSdp.type)) {
|
|
700
|
-
transceiver.currentDirection = direction;
|
|
701
|
-
} else {
|
|
702
|
-
transceiver.offerDirection = direction;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
const localParams = this.localRtp(transceiver);
|
|
706
|
-
transceiver.sender.prepareSend(localParams);
|
|
707
|
-
|
|
708
|
-
if (["recvonly", "sendrecv"].includes(transceiver.direction)) {
|
|
709
|
-
const remotePrams = this.remoteRtp(remoteSdp, transceiver);
|
|
710
|
-
|
|
711
|
-
// register simulcast receiver
|
|
712
|
-
remoteMedia.simulcastParameters.forEach((param) => {
|
|
713
|
-
this.router.registerRtpReceiverByRid(
|
|
714
|
-
transceiver,
|
|
715
|
-
param,
|
|
716
|
-
remotePrams
|
|
717
|
-
);
|
|
679
|
+
if (!transceiver) {
|
|
680
|
+
// create remote transceiver
|
|
681
|
+
transceiver = this.addTransceiver(remoteMedia.kind, {
|
|
682
|
+
direction: "recvonly",
|
|
718
683
|
});
|
|
719
|
-
|
|
720
|
-
transceiver.receiver.prepareReceive(remotePrams);
|
|
721
|
-
// register ssrc receiver
|
|
722
|
-
this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
|
|
684
|
+
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
723
685
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
transceiver.
|
|
730
|
-
|
|
731
|
-
this.fireOnTrack(
|
|
732
|
-
transceiver.receiver.track,
|
|
733
|
-
transceiver,
|
|
734
|
-
new MediaStream({
|
|
735
|
-
id: streamId,
|
|
736
|
-
tracks: [transceiver.receiver.track],
|
|
737
|
-
})
|
|
738
|
-
);
|
|
686
|
+
|
|
687
|
+
if (bundle) {
|
|
688
|
+
if (!bundleTransport) {
|
|
689
|
+
bundleTransport = transceiver.dtlsTransport;
|
|
690
|
+
} else {
|
|
691
|
+
transceiver.setDtlsTransport(bundleTransport);
|
|
739
692
|
}
|
|
740
693
|
}
|
|
741
694
|
|
|
742
|
-
transceiver.
|
|
695
|
+
dtlsTransport = transceiver.dtlsTransport;
|
|
696
|
+
|
|
697
|
+
this.setRemoteRTP(transceiver, remoteMedia, remoteSdp.type, i);
|
|
743
698
|
} else if (remoteMedia.kind === "application") {
|
|
744
|
-
// # configure sctp
|
|
745
|
-
this.sctpRemotePort = remoteMedia.sctpPort;
|
|
746
|
-
if (!this.sctpRemotePort) {
|
|
747
|
-
throw new Error("sctpRemotePort not exist");
|
|
748
|
-
}
|
|
749
699
|
if (!this.sctpTransport) {
|
|
750
700
|
this.sctpTransport = this.createSctpTransport();
|
|
751
701
|
}
|
|
752
|
-
|
|
753
|
-
if (
|
|
754
|
-
|
|
702
|
+
|
|
703
|
+
if (bundle) {
|
|
704
|
+
if (!bundleTransport) {
|
|
705
|
+
bundleTransport = this.sctpTransport.dtlsTransport;
|
|
706
|
+
} else {
|
|
707
|
+
this.sctpTransport.setDtlsTransport(bundleTransport);
|
|
708
|
+
}
|
|
755
709
|
}
|
|
710
|
+
|
|
711
|
+
dtlsTransport = this.sctpTransport.dtlsTransport;
|
|
712
|
+
|
|
713
|
+
this.setRemoteSCTP(remoteMedia, this.sctpTransport);
|
|
714
|
+
} else {
|
|
715
|
+
throw new Error("invalid media kind");
|
|
756
716
|
}
|
|
757
717
|
|
|
718
|
+
const iceTransport = dtlsTransport.iceTransport;
|
|
719
|
+
|
|
758
720
|
if (remoteMedia.iceParams && remoteMedia.dtlsParams) {
|
|
759
|
-
|
|
760
|
-
|
|
721
|
+
iceTransport.setRemoteParams(remoteMedia.iceParams);
|
|
722
|
+
dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
|
|
761
723
|
|
|
762
724
|
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
763
725
|
// RFC 8445 S6.1.1
|
|
764
726
|
if (remoteMedia.iceParams?.iceLite) {
|
|
765
|
-
|
|
727
|
+
iceTransport.connection.iceControlling = true;
|
|
766
728
|
}
|
|
767
729
|
}
|
|
768
730
|
|
|
769
731
|
// # add ICE candidates
|
|
770
|
-
remoteMedia.iceCandidates.forEach(
|
|
732
|
+
remoteMedia.iceCandidates.forEach(iceTransport.addRemoteCandidate);
|
|
771
733
|
|
|
772
|
-
await
|
|
734
|
+
await iceTransport.iceGather.gather();
|
|
773
735
|
|
|
774
736
|
if (remoteMedia.iceCandidatesComplete) {
|
|
775
|
-
await
|
|
737
|
+
await iceTransport.addRemoteCandidate(undefined);
|
|
776
738
|
}
|
|
777
739
|
|
|
778
740
|
// # set DTLS role
|
|
779
741
|
if (remoteSdp.type === "answer" && remoteMedia.dtlsParams?.role) {
|
|
780
|
-
|
|
742
|
+
dtlsTransport.role =
|
|
781
743
|
remoteMedia.dtlsParams.role === "client" ? "server" : "client";
|
|
782
744
|
}
|
|
783
745
|
}
|
|
@@ -810,6 +772,109 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
810
772
|
}
|
|
811
773
|
}
|
|
812
774
|
|
|
775
|
+
private setRemoteRTP(
|
|
776
|
+
transceiver: RTCRtpTransceiver,
|
|
777
|
+
remoteMedia: MediaDescription,
|
|
778
|
+
type: "offer" | "answer",
|
|
779
|
+
mLineIndex: number
|
|
780
|
+
) {
|
|
781
|
+
if (!transceiver.mid) {
|
|
782
|
+
transceiver.mid = remoteMedia.rtp.muxId;
|
|
783
|
+
transceiver.mLineIndex = mLineIndex;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// # negotiate codecs
|
|
787
|
+
transceiver.codecs = remoteMedia.rtp.codecs.filter((remoteCodec) => {
|
|
788
|
+
const localCodecs = this.configuration.codecs[remoteMedia.kind] || [];
|
|
789
|
+
|
|
790
|
+
const existCodec = findCodecByMimeType(localCodecs, remoteCodec);
|
|
791
|
+
if (!existCodec) return false;
|
|
792
|
+
|
|
793
|
+
if (existCodec?.name.toLowerCase() === "rtx") {
|
|
794
|
+
const params = codecParametersFromString(existCodec.parameters ?? "");
|
|
795
|
+
const pt = params["apt"];
|
|
796
|
+
const origin = remoteMedia.rtp.codecs.find((c) => c.payloadType === pt);
|
|
797
|
+
if (!origin) return false;
|
|
798
|
+
return !!findCodecByMimeType(localCodecs, origin);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return true;
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
log("negotiated codecs", transceiver.codecs);
|
|
805
|
+
if (transceiver.codecs.length === 0) {
|
|
806
|
+
throw new Error("negotiate codecs failed.");
|
|
807
|
+
}
|
|
808
|
+
transceiver.headerExtensions = remoteMedia.rtp.headerExtensions.filter(
|
|
809
|
+
(extension) =>
|
|
810
|
+
(
|
|
811
|
+
this.configuration.headerExtensions[
|
|
812
|
+
remoteMedia.kind as "video" | "audio"
|
|
813
|
+
] || []
|
|
814
|
+
).find((v) => v.uri === extension.uri)
|
|
815
|
+
);
|
|
816
|
+
|
|
817
|
+
// # configure direction
|
|
818
|
+
const mediaDirection = remoteMedia.direction || "inactive";
|
|
819
|
+
const direction = reverseDirection(mediaDirection);
|
|
820
|
+
if (["answer", "pranswer"].includes(type)) {
|
|
821
|
+
transceiver.currentDirection = direction;
|
|
822
|
+
} else {
|
|
823
|
+
transceiver.offerDirection = direction;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const localParams = this.getLocalRtpParams(transceiver);
|
|
827
|
+
transceiver.sender.prepareSend(localParams);
|
|
828
|
+
|
|
829
|
+
if (["recvonly", "sendrecv"].includes(transceiver.direction)) {
|
|
830
|
+
const remotePrams = this.getRemoteRtpParams(remoteMedia, transceiver);
|
|
831
|
+
|
|
832
|
+
// register simulcast receiver
|
|
833
|
+
remoteMedia.simulcastParameters.forEach((param) => {
|
|
834
|
+
this.router.registerRtpReceiverByRid(transceiver, param, remotePrams);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
transceiver.receiver.prepareReceive(remotePrams);
|
|
838
|
+
// register ssrc receiver
|
|
839
|
+
this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
|
|
840
|
+
}
|
|
841
|
+
if (["sendonly", "sendrecv"].includes(mediaDirection)) {
|
|
842
|
+
// assign msid
|
|
843
|
+
if (remoteMedia.msid != undefined) {
|
|
844
|
+
const [streamId, trackId] = remoteMedia.msid.split(" ");
|
|
845
|
+
transceiver.receiver.remoteStreamId = streamId;
|
|
846
|
+
transceiver.receiver.remoteTrackId = trackId;
|
|
847
|
+
|
|
848
|
+
this.fireOnTrack(
|
|
849
|
+
transceiver.receiver.track,
|
|
850
|
+
transceiver,
|
|
851
|
+
new MediaStream({
|
|
852
|
+
id: streamId,
|
|
853
|
+
tracks: [transceiver.receiver.track],
|
|
854
|
+
})
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
transceiver.receiver.setupTWCC(remoteMedia.ssrc[0]?.ssrc);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
private setRemoteSCTP(
|
|
863
|
+
remoteMedia: MediaDescription,
|
|
864
|
+
sctpTransport: RTCSctpTransport
|
|
865
|
+
) {
|
|
866
|
+
// # configure sctp
|
|
867
|
+
this.sctpRemotePort = remoteMedia.sctpPort;
|
|
868
|
+
if (!this.sctpRemotePort) {
|
|
869
|
+
throw new Error("sctpRemotePort not exist");
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
sctpTransport.setRemotePort(this.sctpRemotePort);
|
|
873
|
+
if (!sctpTransport.mid) {
|
|
874
|
+
sctpTransport.mid = remoteMedia.rtp.muxId;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
813
878
|
private validateDescription(
|
|
814
879
|
description: SessionDescription,
|
|
815
880
|
isLocal: boolean
|
|
@@ -888,14 +953,19 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
888
953
|
|
|
889
954
|
const direction = options.direction || "sendrecv";
|
|
890
955
|
|
|
891
|
-
const
|
|
892
|
-
|
|
956
|
+
const dtlsTransport = this.createTransport([
|
|
957
|
+
SRTP_PROFILE.SRTP_AEAD_AES_128_GCM, // prefer
|
|
958
|
+
SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
959
|
+
]);
|
|
960
|
+
|
|
961
|
+
const sender = new RTCRtpSender(trackOrKind);
|
|
962
|
+
const receiver = new RTCRtpReceiver(kind, sender.ssrc);
|
|
893
963
|
const transceiver = new RTCRtpTransceiver(
|
|
894
964
|
kind,
|
|
965
|
+
dtlsTransport,
|
|
895
966
|
receiver,
|
|
896
967
|
sender,
|
|
897
|
-
direction
|
|
898
|
-
this.dtlsTransport
|
|
968
|
+
direction
|
|
899
969
|
);
|
|
900
970
|
transceiver.options = options;
|
|
901
971
|
this.router.registerRtpSender(transceiver.sender);
|
|
@@ -903,6 +973,7 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
903
973
|
this.transceivers.push(transceiver);
|
|
904
974
|
this.onTransceiverAdded.execute(transceiver);
|
|
905
975
|
|
|
976
|
+
this.updateIceConnectionState();
|
|
906
977
|
this.needNegotiation();
|
|
907
978
|
|
|
908
979
|
return transceiver;
|
|
@@ -971,58 +1042,81 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
971
1042
|
}
|
|
972
1043
|
}
|
|
973
1044
|
|
|
1045
|
+
private async ensureCerts() {
|
|
1046
|
+
const ensureCert = async (dtlsTransport: RTCDtlsTransport) => {
|
|
1047
|
+
if (this.certificates.length === 0) {
|
|
1048
|
+
const localCertificate = await dtlsTransport.setupCertificate();
|
|
1049
|
+
this.certificates.push(localCertificate);
|
|
1050
|
+
} else {
|
|
1051
|
+
dtlsTransport.localCertificate = this.certificates[0];
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
for (const dtlsTransport of this.dtlsTransports) {
|
|
1056
|
+
await ensureCert(dtlsTransport);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
974
1060
|
async createAnswer() {
|
|
975
1061
|
this.assertNotClosed();
|
|
976
1062
|
if (
|
|
977
1063
|
!["have-remote-offer", "have-local-pranswer"].includes(
|
|
978
1064
|
this.signalingState
|
|
979
|
-
)
|
|
980
|
-
|
|
981
|
-
)
|
|
1065
|
+
)
|
|
1066
|
+
) {
|
|
982
1067
|
throw new Error("createAnswer failed");
|
|
983
|
-
|
|
984
|
-
if (this.
|
|
985
|
-
|
|
1068
|
+
}
|
|
1069
|
+
if (!this._remoteDescription) {
|
|
1070
|
+
throw new Error("wrong state");
|
|
986
1071
|
}
|
|
987
1072
|
|
|
1073
|
+
await this.ensureCerts();
|
|
1074
|
+
|
|
988
1075
|
const description = new SessionDescription();
|
|
989
1076
|
addSDPHeader("answer", description);
|
|
990
1077
|
|
|
991
|
-
this._remoteDescription
|
|
1078
|
+
this._remoteDescription.media.forEach((remoteMedia) => {
|
|
992
1079
|
let dtlsTransport!: RTCDtlsTransport;
|
|
993
1080
|
let media: MediaDescription;
|
|
994
1081
|
|
|
995
|
-
if (["audio", "video"].includes(
|
|
996
|
-
const transceiver = this.getTransceiverByMid(
|
|
1082
|
+
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
1083
|
+
const transceiver = this.getTransceiverByMid(remoteMedia.rtp.muxId!)!;
|
|
997
1084
|
media = createMediaDescriptionForTransceiver(
|
|
998
1085
|
transceiver,
|
|
999
1086
|
this.cname,
|
|
1000
1087
|
andDirection(transceiver.direction, transceiver.offerDirection),
|
|
1001
1088
|
transceiver.mid!
|
|
1002
1089
|
);
|
|
1003
|
-
if (!transceiver.dtlsTransport) throw new Error();
|
|
1004
1090
|
dtlsTransport = transceiver.dtlsTransport;
|
|
1005
|
-
} else if (
|
|
1006
|
-
if (!this.sctpTransport || !this.sctpTransport.mid)
|
|
1091
|
+
} else if (remoteMedia.kind === "application") {
|
|
1092
|
+
if (!this.sctpTransport || !this.sctpTransport.mid) {
|
|
1093
|
+
throw new Error("sctpTransport not found");
|
|
1094
|
+
}
|
|
1007
1095
|
media = createMediaDescriptionForSctp(
|
|
1008
1096
|
this.sctpTransport,
|
|
1009
1097
|
this.sctpTransport.mid
|
|
1010
1098
|
);
|
|
1011
1099
|
|
|
1012
1100
|
dtlsTransport = this.sctpTransport.dtlsTransport;
|
|
1013
|
-
} else
|
|
1101
|
+
} else {
|
|
1102
|
+
throw new Error("invalid kind");
|
|
1103
|
+
}
|
|
1014
1104
|
|
|
1015
1105
|
// # determine DTLS role, or preserve the currently configured role
|
|
1016
|
-
if (!media.dtlsParams)
|
|
1106
|
+
if (!media.dtlsParams) {
|
|
1107
|
+
throw new Error("dtlsParams missing");
|
|
1108
|
+
}
|
|
1017
1109
|
if (dtlsTransport.role === "auto") {
|
|
1018
1110
|
media.dtlsParams.role = "client";
|
|
1019
1111
|
} else {
|
|
1020
1112
|
media.dtlsParams.role = dtlsTransport.role;
|
|
1021
1113
|
}
|
|
1022
|
-
|
|
1114
|
+
|
|
1115
|
+
media.simulcastParameters = remoteMedia.simulcastParameters.map((v) => ({
|
|
1023
1116
|
...v,
|
|
1024
1117
|
direction: reverseSimulcastDirection(v.direction),
|
|
1025
1118
|
}));
|
|
1119
|
+
|
|
1026
1120
|
description.media.push(media);
|
|
1027
1121
|
});
|
|
1028
1122
|
|
|
@@ -1050,29 +1144,94 @@ export class RTCPeerConnection extends EventTarget {
|
|
|
1050
1144
|
if (this.sctpTransport) {
|
|
1051
1145
|
await this.sctpTransport.stop();
|
|
1052
1146
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1147
|
+
for (const dtlsTransport of this.dtlsTransports) {
|
|
1148
|
+
await dtlsTransport.stop();
|
|
1149
|
+
await dtlsTransport.iceTransport.stop();
|
|
1150
|
+
}
|
|
1055
1151
|
|
|
1056
1152
|
this.dispose();
|
|
1057
1153
|
log("peerConnection closed");
|
|
1058
1154
|
}
|
|
1059
1155
|
|
|
1060
1156
|
private assertNotClosed() {
|
|
1061
|
-
if (this.isClosed)
|
|
1157
|
+
if (this.isClosed) {
|
|
1158
|
+
throw new Error("RTCPeerConnection is closed");
|
|
1159
|
+
}
|
|
1062
1160
|
}
|
|
1063
1161
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1162
|
+
// https://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate
|
|
1163
|
+
private updateIceGatheringState() {
|
|
1164
|
+
const all = this.iceTransports;
|
|
1165
|
+
|
|
1166
|
+
function allMatch(...state: IceGathererState[]) {
|
|
1167
|
+
return (
|
|
1168
|
+
all.filter((check) => state.includes(check.iceGather.gatheringState))
|
|
1169
|
+
.length === all.length
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
let newState: IceGathererState;
|
|
1174
|
+
|
|
1175
|
+
if (all.length && allMatch("complete")) {
|
|
1176
|
+
newState = "complete";
|
|
1177
|
+
} else if (!all.length || allMatch("new", "complete")) {
|
|
1178
|
+
newState = "new";
|
|
1179
|
+
} else if (
|
|
1180
|
+
all.map((check) => check.iceGather.gatheringState).includes("gathering")
|
|
1181
|
+
) {
|
|
1182
|
+
newState = "gathering";
|
|
1183
|
+
} else {
|
|
1184
|
+
newState = "new";
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (this.iceGatheringState === newState) {
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
log("iceGatheringStateChange", newState);
|
|
1192
|
+
this.iceGatheringState = newState;
|
|
1193
|
+
this.iceGatheringStateChange.execute(newState);
|
|
1194
|
+
this.emit("icegatheringstatechange", newState);
|
|
1069
1195
|
}
|
|
1070
1196
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1197
|
+
// https://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate
|
|
1198
|
+
private updateIceConnectionState() {
|
|
1199
|
+
const all = this.iceTransports;
|
|
1200
|
+
let newState: RTCIceConnectionState;
|
|
1201
|
+
|
|
1202
|
+
function allMatch(...state: RTCIceConnectionState[]) {
|
|
1203
|
+
return (
|
|
1204
|
+
all.filter((check) => state.includes(check.state)).length === all.length
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (this.connectionState === "closed") {
|
|
1209
|
+
newState = "closed";
|
|
1210
|
+
} else if (allMatch("failed")) {
|
|
1211
|
+
newState = "failed";
|
|
1212
|
+
} else if (allMatch("disconnected")) {
|
|
1213
|
+
newState = "disconnected";
|
|
1214
|
+
} else if (allMatch("new", "closed")) {
|
|
1215
|
+
newState = "new";
|
|
1216
|
+
} else if (allMatch("new", "checking")) {
|
|
1217
|
+
newState = "checking";
|
|
1218
|
+
} else if (allMatch("completed", "closed")) {
|
|
1219
|
+
newState = "completed";
|
|
1220
|
+
} else if (allMatch("connected", "completed", "closed")) {
|
|
1221
|
+
newState = "connected";
|
|
1222
|
+
} else {
|
|
1223
|
+
// unreachable?
|
|
1224
|
+
newState = "new";
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
if (this.iceConnectionState === newState) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
log("iceConnectionStateChange", newState);
|
|
1232
|
+
this.iceConnectionState = newState;
|
|
1233
|
+
this.iceConnectionStateChange.execute(newState);
|
|
1234
|
+
this.emit("iceconnectionstatechange", newState);
|
|
1076
1235
|
}
|
|
1077
1236
|
|
|
1078
1237
|
private setSignalingState(state: RTCSignalingState) {
|
|
@@ -1209,6 +1368,8 @@ export function allocateMid(mids: Set<string>) {
|
|
|
1209
1368
|
return mid;
|
|
1210
1369
|
}
|
|
1211
1370
|
|
|
1371
|
+
export type BundlePolicy = "max-compat" | "max-bundle";
|
|
1372
|
+
|
|
1212
1373
|
export interface PeerConfig {
|
|
1213
1374
|
codecs: Partial<{
|
|
1214
1375
|
audio: RTCRtpCodecParameters[];
|
|
@@ -1225,6 +1386,7 @@ export interface PeerConfig {
|
|
|
1225
1386
|
dtls: Partial<{
|
|
1226
1387
|
keys: DtlsKeys;
|
|
1227
1388
|
}>;
|
|
1389
|
+
bundlePolicy: BundlePolicy;
|
|
1228
1390
|
}
|
|
1229
1391
|
|
|
1230
1392
|
export const findCodecByMimeType = (
|
|
@@ -1276,6 +1438,7 @@ export const defaultPeerConfig: PeerConfig = {
|
|
|
1276
1438
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
1277
1439
|
icePortRange: undefined,
|
|
1278
1440
|
dtls: {},
|
|
1441
|
+
bundlePolicy: "max-compat",
|
|
1279
1442
|
};
|
|
1280
1443
|
|
|
1281
1444
|
export interface RTCTrackEvent {
|