livekit-client 1.14.1 → 1.14.2

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 (76) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +25 -44
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +399 -196
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  10. package/dist/src/e2ee/utils.d.ts +0 -1
  11. package/dist/src/e2ee/utils.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  13. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  14. package/dist/src/proto/livekit_models_pb.d.ts +87 -11
  15. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
  16. package/dist/src/proto/livekit_rtc_pb.d.ts +0 -4
  17. package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
  18. package/dist/src/room/PCTransport.d.ts +20 -1
  19. package/dist/src/room/PCTransport.d.ts.map +1 -1
  20. package/dist/src/room/RTCEngine.d.ts +1 -1
  21. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  22. package/dist/src/room/Room.d.ts.map +1 -1
  23. package/dist/src/room/defaults.d.ts +1 -0
  24. package/dist/src/room/defaults.d.ts.map +1 -1
  25. package/dist/src/room/events.d.ts +1 -1
  26. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  27. package/dist/src/room/timers.d.ts +1 -1
  28. package/dist/src/room/timers.d.ts.map +1 -1
  29. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
  30. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/LocalTrack.d.ts +3 -3
  32. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  33. package/dist/src/room/track/LocalVideoTrack.d.ts +2 -1
  34. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  35. package/dist/src/room/track/options.d.ts +0 -1
  36. package/dist/src/room/track/options.d.ts.map +1 -1
  37. package/dist/src/room/track/utils.d.ts +2 -1
  38. package/dist/src/room/track/utils.d.ts.map +1 -1
  39. package/dist/src/utils/cloneDeep.d.ts +2 -0
  40. package/dist/src/utils/cloneDeep.d.ts.map +1 -0
  41. package/dist/ts4.2/src/e2ee/utils.d.ts +0 -1
  42. package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +87 -11
  43. package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +0 -4
  44. package/dist/ts4.2/src/room/PCTransport.d.ts +20 -1
  45. package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -1
  46. package/dist/ts4.2/src/room/defaults.d.ts +1 -0
  47. package/dist/ts4.2/src/room/events.d.ts +1 -1
  48. package/dist/ts4.2/src/room/timers.d.ts +1 -1
  49. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +1 -1
  50. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -3
  51. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -1
  52. package/dist/ts4.2/src/room/track/options.d.ts +0 -1
  53. package/dist/ts4.2/src/room/track/utils.d.ts +1 -0
  54. package/dist/ts4.2/src/utils/cloneDeep.d.ts +2 -0
  55. package/package.json +14 -14
  56. package/src/connectionHelper/checks/webrtc.ts +1 -1
  57. package/src/e2ee/E2eeManager.ts +2 -1
  58. package/src/e2ee/utils.ts +0 -10
  59. package/src/e2ee/worker/FrameCryptor.ts +13 -14
  60. package/src/e2ee/worker/ParticipantKeyHandler.ts +4 -5
  61. package/src/e2ee/worker/e2ee.worker.ts +3 -1
  62. package/src/proto/livekit_models_pb.ts +140 -15
  63. package/src/proto/livekit_rtc_pb.ts +1 -7
  64. package/src/room/PCTransport.ts +116 -1
  65. package/src/room/RTCEngine.ts +49 -85
  66. package/src/room/Room.ts +10 -9
  67. package/src/room/defaults.ts +4 -2
  68. package/src/room/events.ts +1 -1
  69. package/src/room/participant/LocalParticipant.ts +44 -56
  70. package/src/room/track/LocalAudioTrack.ts +1 -1
  71. package/src/room/track/LocalTrack.ts +8 -5
  72. package/src/room/track/LocalVideoTrack.ts +2 -1
  73. package/src/room/track/options.ts +0 -7
  74. package/src/room/track/utils.ts +17 -8
  75. package/src/utils/cloneDeep.test.ts +54 -0
  76. package/src/utils/cloneDeep.ts +11 -0
