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.
Files changed (64) hide show
  1. package/lib/common/src/binary.d.ts +0 -1
  2. package/lib/common/src/binary.js +1 -2
  3. package/lib/common/src/binary.js.map +1 -1
  4. package/lib/dtls/src/context/cipher.js +5 -2
  5. package/lib/dtls/src/context/cipher.js.map +1 -1
  6. package/lib/dtls/src/handshake/extensions/useSrtp.js +5 -2
  7. package/lib/dtls/src/handshake/extensions/useSrtp.js.map +1 -1
  8. package/lib/ice/src/candidate.js +5 -2
  9. package/lib/ice/src/candidate.js.map +1 -1
  10. package/lib/ice/src/ice.d.ts +2 -2
  11. package/lib/ice/src/ice.js +6 -5
  12. package/lib/ice/src/ice.js.map +1 -1
  13. package/lib/ice/src/stun/attributes.js +5 -2
  14. package/lib/ice/src/stun/attributes.js.map +1 -1
  15. package/lib/rtp/src/rtcp/rr.js +5 -2
  16. package/lib/rtp/src/rtcp/rr.js.map +1 -1
  17. package/lib/rtp/src/rtcp/rtpfb/nack.js +6 -3
  18. package/lib/rtp/src/rtcp/rtpfb/nack.js.map +1 -1
  19. package/lib/rtp/src/rtcp/rtpfb/twcc.js +6 -6
  20. package/lib/rtp/src/rtcp/rtpfb/twcc.js.map +1 -1
  21. package/lib/rtp/src/rtcp/sr.js +5 -2
  22. package/lib/rtp/src/rtcp/sr.js.map +1 -1
  23. package/lib/rtp/src/srtp/cipher/ctr.js +5 -2
  24. package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -1
  25. package/lib/rtp/src/srtp/cipher/gcm.js +6 -3
  26. package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -1
  27. package/lib/sctp/src/param.js +5 -2
  28. package/lib/sctp/src/param.js.map +1 -1
  29. package/lib/sctp/src/sctp.js +5 -5
  30. package/lib/sctp/src/sctp.js.map +1 -1
  31. package/lib/webrtc/src/media/receiver/nack.js +2 -2
  32. package/lib/webrtc/src/media/receiver/nack.js.map +1 -1
  33. package/lib/webrtc/src/media/rtpReceiver.d.ts +3 -2
  34. package/lib/webrtc/src/media/rtpReceiver.js +4 -2
  35. package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
  36. package/lib/webrtc/src/media/rtpSender.d.ts +4 -2
  37. package/lib/webrtc/src/media/rtpSender.js +15 -7
  38. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  39. package/lib/webrtc/src/media/rtpTransceiver.d.ts +7 -6
  40. package/lib/webrtc/src/media/rtpTransceiver.js +8 -3
  41. package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
  42. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +1 -1
  43. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
  44. package/lib/webrtc/src/peerConnection.d.ts +10 -10
  45. package/lib/webrtc/src/peerConnection.js +324 -188
  46. package/lib/webrtc/src/peerConnection.js.map +1 -1
  47. package/lib/webrtc/src/sdp.js +5 -2
  48. package/lib/webrtc/src/sdp.js.map +1 -1
  49. package/lib/webrtc/src/transport/dtls.d.ts +3 -2
  50. package/lib/webrtc/src/transport/dtls.js +3 -0
  51. package/lib/webrtc/src/transport/dtls.js.map +1 -1
  52. package/lib/webrtc/src/transport/sctp.d.ts +5 -3
  53. package/lib/webrtc/src/transport/sctp.js +46 -34
  54. package/lib/webrtc/src/transport/sctp.js.map +1 -1
  55. package/package.json +2 -2
  56. package/src/media/receiver/nack.ts +1 -1
  57. package/src/media/rtpReceiver.ts +6 -5
  58. package/src/media/rtpSender.ts +18 -9
  59. package/src/media/rtpTransceiver.ts +13 -6
  60. package/src/nonstandard/recorder/writer/webm.ts +1 -1
  61. package/src/peerConnection.ts +386 -223
  62. package/src/sdp.ts +1 -1
  63. package/src/transport/dtls.ts +4 -1
  64. 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 lodash_1 = require("lodash");
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, lodash_1.cloneDeep)(exports.defaultPeerConfig);
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
- const { iceTransport, dtlsTransport } = this.createTransport([
152
- const_1.SRTP_PROFILE.SRTP_AEAD_AES_128_GCM,
153
- const_1.SRTP_PROFILE.SRTP_AES128_CM_HMAC_SHA1_80,
154
- ]);
155
- this.iceTransport = iceTransport;
156
- this.dtlsTransport = dtlsTransport;
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
- if (this.certificates.length === 0) {
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
- return new dataChannel_1.RTCDataChannel(this.sctpTransport, parameters);
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
- iceGatherer.onGatheringStateChange.subscribe((state) => {
286
- this.updateIceGatheringState(state);
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(iceGatherer.gatheringState);
305
+ this.updateIceGatheringState();
289
306
  const iceTransport = new ice_1.RTCIceTransport(iceGatherer);
290
- iceTransport.onStateChange.subscribe((state) => {
291
- this.updateIceConnectionState(state);
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 { dtlsTransport, iceTransport };
328
+ return dtlsTransport;
314
329
  }
315
330
  createSctpTransport() {
316
- const sctp = new sctp_1.RTCSctpTransport(this.dtlsTransport);
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
- // # set ICE role
357
- if (description.type === "offer") {
358
- this.iceTransport.connection.iceControlling = true;
359
- }
360
- else {
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
- await this.iceTransport.iceGather.gather();
387
- description.media.map((media) => {
388
- addTransportDescription(media, this.dtlsTransport);
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
- await this.iceTransport.addRemoteCandidate(candidate);
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 iceTransport.start().catch((err) => {
424
- log("iceTransport.start failed", err);
425
- throw err;
426
- });
427
- log("ice connected");
428
- await dtlsTransport.start().catch((err) => {
429
- log("dtlsTransport.start failed", err);
430
- throw err;
431
- });
432
- log("dtls connected");
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
- localRtp(transceiver) {
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
- remoteRtp(remoteDescription, transceiver) {
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
- const transceiver = this.transceivers.find((t) => t.kind === remoteMedia.kind &&
490
- [undefined, remoteMedia.rtp.muxId].includes(t.mid)) ||
491
- (() => {
492
- // create remote transceiver
493
- const transceiver = this.addTransceiver(remoteMedia.kind, {
494
- direction: "recvonly",
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
- transceiver.receiver.prepareReceive(remotePrams);
543
- // register ssrc receiver
544
- this.router.registerRtpReceiverBySsrc(transceiver, remotePrams);
525
+ this.onRemoteTransceiverAdded.execute(transceiver);
545
526
  }
546
- if (["sendonly", "sendrecv"].includes(mediaDirection)) {
547
- // assign msid
548
- if (remoteMedia.msid != undefined) {
549
- const [streamId, trackId] = remoteMedia.msid.split(" ");
550
- transceiver.receiver.remoteStreamId = streamId;
551
- transceiver.receiver.remoteTrackId = trackId;
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.receiver.setupTWCC(remoteMedia.ssrc[0]?.ssrc);
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
- this.sctpTransport.setRemotePort(this.sctpRemotePort);
570
- if (!this.sctpTransport.mid) {
571
- this.sctpTransport.mid = remoteMedia.rtp.muxId;
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
- this.iceTransport.setRemoteParams(remoteMedia.iceParams);
576
- this.dtlsTransport.setRemoteParams(remoteMedia.dtlsParams);
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
- this.iceTransport.connection.iceControlling = true;
563
+ iceTransport.connection.iceControlling = true;
581
564
  }
582
565
  }
583
566
  // # add ICE candidates
584
- remoteMedia.iceCandidates.forEach(this.iceTransport.addRemoteCandidate);
585
- await this.iceTransport.iceGather.gather();
567
+ remoteMedia.iceCandidates.forEach(iceTransport.addRemoteCandidate);
568
+ await iceTransport.iceGather.gather();
586
569
  if (remoteMedia.iceCandidatesComplete) {
587
- await this.iceTransport.addRemoteCandidate(undefined);
570
+ await iceTransport.addRemoteCandidate(undefined);
588
571
  }
589
572
  // # set DTLS role
590
573
  if (remoteSdp.type === "answer" && remoteMedia.dtlsParams?.role) {
591
- this.dtlsTransport.role =
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, lodash_1.isEqual)(offerMedia, answerMedia))
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 sender = new rtpSender_1.RTCRtpSender(trackOrKind, this.dtlsTransport);
678
- const receiver = new rtpReceiver_1.RTCRtpReceiver(kind, this.dtlsTransport, sender.ssrc);
679
- const transceiver = new rtpTransceiver_1.RTCRtpTransceiver(kind, receiver, sender, direction, this.dtlsTransport);
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?.media.forEach((remoteM) => {
823
+ this._remoteDescription.media.forEach((remoteMedia) => {
749
824
  let dtlsTransport;
750
825
  let media;
751
- if (["audio", "video"].includes(remoteM.kind)) {
752
- const transceiver = this.getTransceiverByMid(remoteM.rtp.muxId);
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 (remoteM.kind === "application") {
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 = remoteM.simulcastParameters.map((v) => ({
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
- await this.dtlsTransport.stop();
802
- await this.iceTransport.stop();
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
- updateIceGatheringState(state) {
811
- log("iceGatheringStateChange", state);
812
- this.iceGatheringState = state;
813
- this.iceGatheringStateChange.execute(state);
814
- this.emit("icegatheringstatechange", state);
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
- updateIceConnectionState(state) {
817
- log("iceConnectionStateChange", state);
818
- this.iceConnectionState = state;
819
- this.iceConnectionStateChange.execute(state);
820
- this.emit("iceconnectionstatechange", state);
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