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
|
@@ -24,7 +24,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.defaultPeerConfig = exports.findCodecByMimeType = exports.allocateMid = exports.addTransportDescription = exports.createMediaDescriptionForSctp = exports.createMediaDescriptionForTransceiver = exports.RTCPeerConnection = void 0;
|
|
26
26
|
const debug_1 = __importDefault(require("debug"));
|
|
27
|
-
const
|
|
27
|
+
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
|
|
28
|
+
const isEqual_1 = __importDefault(require("lodash/isEqual"));
|
|
28
29
|
const rx_mini_1 = __importDefault(require("rx.mini"));
|
|
29
30
|
const uuid = __importStar(require("uuid"));
|
|
30
31
|
const _1 = require(".");
|
|
@@ -44,11 +45,11 @@ const sctp_1 = require("./transport/sctp");
|
|
|
44
45
|
const utils_1 = require("./utils");
|
|
45
46
|
const log = (0, debug_1.default)("werift:packages/webrtc/src/peerConnection.ts");
|
|
46
47
|
class RTCPeerConnection extends helper_1.EventTarget {
|
|
47
|
-
constructor({ codecs, headerExtensions, iceServers, iceTransportPolicy, icePortRange, dtls, } = {}) {
|
|
48
|
+
constructor({ codecs, headerExtensions, iceServers, iceTransportPolicy, icePortRange, dtls, bundlePolicy, } = {}) {
|
|
48
49
|
super();
|
|
49
50
|
this.cname = uuid.v4();
|
|
50
51
|
this.masterTransportEstablished = false;
|
|
51
|
-
this.configuration = (0,
|
|
52
|
+
this.configuration = (0, cloneDeep_1.default)(exports.defaultPeerConfig);
|
|
52
53
|
this.connectionState = "new";
|
|
53
54
|
this.iceConnectionState = "new";
|
|
54
55
|
this.iceGatheringState = "new";
|
|
@@ -61,11 +62,6 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
61
62
|
this.connectionStateChange = new rx_mini_1.default();
|
|
62
63
|
this.onDataChannel = new rx_mini_1.default();
|
|
63
64
|
this.onRemoteTransceiverAdded = new rx_mini_1.default();
|
|
64
|
-
/**
|
|
65
|
-
* should use onRemoteTransceiverAdded
|
|
66
|
-
* @deprecated
|
|
67
|
-
*/
|
|
68
|
-
this.onTransceiver = new rx_mini_1.default();
|
|
69
65
|
this.onTransceiverAdded = new rx_mini_1.default();
|
|
70
66
|
this.onIceCandidate = new rx_mini_1.default();
|
|
71
67
|
this.onNegotiationneeded = new rx_mini_1.default();
|
|
@@ -148,12 +144,24 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
148
144
|
break;
|
|
149
145
|
}
|
|
150
146
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
this.
|
|
147
|
+
if (bundlePolicy) {
|
|
148
|
+
this.configuration.bundlePolicy = bundlePolicy;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
get dtlsTransports() {
|
|
152
|
+
const transports = this.transceivers.map((t) => t.dtlsTransport);
|
|
153
|
+
if (this.sctpTransport) {
|
|
154
|
+
transports.push(this.sctpTransport.dtlsTransport);
|
|
155
|
+
}
|
|
156
|
+
return transports.reduce((acc, cur) => {
|
|
157
|
+
if (!acc.map((d) => d.id).includes(cur.id)) {
|
|
158
|
+
acc.push(cur);
|
|
159
|
+
}
|
|
160
|
+
return acc;
|
|
161
|
+
}, []);
|
|
162
|
+
}
|
|
163
|
+
get iceTransports() {
|
|
164
|
+
return this.dtlsTransports.map((d) => d.iceTransport);
|
|
157
165
|
}
|
|
158
166
|
get localDescription() {
|
|
159
167
|
if (!this._localDescription)
|
|
@@ -178,9 +186,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
178
186
|
return this.transceivers.find((transceiver) => transceiver.mLineIndex === index);
|
|
179
187
|
}
|
|
180
188
|
async createOffer() {
|
|
181
|
-
|
|
182
|
-
await this.dtlsTransport.setupCertificate();
|
|
183
|
-
}
|
|
189
|
+
await this.ensureCerts();
|
|
184
190
|
this.transceivers.forEach((transceiver) => {
|
|
185
191
|
transceiver.codecs = this.configuration.codecs[transceiver.kind];
|
|
186
192
|
transceiver.headerExtensions =
|
|
@@ -252,7 +258,8 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
252
258
|
ordered: settings.ordered,
|
|
253
259
|
protocol: settings.protocol,
|
|
254
260
|
});
|
|
255
|
-
|
|
261
|
+
const channel = new dataChannel_1.RTCDataChannel(this.sctpTransport, parameters);
|
|
262
|
+
return channel;
|
|
256
263
|
}
|
|
257
264
|
removeTrack(sender) {
|
|
258
265
|
if (this.isClosed)
|
|
@@ -277,20 +284,29 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
277
284
|
this.needNegotiation();
|
|
278
285
|
}
|
|
279
286
|
createTransport(srtpProfiles = []) {
|
|
287
|
+
const [existing] = this.iceTransports;
|
|
288
|
+
if (this.configuration.bundlePolicy === "max-bundle") {
|
|
289
|
+
if (existing) {
|
|
290
|
+
return this.dtlsTransports[0];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
280
293
|
const iceGatherer = new ice_1.RTCIceGatherer({
|
|
281
294
|
...(0, utils_1.parseIceServers)(this.configuration.iceServers),
|
|
282
295
|
forceTurn: this.configuration.iceTransportPolicy === "relay",
|
|
283
296
|
portRange: this.configuration.icePortRange,
|
|
284
297
|
});
|
|
285
|
-
|
|
286
|
-
|
|
298
|
+
if (existing) {
|
|
299
|
+
iceGatherer.connection.localUserName = existing.connection.localUserName;
|
|
300
|
+
iceGatherer.connection.localPassword = existing.connection.localPassword;
|
|
301
|
+
}
|
|
302
|
+
iceGatherer.onGatheringStateChange.subscribe(() => {
|
|
303
|
+
this.updateIceGatheringState();
|
|
287
304
|
});
|
|
288
|
-
this.updateIceGatheringState(
|
|
305
|
+
this.updateIceGatheringState();
|
|
289
306
|
const iceTransport = new ice_1.RTCIceTransport(iceGatherer);
|
|
290
|
-
iceTransport.onStateChange.subscribe((
|
|
291
|
-
this.updateIceConnectionState(
|
|
307
|
+
iceTransport.onStateChange.subscribe(() => {
|
|
308
|
+
this.updateIceConnectionState();
|
|
292
309
|
});
|
|
293
|
-
this.updateIceConnectionState(iceTransport.state);
|
|
294
310
|
iceTransport.iceGather.onIceCandidate = (candidate) => {
|
|
295
311
|
if (!this.localDescription)
|
|
296
312
|
return;
|
|
@@ -302,7 +318,6 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
302
318
|
}
|
|
303
319
|
candidate.sdpMLineIndex = 0;
|
|
304
320
|
candidate.sdpMid = media.rtp.muxId;
|
|
305
|
-
// for chrome & firefox & maybe others
|
|
306
321
|
candidate.foundation = "candidate:" + candidate.foundation;
|
|
307
322
|
this.onIceCandidate.execute(candidate.toJSON());
|
|
308
323
|
if (this.onicecandidate)
|
|
@@ -310,10 +325,15 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
310
325
|
this.emit("icecandidate", { candidate });
|
|
311
326
|
};
|
|
312
327
|
const dtlsTransport = new dtls_1.RTCDtlsTransport(iceTransport, this.router, this.certificates, srtpProfiles);
|
|
313
|
-
return
|
|
328
|
+
return dtlsTransport;
|
|
314
329
|
}
|
|
315
330
|
createSctpTransport() {
|
|
316
|
-
const
|
|
331
|
+
const dtlsTransport = this.createTransport([
|
|
332
|
+
const_1.SRTP_PROFILE.SRTP_AEAD_AES_128_GCM,
|
|
333
|
+
const_1.SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
334
|
+
]);
|
|
335
|
+
const sctp = new sctp_1.RTCSctpTransport();
|
|
336
|
+
sctp.setDtlsTransport(dtlsTransport);
|
|
317
337
|
sctp.mid = undefined;
|
|
318
338
|
sctp.onDataChannel.subscribe((channel) => {
|
|
319
339
|
this.onDataChannel.execute(channel);
|
|
@@ -322,6 +342,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
322
342
|
this.ondatachannel(event);
|
|
323
343
|
this.emit("datachannel", event);
|
|
324
344
|
});
|
|
345
|
+
this.updateIceConnectionState();
|
|
325
346
|
return sctp;
|
|
326
347
|
}
|
|
327
348
|
async setLocalDescription(sessionDescription) {
|
|
@@ -339,9 +360,6 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
339
360
|
// # assign MID
|
|
340
361
|
description.media.forEach((media, i) => {
|
|
341
362
|
const mid = media.rtp.muxId;
|
|
342
|
-
if (!mid) {
|
|
343
|
-
throw new Error("mid not exist");
|
|
344
|
-
}
|
|
345
363
|
this.seenMid.add(mid);
|
|
346
364
|
if (["audio", "video"].includes(media.kind)) {
|
|
347
365
|
const transceiver = this.getTransceiverByMLineIndex(i);
|
|
@@ -353,26 +371,30 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
353
371
|
this.sctpTransport.mid = mid;
|
|
354
372
|
}
|
|
355
373
|
});
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
this.iceTransport.connection.iceControlling = false;
|
|
362
|
-
}
|
|
363
|
-
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
364
|
-
// RFC 8445 S6.1.1
|
|
365
|
-
if (this.iceTransport.connection.remoteIsLite) {
|
|
366
|
-
this.iceTransport.connection.iceControlling = true;
|
|
367
|
-
}
|
|
368
|
-
// # set DTLS role for mediasoup
|
|
369
|
-
if (description.type === "answer") {
|
|
370
|
-
const role = description.media.find((media) => media.dtlsParams)
|
|
371
|
-
?.dtlsParams?.role;
|
|
372
|
-
if (role) {
|
|
373
|
-
this.dtlsTransport.role = role;
|
|
374
|
+
const setupRole = (dtlsTransport) => {
|
|
375
|
+
const iceTransport = dtlsTransport.iceTransport;
|
|
376
|
+
// # set ICE role
|
|
377
|
+
if (description.type === "offer") {
|
|
378
|
+
iceTransport.connection.iceControlling = true;
|
|
374
379
|
}
|
|
375
|
-
|
|
380
|
+
else {
|
|
381
|
+
iceTransport.connection.iceControlling = false;
|
|
382
|
+
}
|
|
383
|
+
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
384
|
+
// RFC 8445 S6.1.1
|
|
385
|
+
if (iceTransport.connection.remoteIsLite) {
|
|
386
|
+
iceTransport.connection.iceControlling = true;
|
|
387
|
+
}
|
|
388
|
+
// # set DTLS role for mediasoup
|
|
389
|
+
if (description.type === "answer") {
|
|
390
|
+
const role = description.media.find((media) => media.dtlsParams)
|
|
391
|
+
?.dtlsParams?.role;
|
|
392
|
+
if (role) {
|
|
393
|
+
dtlsTransport.role = role;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
this.dtlsTransports.forEach((d) => setupRole(d));
|
|
376
398
|
// # configure direction
|
|
377
399
|
this.transceivers.forEach((t) => {
|
|
378
400
|
if (["answer", "pranswer"].includes(description.type)) {
|
|
@@ -383,10 +405,18 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
383
405
|
// for trickle ice
|
|
384
406
|
this.setLocal(description);
|
|
385
407
|
// # gather candidates
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
408
|
+
for (const iceTransport of this.iceTransports) {
|
|
409
|
+
await iceTransport.iceGather.gather();
|
|
410
|
+
}
|
|
411
|
+
description.media
|
|
412
|
+
.filter((m) => ["audio", "video"].includes(m.kind))
|
|
413
|
+
.forEach((m, i) => {
|
|
414
|
+
addTransportDescription(m, this.transceivers[i].dtlsTransport);
|
|
389
415
|
});
|
|
416
|
+
const sctpMedia = description.media.find((m) => m.kind === "application");
|
|
417
|
+
if (this.sctpTransport && sctpMedia) {
|
|
418
|
+
addTransportDescription(sctpMedia, this.sctpTransport.dtlsTransport);
|
|
419
|
+
}
|
|
390
420
|
this.setLocal(description);
|
|
391
421
|
// connect transports
|
|
392
422
|
if (description.type === "answer") {
|
|
@@ -412,33 +442,34 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
412
442
|
}
|
|
413
443
|
async addIceCandidate(candidateMessage) {
|
|
414
444
|
const candidate = ice_1.IceCandidate.fromJSON(candidateMessage);
|
|
415
|
-
|
|
445
|
+
for (const iceTransport of this.iceTransports) {
|
|
446
|
+
await iceTransport.addRemoteCandidate(candidate);
|
|
447
|
+
}
|
|
416
448
|
}
|
|
417
449
|
async connect() {
|
|
418
450
|
if (this.masterTransportEstablished)
|
|
419
451
|
return;
|
|
420
|
-
const dtlsTransport = this.dtlsTransport;
|
|
421
|
-
const iceTransport = dtlsTransport.iceTransport;
|
|
422
452
|
this.setConnectionState("connecting");
|
|
423
|
-
await
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
453
|
+
await Promise.all(this.dtlsTransports.map(async (dtlsTransport) => {
|
|
454
|
+
const { iceTransport } = dtlsTransport;
|
|
455
|
+
await iceTransport.start().catch((err) => {
|
|
456
|
+
log("iceTransport.start failed", err);
|
|
457
|
+
throw err;
|
|
458
|
+
});
|
|
459
|
+
await dtlsTransport.start().catch((err) => {
|
|
460
|
+
log("dtlsTransport.start failed", err);
|
|
461
|
+
throw err;
|
|
462
|
+
});
|
|
463
|
+
}));
|
|
433
464
|
if (this.sctpTransport && this.sctpRemotePort) {
|
|
434
465
|
await this.sctpTransport.start(this.sctpRemotePort);
|
|
435
466
|
await this.sctpTransport.sctp.stateChanged.connected.asPromise();
|
|
467
|
+
log("sctp connected");
|
|
436
468
|
}
|
|
437
|
-
log("sctp connected");
|
|
438
469
|
this.masterTransportEstablished = true;
|
|
439
470
|
this.setConnectionState("connected");
|
|
440
471
|
}
|
|
441
|
-
|
|
472
|
+
getLocalRtpParams(transceiver) {
|
|
442
473
|
if (transceiver.mid == undefined)
|
|
443
474
|
throw new Error("mid not assigned");
|
|
444
475
|
const rtp = {
|
|
@@ -449,12 +480,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
449
480
|
};
|
|
450
481
|
return rtp;
|
|
451
482
|
}
|
|
452
|
-
|
|
453
|
-
if (transceiver.mLineIndex == undefined)
|
|
454
|
-
throw new Error("mLineIndex not assigned");
|
|
455
|
-
const media = remoteDescription.media[transceiver.mLineIndex];
|
|
456
|
-
if (!media)
|
|
457
|
-
throw new Error("media line not exist");
|
|
483
|
+
getRemoteRtpParams(media, transceiver) {
|
|
458
484
|
const receiveParameters = {
|
|
459
485
|
muxId: media.rtp.muxId,
|
|
460
486
|
rtcp: media.rtp.rtcp,
|
|
@@ -483,112 +509,69 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
483
509
|
const remoteSdp = sdp_1.SessionDescription.parse(sessionDescription.sdp);
|
|
484
510
|
remoteSdp.type = sessionDescription.type;
|
|
485
511
|
this.validateDescription(remoteSdp, false);
|
|
512
|
+
const bundle = remoteSdp.group.find((g) => g.semantic === "BUNDLE");
|
|
513
|
+
let bundleTransport;
|
|
486
514
|
// # apply description
|
|
487
515
|
for (const [i, remoteMedia] of (0, helper_1.enumerate)(remoteSdp.media)) {
|
|
516
|
+
let dtlsTransport;
|
|
488
517
|
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
489
|
-
|
|
490
|
-
[undefined, remoteMedia.rtp.muxId].includes(t.mid))
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
});
|
|
496
|
-
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
497
|
-
this.onTransceiver.execute(transceiver);
|
|
498
|
-
return transceiver;
|
|
499
|
-
})();
|
|
500
|
-
if (!transceiver.mid) {
|
|
501
|
-
transceiver.mid = remoteMedia.rtp.muxId;
|
|
502
|
-
transceiver.mLineIndex = i;
|
|
503
|
-
}
|
|
504
|
-
// # negotiate codecs
|
|
505
|
-
transceiver.codecs = remoteMedia.rtp.codecs.filter((remoteCodec) => {
|
|
506
|
-
const localCodecs = this.configuration.codecs[remoteMedia.kind] || [];
|
|
507
|
-
const existCodec = (0, exports.findCodecByMimeType)(localCodecs, remoteCodec);
|
|
508
|
-
if (!existCodec)
|
|
509
|
-
return false;
|
|
510
|
-
if (existCodec?.name.toLowerCase() === "rtx") {
|
|
511
|
-
const params = (0, _1.codecParametersFromString)(existCodec.parameters ?? "");
|
|
512
|
-
const pt = params["apt"];
|
|
513
|
-
const origin = remoteMedia.rtp.codecs.find((c) => c.payloadType === pt);
|
|
514
|
-
if (!origin)
|
|
515
|
-
return false;
|
|
516
|
-
return !!(0, exports.findCodecByMimeType)(localCodecs, origin);
|
|
517
|
-
}
|
|
518
|
-
return true;
|
|
519
|
-
});
|
|
520
|
-
log("negotiated codecs", transceiver.codecs);
|
|
521
|
-
if (transceiver.codecs.length === 0) {
|
|
522
|
-
throw new Error("negotiate codecs failed.");
|
|
523
|
-
}
|
|
524
|
-
transceiver.headerExtensions = remoteMedia.rtp.headerExtensions.filter((extension) => (this.configuration.headerExtensions[remoteMedia.kind] || []).find((v) => v.uri === extension.uri));
|
|
525
|
-
// # configure direction
|
|
526
|
-
const mediaDirection = remoteMedia.direction || "inactive";
|
|
527
|
-
const direction = (0, utils_1.reverseDirection)(mediaDirection);
|
|
528
|
-
if (["answer", "pranswer"].includes(remoteSdp.type)) {
|
|
529
|
-
transceiver.currentDirection = direction;
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
transceiver.offerDirection = direction;
|
|
533
|
-
}
|
|
534
|
-
const localParams = this.localRtp(transceiver);
|
|
535
|
-
transceiver.sender.prepareSend(localParams);
|
|
536
|
-
if (["recvonly", "sendrecv"].includes(transceiver.direction)) {
|
|
537
|
-
const remotePrams = this.remoteRtp(remoteSdp, transceiver);
|
|
538
|
-
// register simulcast receiver
|
|
539
|
-
remoteMedia.simulcastParameters.forEach((param) => {
|
|
540
|
-
this.router.registerRtpReceiverByRid(transceiver, param, remotePrams);
|
|
518
|
+
let transceiver = this.transceivers.find((t) => t.kind === remoteMedia.kind &&
|
|
519
|
+
[undefined, remoteMedia.rtp.muxId].includes(t.mid));
|
|
520
|
+
if (!transceiver) {
|
|
521
|
+
// create remote transceiver
|
|
522
|
+
transceiver = this.addTransceiver(remoteMedia.kind, {
|
|
523
|
+
direction: "recvonly",
|
|
541
524
|
});
|
|
542
|
-
|
|
543
|
-
// register ssrc receiver
|
|
544
|
-
this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
|
|
525
|
+
this.onRemoteTransceiverAdded.execute(transceiver);
|
|
545
526
|
}
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
transceiver.
|
|
552
|
-
this.fireOnTrack(transceiver.receiver.track, transceiver, new track_1.MediaStream({
|
|
553
|
-
id: streamId,
|
|
554
|
-
tracks: [transceiver.receiver.track],
|
|
555
|
-
}));
|
|
527
|
+
if (bundle) {
|
|
528
|
+
if (!bundleTransport) {
|
|
529
|
+
bundleTransport = transceiver.dtlsTransport;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
transceiver.setDtlsTransport(bundleTransport);
|
|
556
533
|
}
|
|
557
534
|
}
|
|
558
|
-
transceiver.
|
|
535
|
+
dtlsTransport = transceiver.dtlsTransport;
|
|
536
|
+
this.setRemoteRTP(transceiver, remoteMedia, remoteSdp.type, i);
|
|
559
537
|
}
|
|
560
538
|
else if (remoteMedia.kind === "application") {
|
|
561
|
-
// # configure sctp
|
|
562
|
-
this.sctpRemotePort = remoteMedia.sctpPort;
|
|
563
|
-
if (!this.sctpRemotePort) {
|
|
564
|
-
throw new Error("sctpRemotePort not exist");
|
|
565
|
-
}
|
|
566
539
|
if (!this.sctpTransport) {
|
|
567
540
|
this.sctpTransport = this.createSctpTransport();
|
|
568
541
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
542
|
+
if (bundle) {
|
|
543
|
+
if (!bundleTransport) {
|
|
544
|
+
bundleTransport = this.sctpTransport.dtlsTransport;
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
this.sctpTransport.setDtlsTransport(bundleTransport);
|
|
548
|
+
}
|
|
572
549
|
}
|
|
550
|
+
dtlsTransport = this.sctpTransport.dtlsTransport;
|
|
551
|
+
this.setRemoteSCTP(remoteMedia, this.sctpTransport);
|
|
573
552
|
}
|
|
553
|
+
else {
|
|
554
|
+
throw new Error("invalid media kind");
|
|
555
|
+
}
|
|
556
|
+
const iceTransport = dtlsTransport.iceTransport;
|
|
574
557
|
if (remoteMedia.iceParams && remoteMedia.dtlsParams) {
|
|
575
|
-
|
|
576
|
-
|
|
558
|
+
iceTransport.setRemoteParams(remoteMedia.iceParams);
|
|
559
|
+
dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
|
|
577
560
|
// One agent full, one lite: The full agent MUST take the controlling role, and the lite agent MUST take the controlled role
|
|
578
561
|
// RFC 8445 S6.1.1
|
|
579
562
|
if (remoteMedia.iceParams?.iceLite) {
|
|
580
|
-
|
|
563
|
+
iceTransport.connection.iceControlling = true;
|
|
581
564
|
}
|
|
582
565
|
}
|
|
583
566
|
// # add ICE candidates
|
|
584
|
-
remoteMedia.iceCandidates.forEach(
|
|
585
|
-
await
|
|
567
|
+
remoteMedia.iceCandidates.forEach(iceTransport.addRemoteCandidate);
|
|
568
|
+
await iceTransport.iceGather.gather();
|
|
586
569
|
if (remoteMedia.iceCandidatesComplete) {
|
|
587
|
-
await
|
|
570
|
+
await iceTransport.addRemoteCandidate(undefined);
|
|
588
571
|
}
|
|
589
572
|
// # set DTLS role
|
|
590
573
|
if (remoteSdp.type === "answer" && remoteMedia.dtlsParams?.role) {
|
|
591
|
-
|
|
574
|
+
dtlsTransport.role =
|
|
592
575
|
remoteMedia.dtlsParams.role === "client" ? "server" : "client";
|
|
593
576
|
}
|
|
594
577
|
}
|
|
@@ -618,6 +601,78 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
618
601
|
this.needNegotiation();
|
|
619
602
|
}
|
|
620
603
|
}
|
|
604
|
+
setRemoteRTP(transceiver, remoteMedia, type, mLineIndex) {
|
|
605
|
+
if (!transceiver.mid) {
|
|
606
|
+
transceiver.mid = remoteMedia.rtp.muxId;
|
|
607
|
+
transceiver.mLineIndex = mLineIndex;
|
|
608
|
+
}
|
|
609
|
+
// # negotiate codecs
|
|
610
|
+
transceiver.codecs = remoteMedia.rtp.codecs.filter((remoteCodec) => {
|
|
611
|
+
const localCodecs = this.configuration.codecs[remoteMedia.kind] || [];
|
|
612
|
+
const existCodec = (0, exports.findCodecByMimeType)(localCodecs, remoteCodec);
|
|
613
|
+
if (!existCodec)
|
|
614
|
+
return false;
|
|
615
|
+
if (existCodec?.name.toLowerCase() === "rtx") {
|
|
616
|
+
const params = (0, _1.codecParametersFromString)(existCodec.parameters ?? "");
|
|
617
|
+
const pt = params["apt"];
|
|
618
|
+
const origin = remoteMedia.rtp.codecs.find((c) => c.payloadType === pt);
|
|
619
|
+
if (!origin)
|
|
620
|
+
return false;
|
|
621
|
+
return !!(0, exports.findCodecByMimeType)(localCodecs, origin);
|
|
622
|
+
}
|
|
623
|
+
return true;
|
|
624
|
+
});
|
|
625
|
+
log("negotiated codecs", transceiver.codecs);
|
|
626
|
+
if (transceiver.codecs.length === 0) {
|
|
627
|
+
throw new Error("negotiate codecs failed.");
|
|
628
|
+
}
|
|
629
|
+
transceiver.headerExtensions = remoteMedia.rtp.headerExtensions.filter((extension) => (this.configuration.headerExtensions[remoteMedia.kind] || []).find((v) => v.uri === extension.uri));
|
|
630
|
+
// # configure direction
|
|
631
|
+
const mediaDirection = remoteMedia.direction || "inactive";
|
|
632
|
+
const direction = (0, utils_1.reverseDirection)(mediaDirection);
|
|
633
|
+
if (["answer", "pranswer"].includes(type)) {
|
|
634
|
+
transceiver.currentDirection = direction;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
transceiver.offerDirection = direction;
|
|
638
|
+
}
|
|
639
|
+
const localParams = this.getLocalRtpParams(transceiver);
|
|
640
|
+
transceiver.sender.prepareSend(localParams);
|
|
641
|
+
if (["recvonly", "sendrecv"].includes(transceiver.direction)) {
|
|
642
|
+
const remotePrams = this.getRemoteRtpParams(remoteMedia, transceiver);
|
|
643
|
+
// register simulcast receiver
|
|
644
|
+
remoteMedia.simulcastParameters.forEach((param) => {
|
|
645
|
+
this.router.registerRtpReceiverByRid(transceiver, param, remotePrams);
|
|
646
|
+
});
|
|
647
|
+
transceiver.receiver.prepareReceive(remotePrams);
|
|
648
|
+
// register ssrc receiver
|
|
649
|
+
this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
|
|
650
|
+
}
|
|
651
|
+
if (["sendonly", "sendrecv"].includes(mediaDirection)) {
|
|
652
|
+
// assign msid
|
|
653
|
+
if (remoteMedia.msid != undefined) {
|
|
654
|
+
const [streamId, trackId] = remoteMedia.msid.split(" ");
|
|
655
|
+
transceiver.receiver.remoteStreamId = streamId;
|
|
656
|
+
transceiver.receiver.remoteTrackId = trackId;
|
|
657
|
+
this.fireOnTrack(transceiver.receiver.track, transceiver, new track_1.MediaStream({
|
|
658
|
+
id: streamId,
|
|
659
|
+
tracks: [transceiver.receiver.track],
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
transceiver.receiver.setupTWCC(remoteMedia.ssrc[0]?.ssrc);
|
|
664
|
+
}
|
|
665
|
+
setRemoteSCTP(remoteMedia, sctpTransport) {
|
|
666
|
+
// # configure sctp
|
|
667
|
+
this.sctpRemotePort = remoteMedia.sctpPort;
|
|
668
|
+
if (!this.sctpRemotePort) {
|
|
669
|
+
throw new Error("sctpRemotePort not exist");
|
|
670
|
+
}
|
|
671
|
+
sctpTransport.setRemotePort(this.sctpRemotePort);
|
|
672
|
+
if (!sctpTransport.mid) {
|
|
673
|
+
sctpTransport.mid = remoteMedia.rtp.muxId;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
621
676
|
validateDescription(description, isLocal) {
|
|
622
677
|
if (isLocal) {
|
|
623
678
|
if (description.type === "offer") {
|
|
@@ -656,7 +711,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
656
711
|
throw new Error();
|
|
657
712
|
const offerMedia = offer.media.map((v) => [v.kind, v.rtp.muxId]);
|
|
658
713
|
const answerMedia = description.media.map((v) => [v.kind, v.rtp.muxId]);
|
|
659
|
-
if (!(0,
|
|
714
|
+
if (!(0, isEqual_1.default)(offerMedia, answerMedia))
|
|
660
715
|
throw new Error("Media sections in answer do not match offer");
|
|
661
716
|
}
|
|
662
717
|
}
|
|
@@ -674,13 +729,18 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
674
729
|
addTransceiver(trackOrKind, options = {}) {
|
|
675
730
|
const kind = typeof trackOrKind === "string" ? trackOrKind : trackOrKind.kind;
|
|
676
731
|
const direction = options.direction || "sendrecv";
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
732
|
+
const dtlsTransport = this.createTransport([
|
|
733
|
+
const_1.SRTP_PROFILE.SRTP_AEAD_AES_128_GCM,
|
|
734
|
+
const_1.SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
|
|
735
|
+
]);
|
|
736
|
+
const sender = new rtpSender_1.RTCRtpSender(trackOrKind);
|
|
737
|
+
const receiver = new rtpReceiver_1.RTCRtpReceiver(kind, sender.ssrc);
|
|
738
|
+
const transceiver = new rtpTransceiver_1.RTCRtpTransceiver(kind, dtlsTransport, receiver, sender, direction);
|
|
680
739
|
transceiver.options = options;
|
|
681
740
|
this.router.registerRtpSender(transceiver.sender);
|
|
682
741
|
this.transceivers.push(transceiver);
|
|
683
742
|
this.onTransceiverAdded.execute(transceiver);
|
|
743
|
+
this.updateIceConnectionState();
|
|
684
744
|
this.needNegotiation();
|
|
685
745
|
return transceiver;
|
|
686
746
|
}
|
|
@@ -735,44 +795,60 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
735
795
|
return transceiver.sender;
|
|
736
796
|
}
|
|
737
797
|
}
|
|
798
|
+
async ensureCerts() {
|
|
799
|
+
const ensureCert = async (dtlsTransport) => {
|
|
800
|
+
if (this.certificates.length === 0) {
|
|
801
|
+
const localCertificate = await dtlsTransport.setupCertificate();
|
|
802
|
+
this.certificates.push(localCertificate);
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
dtlsTransport.localCertificate = this.certificates[0];
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
for (const dtlsTransport of this.dtlsTransports) {
|
|
809
|
+
await ensureCert(dtlsTransport);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
738
812
|
async createAnswer() {
|
|
739
813
|
this.assertNotClosed();
|
|
740
|
-
if (!["have-remote-offer", "have-local-pranswer"].includes(this.signalingState)
|
|
741
|
-
!this.dtlsTransport)
|
|
814
|
+
if (!["have-remote-offer", "have-local-pranswer"].includes(this.signalingState)) {
|
|
742
815
|
throw new Error("createAnswer failed");
|
|
743
|
-
if (this.certificates.length === 0) {
|
|
744
|
-
await this.dtlsTransport.setupCertificate();
|
|
745
816
|
}
|
|
817
|
+
if (!this._remoteDescription) {
|
|
818
|
+
throw new Error("wrong state");
|
|
819
|
+
}
|
|
820
|
+
await this.ensureCerts();
|
|
746
821
|
const description = new sdp_1.SessionDescription();
|
|
747
822
|
(0, sdp_1.addSDPHeader)("answer", description);
|
|
748
|
-
this._remoteDescription
|
|
823
|
+
this._remoteDescription.media.forEach((remoteMedia) => {
|
|
749
824
|
let dtlsTransport;
|
|
750
825
|
let media;
|
|
751
|
-
if (["audio", "video"].includes(
|
|
752
|
-
const transceiver = this.getTransceiverByMid(
|
|
826
|
+
if (["audio", "video"].includes(remoteMedia.kind)) {
|
|
827
|
+
const transceiver = this.getTransceiverByMid(remoteMedia.rtp.muxId);
|
|
753
828
|
media = createMediaDescriptionForTransceiver(transceiver, this.cname, (0, utils_1.andDirection)(transceiver.direction, transceiver.offerDirection), transceiver.mid);
|
|
754
|
-
if (!transceiver.dtlsTransport)
|
|
755
|
-
throw new Error();
|
|
756
829
|
dtlsTransport = transceiver.dtlsTransport;
|
|
757
830
|
}
|
|
758
|
-
else if (
|
|
759
|
-
if (!this.sctpTransport || !this.sctpTransport.mid)
|
|
760
|
-
throw new Error();
|
|
831
|
+
else if (remoteMedia.kind === "application") {
|
|
832
|
+
if (!this.sctpTransport || !this.sctpTransport.mid) {
|
|
833
|
+
throw new Error("sctpTransport not found");
|
|
834
|
+
}
|
|
761
835
|
media = createMediaDescriptionForSctp(this.sctpTransport, this.sctpTransport.mid);
|
|
762
836
|
dtlsTransport = this.sctpTransport.dtlsTransport;
|
|
763
837
|
}
|
|
764
|
-
else
|
|
765
|
-
throw new Error();
|
|
838
|
+
else {
|
|
839
|
+
throw new Error("invalid kind");
|
|
840
|
+
}
|
|
766
841
|
// # determine DTLS role, or preserve the currently configured role
|
|
767
|
-
if (!media.dtlsParams)
|
|
768
|
-
throw new Error();
|
|
842
|
+
if (!media.dtlsParams) {
|
|
843
|
+
throw new Error("dtlsParams missing");
|
|
844
|
+
}
|
|
769
845
|
if (dtlsTransport.role === "auto") {
|
|
770
846
|
media.dtlsParams.role = "client";
|
|
771
847
|
}
|
|
772
848
|
else {
|
|
773
849
|
media.dtlsParams.role = dtlsTransport.role;
|
|
774
850
|
}
|
|
775
|
-
media.simulcastParameters =
|
|
851
|
+
media.simulcastParameters = remoteMedia.simulcastParameters.map((v) => ({
|
|
776
852
|
...v,
|
|
777
853
|
direction: (0, utils_1.reverseSimulcastDirection)(v.direction),
|
|
778
854
|
}));
|
|
@@ -798,26 +874,85 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
798
874
|
if (this.sctpTransport) {
|
|
799
875
|
await this.sctpTransport.stop();
|
|
800
876
|
}
|
|
801
|
-
|
|
802
|
-
|
|
877
|
+
for (const dtlsTransport of this.dtlsTransports) {
|
|
878
|
+
await dtlsTransport.stop();
|
|
879
|
+
await dtlsTransport.iceTransport.stop();
|
|
880
|
+
}
|
|
803
881
|
this.dispose();
|
|
804
882
|
log("peerConnection closed");
|
|
805
883
|
}
|
|
806
884
|
assertNotClosed() {
|
|
807
|
-
if (this.isClosed)
|
|
885
|
+
if (this.isClosed) {
|
|
808
886
|
throw new Error("RTCPeerConnection is closed");
|
|
887
|
+
}
|
|
809
888
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
889
|
+
// https://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate
|
|
890
|
+
updateIceGatheringState() {
|
|
891
|
+
const all = this.iceTransports;
|
|
892
|
+
function allMatch(...state) {
|
|
893
|
+
return (all.filter((check) => state.includes(check.iceGather.gatheringState))
|
|
894
|
+
.length === all.length);
|
|
895
|
+
}
|
|
896
|
+
let newState;
|
|
897
|
+
if (all.length && allMatch("complete")) {
|
|
898
|
+
newState = "complete";
|
|
899
|
+
}
|
|
900
|
+
else if (!all.length || allMatch("new", "complete")) {
|
|
901
|
+
newState = "new";
|
|
902
|
+
}
|
|
903
|
+
else if (all.map((check) => check.iceGather.gatheringState).includes("gathering")) {
|
|
904
|
+
newState = "gathering";
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
newState = "new";
|
|
908
|
+
}
|
|
909
|
+
if (this.iceGatheringState === newState) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
log("iceGatheringStateChange", newState);
|
|
913
|
+
this.iceGatheringState = newState;
|
|
914
|
+
this.iceGatheringStateChange.execute(newState);
|
|
915
|
+
this.emit("icegatheringstatechange", newState);
|
|
815
916
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
917
|
+
// https://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate
|
|
918
|
+
updateIceConnectionState() {
|
|
919
|
+
const all = this.iceTransports;
|
|
920
|
+
let newState;
|
|
921
|
+
function allMatch(...state) {
|
|
922
|
+
return (all.filter((check) => state.includes(check.state)).length === all.length);
|
|
923
|
+
}
|
|
924
|
+
if (this.connectionState === "closed") {
|
|
925
|
+
newState = "closed";
|
|
926
|
+
}
|
|
927
|
+
else if (allMatch("failed")) {
|
|
928
|
+
newState = "failed";
|
|
929
|
+
}
|
|
930
|
+
else if (allMatch("disconnected")) {
|
|
931
|
+
newState = "disconnected";
|
|
932
|
+
}
|
|
933
|
+
else if (allMatch("new", "closed")) {
|
|
934
|
+
newState = "new";
|
|
935
|
+
}
|
|
936
|
+
else if (allMatch("new", "checking")) {
|
|
937
|
+
newState = "checking";
|
|
938
|
+
}
|
|
939
|
+
else if (allMatch("completed", "closed")) {
|
|
940
|
+
newState = "completed";
|
|
941
|
+
}
|
|
942
|
+
else if (allMatch("connected", "completed", "closed")) {
|
|
943
|
+
newState = "connected";
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
// unreachable?
|
|
947
|
+
newState = "new";
|
|
948
|
+
}
|
|
949
|
+
if (this.iceConnectionState === newState) {
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
log("iceConnectionStateChange", newState);
|
|
953
|
+
this.iceConnectionState = newState;
|
|
954
|
+
this.iceConnectionStateChange.execute(newState);
|
|
955
|
+
this.emit("iceconnectionstatechange", newState);
|
|
821
956
|
}
|
|
822
957
|
setSignalingState(state) {
|
|
823
958
|
log("signalingStateChange", state);
|
|
@@ -958,5 +1093,6 @@ exports.defaultPeerConfig = {
|
|
|
958
1093
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
959
1094
|
icePortRange: undefined,
|
|
960
1095
|
dtls: {},
|
|
1096
|
+
bundlePolicy: "max-compat",
|
|
961
1097
|
};
|
|
962
1098
|
//# sourceMappingURL=peerConnection.js.map
|