livekit-client 1.14.1 → 1.14.2

Sign up to get free protection for your applications and to get access to all the features.
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;