@@ -326,11 +326,11 @@ export class FrameCryptor extends BaseFrameCryptor {
326
326
  }
327
327
  } else if (!this.keys.getKeySet(keyIndex) && this.keys.hasValidKey) {
328
328
  // emit an error in case the key index is out of bounds but the key handler thinks we still have a valid key
329
- workerLogger.warn('skipping decryption due to missing key at index');
329
+ workerLogger.warn(`skipping decryption due to missing key at index ${keyIndex}`);
330
330
  this.emit(
331
331
  CryptorEvent.Error,
332
332
  new CryptorError(
333
- `missing key at index for participant ${this.participantIdentity}`,
333
+ `missing key at index ${keyIndex} for participant ${this.participantIdentity}`,
334
334
  CryptorErrorReason.MissingKey,
335
335
  ),
336
336
  );
@@ -418,7 +418,7 @@ export class FrameCryptor extends BaseFrameCryptor {
418
418
  );
419
419
 
420
420
  let ratchetedKeySet: KeySet | undefined;
421
- if (keySet === this.keys.getKeySet(keyIndex)) {
421
+ if ((initialMaterial ?? keySet) === this.keys.getKeySet(keyIndex)) {
422
422
  // only ratchet if the currently set key is still the same as the one used to decrypt this frame
423
423
  // if not, it might be that a different frame has already ratcheted and we try with that one first
424
424
  const newMaterial = await this.keys.ratchetKey(keyIndex, false);
@@ -431,22 +431,21 @@ export class FrameCryptor extends BaseFrameCryptor {
431
431
  encryptionKey: ratchetedKeySet?.encryptionKey,
432
432
  });
433
433
  if (frame && ratchetedKeySet) {
434
- this.keys.setKeySet(ratchetedKeySet, keyIndex, true);
435
- // decryption was successful, set the new key index to reflect the ratcheted key set
436
- this.keys.setCurrentKeyIndex(keyIndex);
434
+ // before updating the keys, make sure that the keySet used for this frame is still the same as the currently set key
435
+ // if it's not, a new key might have been set already, which we don't want to override
436
+ if ((initialMaterial ?? keySet) === this.keys.getKeySet(keyIndex)) {
437
+ this.keys.setKeySet(ratchetedKeySet, keyIndex, true);
438
+ // decryption was successful, set the new key index to reflect the ratcheted key set
439
+ this.keys.setCurrentKeyIndex(keyIndex);
440
+ }
437
441
  }
438
442
  return frame;
439
443
  } else {
440
444
  /**
441
- * Since the key it is first send and only afterwards actually used for encrypting, there were
442
- * situations when the decrypting failed due to the fact that the received frame was not encrypted
443
- * yet and ratcheting, of course, did not solve the problem. So if we fail RATCHET_WINDOW_SIZE times,
444
- * we come back to the initial key.
445
+ * Because we only set a new key once decryption has been successful,
446
+ * we can be sure that we don't need to reset the key to the initial material at this point
447
+ * as the key has not been updated on the keyHandler instance
445
448
  */
446
- if (initialMaterial) {
447
- workerLogger.debug('resetting to initial material');
448
- this.keys.setKeyFromMaterial(initialMaterial.material, keyIndex);
449
- }
450
449
 
451
450
  workerLogger.warn('maximum ratchet attempts exceeded');
452
451
  throw new CryptorError(
@@ -138,12 +138,11 @@ export class ParticipantKeyHandler extends (EventEmitter as new () => TypedEvent
138
138
  * also updates the currentKeyIndex
139
139
  */
140
140
  async setKeyFromMaterial(material: CryptoKey, keyIndex = 0, emitRatchetEvent = false) {
141
- workerLogger.debug('setting new key');
142
- if (keyIndex >= 0) {
143
- this.currentKeyIndex = keyIndex % this.cryptoKeyRing.length;
144
- }
141
+ const newIndex = keyIndex >= 0 ? keyIndex % this.cryptoKeyRing.length : -1;
142
+ workerLogger.debug(`setting new key with index ${newIndex}`);
145
143
  const keySet = await deriveKeys(material, this.keyProviderOptions.ratchetSalt);
146
- this.setKeySet(keySet, this.currentKeyIndex, emitRatchetEvent);
144
+ this.setKeySet(keySet, newIndex >= 0 ? newIndex : this.currentKeyIndex, emitRatchetEvent);
145
+ if (newIndex >= 0) this.currentKeyIndex = newIndex;
147
146
  }
148
147
 
149
148
  setKeySet(keySet: KeySet, keyIndex: number, emitRatchetEvent = false) {
@@ -75,7 +75,9 @@ onmessage = (ev) => {
75
75
  workerLogger.warn('set shared key');
76
76
  setSharedKey(data.key, data.keyIndex);
77
77
  } else if (data.participantIdentity) {
78
- workerLogger.warn(`set participant sender key ${data.participantIdentity}`);
78
+ workerLogger.warn(
79
+ `set participant sender key ${data.participantIdentity} index ${data.keyIndex}`,
80
+ );
79
81
  getParticipantKeyHandler(data.participantIdentity).setKey(data.key, data.keyIndex);
80
82
  } else {
81
83
  workerLogger.error('no participant Id was provided and shared key usage is disabled');
@@ -12,7 +12,7 @@
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
 
15
- // @generated by protoc-gen-es v1.3.0 with parameter "target=ts"
15
+ // @generated by protoc-gen-es v1.3.3 with parameter "target=ts"
16
16
  // @generated from file livekit_models.proto (package livekit, syntax proto3)
17
17
  /* eslint-disable */
18
18
  // @ts-nocheck
@@ -84,6 +84,26 @@ proto3.util.setEnumType(VideoCodec, "livekit.VideoCodec", [
84
84
  { no: 4, name: "VP8" },
85
85
  ]);
86
86
 
87
+ /**
88
+ * @generated from enum livekit.ImageCodec
89
+ */
90
+ export enum ImageCodec {
91
+ /**
92
+ * @generated from enum value: IC_DEFAULT = 0;
93
+ */
94
+ IC_DEFAULT = 0,
95
+
96
+ /**
97
+ * @generated from enum value: IC_JPEG = 1;
98
+ */
99
+ IC_JPEG = 1,
100
+ }
101
+ // Retrieve enum metadata with: proto3.getEnumType(ImageCodec)
102
+ proto3.util.setEnumType(ImageCodec, "livekit.ImageCodec", [
103
+ { no: 0, name: "IC_DEFAULT" },
104
+ { no: 1, name: "IC_JPEG" },
105
+ ]);
106
+
87
107
  /**
88
108
  * @generated from enum livekit.TrackType
89
109
  */
@@ -411,11 +431,6 @@ export class Room extends Message<Room> {
411
431
  */
412
432
  activeRecording = false;
413
433
 
414
- /**
415
- * @generated from field: livekit.PlayoutDelay playout_delay = 12;
416
- */
417
- playoutDelay?: PlayoutDelay;
418
-
419
434
  constructor(data?: PartialMessage<Room>) {
420
435
  super();
421
436
  proto3.util.initPartial(data, this);
@@ -435,7 +450,6 @@ export class Room extends Message<Room> {
435
450
  { no: 9, name: "num_participants", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
436
451
  { no: 11, name: "num_publishers", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
437
452
  { no: 10, name: "active_recording", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
438
- { no: 12, name: "playout_delay", kind: "message", T: PlayoutDelay },
439
453
  ]);
440
454
 
441
455
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Room {
@@ -1282,6 +1296,11 @@ export class UserPacket extends Message<UserPacket> {
1282
1296
  */
1283
1297
  participantSid = "";
1284
1298
 
1299
+ /**
1300
+ * @generated from field: string participant_identity = 5;
1301
+ */
1302
+ participantIdentity = "";
1303
+
1285
1304
  /**
1286
1305
  * user defined payload
1287
1306
  *
@@ -1290,12 +1309,19 @@ export class UserPacket extends Message<UserPacket> {
1290
1309
  payload = new Uint8Array(0);
1291
1310
 
1292
1311
  /**
1293
- * the ID of the participants who will receive the message (the message will be sent to all the people in the room if this variable is empty)
1312
+ * the ID of the participants who will receive the message (sent to all by default)
1294
1313
  *
1295
1314
  * @generated from field: repeated string destination_sids = 3;
1296
1315
  */
1297
1316
  destinationSids: string[] = [];
1298
1317
 
1318
+ /**
1319
+ * identities of participants who will receive the message (sent to all by default)
1320
+ *
1321
+ * @generated from field: repeated string destination_identities = 6;
1322
+ */
1323
+ destinationIdentities: string[] = [];
1324
+
1299
1325
  /**
1300
1326
  * topic under which the message was published
1301
1327
  *
@@ -1312,8 +1338,10 @@ export class UserPacket extends Message<UserPacket> {
1312
1338
  static readonly typeName = "livekit.UserPacket";
1313
1339
  static readonly fields: FieldList = proto3.util.newFieldList(() => [
1314
1340
  { no: 1, name: "participant_sid", kind: "scalar", T: 9 /* ScalarType.STRING */ },
1341
+ { no: 5, name: "participant_identity", kind: "scalar", T: 9 /* ScalarType.STRING */ },
1315
1342
  { no: 2, name: "payload", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
1316
1343
  { no: 3, name: "destination_sids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true },
1344
+ { no: 6, name: "destination_identities", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true },
1317
1345
  { no: 4, name: "topic", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true },
1318
1346
  ]);
1319
1347
 
@@ -1613,6 +1641,16 @@ export enum ClientInfo_SDK {
1613
1641
  * @generated from enum value: RUST = 8;
1614
1642
  */
1615
1643
  RUST = 8,
1644
+
1645
+ /**
1646
+ * @generated from enum value: PYTHON = 9;
1647
+ */
1648
+ PYTHON = 9,
1649
+
1650
+ /**
1651
+ * @generated from enum value: CPP = 10;
1652
+ */
1653
+ CPP = 10,
1616
1654
  }
1617
1655
  // Retrieve enum metadata with: proto3.getEnumType(ClientInfo_SDK)
1618
1656
  proto3.util.setEnumType(ClientInfo_SDK, "livekit.ClientInfo.SDK", [
@@ -1625,6 +1663,8 @@ proto3.util.setEnumType(ClientInfo_SDK, "livekit.ClientInfo.SDK", [
1625
1663
  { no: 6, name: "UNITY" },
1626
1664
  { no: 7, name: "REACT_NATIVE" },
1627
1665
  { no: 8, name: "RUST" },
1666
+ { no: 9, name: "PYTHON" },
1667
+ { no: 10, name: "CPP" },
1628
1668
  ]);
1629
1669
 
1630
1670
  /**
@@ -1774,6 +1814,91 @@ export class DisabledCodecs extends Message<DisabledCodecs> {
1774
1814
  }
1775
1815
  }
1776
1816
 
1817
+ /**
1818
+ * @generated from message livekit.RTPDrift
1819
+ */
1820
+ export class RTPDrift extends Message<RTPDrift> {
1821
+ /**
1822
+ * @generated from field: google.protobuf.Timestamp start_time = 1;
1823
+ */
1824
+ startTime?: Timestamp;
1825
+
1826
+ /**
1827
+ * @generated from field: google.protobuf.Timestamp end_time = 2;
1828
+ */
1829
+ endTime?: Timestamp;
1830
+
1831
+ /**
1832
+ * @generated from field: double duration = 3;
1833
+ */
1834
+ duration = 0;
1835
+
1836
+ /**
1837
+ * @generated from field: uint64 start_timestamp = 4;
1838
+ */
1839
+ startTimestamp = protoInt64.zero;
1840
+
1841
+ /**
1842
+ * @generated from field: uint64 end_timestamp = 5;
1843
+ */
1844
+ endTimestamp = protoInt64.zero;
1845
+
1846
+ /**
1847
+ * @generated from field: uint64 rtp_clock_ticks = 6;
1848
+ */
1849
+ rtpClockTicks = protoInt64.zero;
1850
+
1851
+ /**
1852
+ * @generated from field: int64 drift_samples = 7;
1853
+ */
1854
+ driftSamples = protoInt64.zero;
1855
+
1856
+ /**
1857
+ * @generated from field: double drift_ms = 8;
1858
+ */
1859
+ driftMs = 0;
1860
+
1861
+ /**
1862
+ * @generated from field: double clock_rate = 9;
1863
+ */
1864
+ clockRate = 0;
1865
+
1866
+ constructor(data?: PartialMessage<RTPDrift>) {
1867
+ super();
1868
+ proto3.util.initPartial(data, this);
1869
+ }
1870
+
1871
+ static readonly runtime: typeof proto3 = proto3;
1872
+ static readonly typeName = "livekit.RTPDrift";
1873
+ static readonly fields: FieldList = proto3.util.newFieldList(() => [
1874
+ { no: 1, name: "start_time", kind: "message", T: Timestamp },
1875
+ { no: 2, name: "end_time", kind: "message", T: Timestamp },
1876
+ { no: 3, name: "duration", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ },
1877
+ { no: 4, name: "start_timestamp", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
1878
+ { no: 5, name: "end_timestamp", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
1879
+ { no: 6, name: "rtp_clock_ticks", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
1880
+ { no: 7, name: "drift_samples", kind: "scalar", T: 3 /* ScalarType.INT64 */ },
1881
+ { no: 8, name: "drift_ms", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ },
1882
+ { no: 9, name: "clock_rate", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ },
1883
+ ]);
1884
+
1885
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RTPDrift {
1886
+ return new RTPDrift().fromBinary(bytes, options);
1887
+ }
1888
+
1889
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RTPDrift {
1890
+ return new RTPDrift().fromJson(jsonValue, options);
1891
+ }
1892
+
1893
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RTPDrift {
1894
+ return new RTPDrift().fromJsonString(jsonString, options);
1895
+ }
1896
+
1897
+ static equals(a: RTPDrift | PlainMessage<RTPDrift> | undefined, b: RTPDrift | PlainMessage<RTPDrift> | undefined): boolean {
1898
+ return proto3.util.equals(RTPDrift, a, b);
1899
+ }
1900
+ }
1901
+
1777
1902
  /**
1778
1903
  * @generated from message livekit.RTPStats
1779
1904
  */
@@ -1984,16 +2109,16 @@ export class RTPStats extends Message<RTPStats> {
1984
2109
  lastLayerLockPli?: Timestamp;
1985
2110
 
1986
2111
  /**
1987
- * @generated from field: double sample_rate = 42;
2112
+ * @generated from field: livekit.RTPDrift packet_drift = 44;
1988
2113
  */
1989
- sampleRate = 0;
2114
+ packetDrift?: RTPDrift;
1990
2115
 
1991
2116
  /**
1992
- * NEXT_ID: 44
2117
+ * NEXT_ID: 46
1993
2118
  *
1994
- * @generated from field: double drift_ms = 43;
2119
+ * @generated from field: livekit.RTPDrift report_drift = 45;
1995
2120
  */
1996
- driftMs = 0;
2121
+ reportDrift?: RTPDrift;
1997
2122
 
1998
2123
  constructor(data?: PartialMessage<RTPStats>) {
1999
2124
  super();
@@ -2044,8 +2169,8 @@ export class RTPStats extends Message<RTPStats> {
2044
2169
  { no: 34, name: "last_key_frame", kind: "message", T: Timestamp },
2045
2170
  { no: 35, name: "layer_lock_plis", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
2046
2171
  { no: 36, name: "last_layer_lock_pli", kind: "message", T: Timestamp },
2047
- { no: 42, name: "sample_rate", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ },
2048
- { no: 43, name: "drift_ms", kind: "scalar", T: 1 /* ScalarType.DOUBLE */ },
2172
+ { no: 44, name: "packet_drift", kind: "message", T: RTPDrift },
2173
+ { no: 45, name: "report_drift", kind: "message", T: RTPDrift },
2049
2174
  ]);
2050
2175
 
2051
2176
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RTPStats {
@@ -12,7 +12,7 @@
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
 
15
- // @generated by protoc-gen-es v1.3.0 with parameter "target=ts"
15
+ // @generated by protoc-gen-es v1.3.3 with parameter "target=ts"
16
16
  // @generated from file livekit_rtc.proto (package livekit, syntax proto3)
17
17
  /* eslint-disable */
18
18
  // @ts-nocheck
@@ -487,11 +487,6 @@ export class SimulcastCodec extends Message<SimulcastCodec> {
487
487
  */
488
488
  cid = "";
489
489
 
490
- /**
491
- * @generated from field: bool enable_simulcast_layers = 3;
492
- */
493
- enableSimulcastLayers = false;
494
-
495
490
  constructor(data?: PartialMessage<SimulcastCodec>) {
496
491
  super();
497
492
  proto3.util.initPartial(data, this);
@@ -502,7 +497,6 @@ export class SimulcastCodec extends Message<SimulcastCodec> {
502
497
  static readonly fields: FieldList = proto3.util.newFieldList(() => [
503
498
  { no: 1, name: "codec", kind: "scalar", T: 9 /* ScalarType.STRING */ },
504
499
  { no: 2, name: "cid", kind: "scalar", T: 9 /* ScalarType.STRING */ },
505
- { no: 3, name: "enable_simulcast_layers", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
506
500
  ]);
507
501
 
508
502
  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): SimulcastCodec {
@@ -32,7 +32,7 @@ export const PCEvents = {
32
32
  export default class PCTransport extends EventEmitter {
33
33
  private _pc: RTCPeerConnection | null;
34
34
 
35
- public get pc() {
35
+ private get pc() {
36
36
  if (this._pc) return this._pc;
37
37
  throw new UnexpectedConnectionState('Expected peer connection to be available');
38
38
  }
@@ -51,12 +51,38 @@ export default class PCTransport extends EventEmitter {
51
51
 
52
52
  onOffer?: (offer: RTCSessionDescriptionInit) => void;
53
53
 
54
+ onIceCandidate?: (candidate: RTCIceCandidate) => void;
55
+
56
+ onIceCandidateError?: (ev: Event) => void;
57
+
58
+ onConnectionStateChange?: (state: RTCPeerConnectionState) => void;
59
+
60
+ onDataChannel?: (ev: RTCDataChannelEvent) => void;
61
+
62
+ onTrack?: (ev: RTCTrackEvent) => void;
63
+
54
64
  constructor(config?: RTCConfiguration, mediaConstraints: Record<string, unknown> = {}) {
55
65
  super();
56
66
  this._pc = isChromiumBased()
57
67
  ? // @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor
58
68
  new RTCPeerConnection(config, mediaConstraints)
59
69
  : new RTCPeerConnection(config);
70
+ this._pc.onicecandidate = (ev) => {
71
+ if (!ev.candidate) return;
72
+ this.onIceCandidate?.(ev.candidate);
73
+ };
74
+ this._pc.onicecandidateerror = (ev) => {
75
+ this.onIceCandidateError?.(ev);
76
+ };
77
+ this._pc.onconnectionstatechange = () => {
78
+ this.onConnectionStateChange?.(this._pc?.connectionState ?? 'closed');
79
+ };
80
+ this._pc.ondatachannel = (ev) => {
81
+ this.onDataChannel?.(ev);
82
+ };
83
+ this._pc.ontrack = (ev) => {
84
+ this.onTrack?.(ev);
85
+ };
60
86
  }
61
87
 
62
88
  get isICEConnected(): boolean {
@@ -270,10 +296,99 @@ export default class PCTransport extends EventEmitter {
270
296
  return answer;
271
297
  }
272
298
 
299
+ createDataChannel(label: string, dataChannelDict: RTCDataChannelInit) {
300
+ return this.pc.createDataChannel(label, dataChannelDict);
301
+ }
302
+
303
+ addTransceiver(mediaStreamTrack: MediaStreamTrack, transceiverInit: RTCRtpTransceiverInit) {
304
+ return this.pc.addTransceiver(mediaStreamTrack, transceiverInit);
305
+ }
306
+
307
+ addTrack(track: MediaStreamTrack) {
308
+ return this.pc.addTrack(track);
309
+ }
310
+
273
311
  setTrackCodecBitrate(info: TrackBitrateInfo) {
274
312
  this.trackBitrates.push(info);
275
313
  }
276
314
 
315
+ setConfiguration(rtcConfig: RTCConfiguration) {
316
+ return this.pc.setConfiguration(rtcConfig);
317
+ }
318
+
319
+ canRemoveTrack(): boolean {
320
+ return !!this.pc.removeTrack;
321
+ }
322
+
323
+ removeTrack(sender: RTCRtpSender) {
324
+ return this.pc.removeTrack(sender);
325
+ }
326
+
327
+ getConnectionState() {
328
+ return this.pc.connectionState;
329
+ }
330
+
331
+ getICEConnectionState() {
332
+ return this.pc.iceConnectionState;
333
+ }
334
+
335
+ getSignallingState() {
336
+ return this.pc.signalingState;
337
+ }
338
+
339
+ getTransceivers() {
340
+ return this.pc.getTransceivers();
341
+ }
342
+
343
+ getSenders() {
344
+ return this.pc.getSenders();
345
+ }
346
+
347
+ getLocalDescription() {
348
+ return this.pc.localDescription;
349
+ }
350
+
351
+ getRemoteDescription() {
352
+ return this.pc.remoteDescription;
353
+ }
354
+
355
+ async getConnectedAddress(): Promise<string | undefined> {
356
+ if (!this._pc) {
357
+ return;
358
+ }
359
+ let selectedCandidatePairId = '';
360
+ const candidatePairs = new Map<string, RTCIceCandidatePairStats>();
361
+ // id -> candidate ip
362
+ const candidates = new Map<string, string>();
363
+ const stats: RTCStatsReport = await this._pc.getStats();
364
+ stats.forEach((v) => {
365
+ switch (v.type) {
366
+ case 'transport':
367
+ selectedCandidatePairId = v.selectedCandidatePairId;
368
+ break;
369
+ case 'candidate-pair':
370
+ if (selectedCandidatePairId === '' && v.selected) {
371
+ selectedCandidatePairId = v.id;
372
+ }
373
+ candidatePairs.set(v.id, v);
374
+ break;
375
+ case 'remote-candidate':
376
+ candidates.set(v.id, `${v.address}:${v.port}`);
377
+ break;
378
+ default:
379
+ }
380
+ });
381
+
382
+ if (selectedCandidatePairId === '') {
383
+ return undefined;
384
+ }
385
+ const selectedID = candidatePairs.get(selectedCandidatePairId)?.remoteCandidateId;
386
+ if (selectedID === undefined) {
387
+ return undefined;
388
+ }
389
+ return candidates.get(selectedID);
390
+ }
391
+
277
392
  close() {
278
393
  if (!this._pc) {
279
394
  return;