livekit-client 2.18.0 → 2.18.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 (145) 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 +8 -7
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +8026 -5883
  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/api/SignalClient.d.ts +12 -4
  10. package/dist/src/api/SignalClient.d.ts.map +1 -1
  11. package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  12. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  13. package/dist/src/e2ee/constants.d.ts.map +1 -1
  14. package/dist/src/e2ee/types.d.ts +6 -0
  15. package/dist/src/e2ee/types.d.ts.map +1 -1
  16. package/dist/src/e2ee/utils.d.ts +2 -1
  17. package/dist/src/e2ee/utils.d.ts.map +1 -1
  18. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  19. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  20. package/dist/src/index.d.ts +6 -4
  21. package/dist/src/index.d.ts.map +1 -1
  22. package/dist/src/room/PCTransport.d.ts +5 -0
  23. package/dist/src/room/PCTransport.d.ts.map +1 -1
  24. package/dist/src/room/PCTransportManager.d.ts +1 -1
  25. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  26. package/dist/src/room/RTCEngine.d.ts +28 -11
  27. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  28. package/dist/src/room/Room.d.ts +14 -3
  29. package/dist/src/room/Room.d.ts.map +1 -1
  30. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
  31. package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
  32. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  33. package/dist/src/room/data-track/LocalDataTrack.d.ts +28 -5
  34. package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
  35. package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -5
  36. package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
  37. package/dist/src/room/data-track/depacketizer.d.ts +4 -4
  38. package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
  39. package/dist/src/room/data-track/frame.d.ts +14 -0
  40. package/dist/src/room/data-track/frame.d.ts.map +1 -1
  41. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +19 -11
  42. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
  43. package/dist/src/room/data-track/incoming/pipeline.d.ts +6 -5
  44. package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
  45. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
  46. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
  47. package/dist/src/room/data-track/outgoing/errors.d.ts +16 -6
  48. package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
  49. package/dist/src/room/data-track/outgoing/pipeline.d.ts +7 -6
  50. package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
  51. package/dist/src/room/data-track/outgoing/types.d.ts +14 -4
  52. package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
  53. package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
  54. package/dist/src/room/data-track/packetizer.d.ts +4 -4
  55. package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
  56. package/dist/src/room/data-track/track-interfaces.d.ts +1 -1
  57. package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -1
  58. package/dist/src/room/data-track/types.d.ts +6 -1
  59. package/dist/src/room/data-track/types.d.ts.map +1 -1
  60. package/dist/src/room/events.d.ts +24 -3
  61. package/dist/src/room/events.d.ts.map +1 -1
  62. package/dist/src/room/participant/LocalParticipant.d.ts +12 -1
  63. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  64. package/dist/src/room/participant/RemoteParticipant.d.ts +13 -0
  65. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  66. package/dist/src/room/utils.d.ts +1 -0
  67. package/dist/src/room/utils.d.ts.map +1 -1
  68. package/dist/src/utils/deferrable-map.d.ts +32 -0
  69. package/dist/src/utils/deferrable-map.d.ts.map +1 -0
  70. package/dist/src/utils/serializer.d.ts +48 -0
  71. package/dist/src/utils/serializer.d.ts.map +1 -0
  72. package/dist/ts4.2/api/SignalClient.d.ts +12 -4
  73. package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +2 -1
  74. package/dist/ts4.2/e2ee/types.d.ts +6 -0
  75. package/dist/ts4.2/e2ee/utils.d.ts +2 -1
  76. package/dist/ts4.2/index.d.ts +7 -4
  77. package/dist/ts4.2/room/PCTransport.d.ts +5 -0
  78. package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
  79. package/dist/ts4.2/room/RTCEngine.d.ts +28 -11
  80. package/dist/ts4.2/room/Room.d.ts +14 -3
  81. package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
  82. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +27 -4
  83. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +4 -4
  84. package/dist/ts4.2/room/data-track/depacketizer.d.ts +4 -4
  85. package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
  86. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +21 -10
  87. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +6 -5
  88. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
  89. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +16 -6
  90. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +7 -6
  91. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +14 -4
  92. package/dist/ts4.2/room/data-track/packetizer.d.ts +4 -4
  93. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +1 -1
  94. package/dist/ts4.2/room/data-track/types.d.ts +6 -1
  95. package/dist/ts4.2/room/events.d.ts +24 -3
  96. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +12 -1
  97. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +13 -0
  98. package/dist/ts4.2/room/utils.d.ts +1 -0
  99. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
  100. package/dist/ts4.2/utils/serializer.d.ts +48 -0
  101. package/package.json +1 -1
  102. package/src/api/SignalClient.test.ts +9 -4
  103. package/src/api/SignalClient.ts +116 -9
  104. package/src/connectionHelper/ConnectionCheck.ts +1 -1
  105. package/src/e2ee/constants.ts +1 -0
  106. package/src/e2ee/types.ts +6 -0
  107. package/src/e2ee/utils.ts +4 -3
  108. package/src/e2ee/worker/DataCryptor.ts +1 -4
  109. package/src/e2ee/worker/FrameCryptor.ts +1 -4
  110. package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
  111. package/src/index.ts +13 -4
  112. package/src/room/PCTransport.ts +41 -1
  113. package/src/room/PCTransportManager.ts +1 -1
  114. package/src/room/RTCEngine.ts +312 -125
  115. package/src/room/Room.ts +168 -35
  116. package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +26 -2
  117. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -7
  118. package/src/room/data-track/LocalDataTrack.ts +83 -10
  119. package/src/room/data-track/RemoteDataTrack.ts +7 -9
  120. package/src/room/data-track/depacketizer.ts +21 -12
  121. package/src/room/data-track/frame.ts +28 -2
  122. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +58 -73
  123. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +139 -80
  124. package/src/room/data-track/incoming/pipeline.ts +29 -24
  125. package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +225 -32
  126. package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +150 -75
  127. package/src/room/data-track/outgoing/errors.ts +36 -7
  128. package/src/room/data-track/outgoing/pipeline.ts +23 -17
  129. package/src/room/data-track/outgoing/types.ts +12 -3
  130. package/src/room/data-track/packet/extensions.ts +17 -22
  131. package/src/room/data-track/packet/index.test.ts +22 -33
  132. package/src/room/data-track/packetizer.test.ts +2 -2
  133. package/src/room/data-track/packetizer.ts +4 -4
  134. package/src/room/data-track/track-interfaces.ts +1 -1
  135. package/src/room/data-track/types.ts +21 -1
  136. package/src/room/events.ts +26 -1
  137. package/src/room/participant/LocalParticipant.ts +74 -8
  138. package/src/room/participant/RemoteParticipant.ts +25 -0
  139. package/src/room/utils.ts +4 -0
  140. package/src/utils/deferrable-map.ts +109 -0
  141. package/src/utils/serializer.ts +72 -0
  142. package/dist/src/room/data-track/e2ee.d.ts +0 -12
  143. package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
  144. package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
  145. package/src/room/data-track/e2ee.ts +0 -15
@@ -72,7 +72,7 @@ describe('DataTrackPacket', () => {
72
72
 
73
73
  const packet = new DataTrackPacket(header, payloadBytes);
74
74
 
75
- expect(packet.toBinaryLengthBytes()).toStrictEqual(78);
75
+ expect(packet.toBinaryLengthBytes()).toStrictEqual(74);
76
76
  expect(packet.toBinary()).toStrictEqual(
77
77
  new Uint8Array([
78
78
  0xc, // Version 0, final, extension
@@ -88,13 +88,11 @@ describe('DataTrackPacket', () => {
88
88
  17,
89
89
  136,
90
90
  0, // Rtp oriented extension words (big endian)
91
- 7,
91
+ 6,
92
92
 
93
93
  // E2ee extension
94
- 0, // ID 1 (big endian)
95
- 1,
96
- 0, // Length 12 (big endian)
97
- 12,
94
+ 1, // ID 1
95
+ 13, // Length 13
98
96
  0xfa, // Key index
99
97
  0x3c, // Iv array
100
98
  0x3c,
@@ -110,10 +108,8 @@ describe('DataTrackPacket', () => {
110
108
  0x3c,
111
109
 
112
110
  // User timestamp extension
113
- 0, // ID 2 (big endian)
114
- 2,
115
- 0, // Length 7 (big endian)
116
- 7,
111
+ 2, // ID 2
112
+ 8, // Length 8
117
113
  68, // Timestamp value (big endian)
118
114
  17,
119
115
  34,
@@ -178,7 +174,7 @@ describe('DataTrackPacket', () => {
178
174
 
179
175
  const packet = new DataTrackPacket(header, payloadBytes);
180
176
 
181
- expect(packet.toBinaryLengthBytes()).toStrictEqual(66);
177
+ expect(packet.toBinaryLengthBytes()).toStrictEqual(62);
182
178
  expect(packet.toBinary()).toStrictEqual(
183
179
  new Uint8Array([
184
180
  0x14, // Version 0, start, extension
@@ -194,13 +190,11 @@ describe('DataTrackPacket', () => {
194
190
  0,
195
191
  104,
196
192
  0, // RTP oriented extension words (big endian)
197
- 4,
193
+ 3,
198
194
 
199
195
  // E2ee extension
200
- 0, // ID 1 (big endian)
201
- 1,
202
- 0, // Length 12 (big endian)
203
- 12,
196
+ 1, // ID 1
197
+ 13, // Length 13
204
198
  0xfa, // Key index
205
199
  0x3c, // Iv array
206
200
  0x3c,
@@ -216,8 +210,6 @@ describe('DataTrackPacket', () => {
216
210
  0x3c,
217
211
 
218
212
  0, // Extension padding
219
- 0,
220
- 0,
221
213
 
222
214
  0xfa, // Payload
223
215
  0xfa,
@@ -431,13 +423,11 @@ describe('DataTrackPacket', () => {
431
423
  ...VALID_PACKET_BYTES,
432
424
 
433
425
  0, // RTP oriented extension words (big endian)
434
- 4,
426
+ 3,
435
427
 
436
428
  // E2ee extension
437
- 0, // ID 1 (big endian)
438
- 1,
439
- 0, // Length 12 (big endian)
440
- 12,
429
+ 1, // ID 1
430
+ 12, // Length 12
441
431
  0xfa, // Key index
442
432
  0x3c, // Iv array
443
433
  0x3c,
@@ -453,8 +443,6 @@ describe('DataTrackPacket', () => {
453
443
  0x3c,
454
444
 
455
445
  0, // Padding
456
- 0,
457
- 0,
458
446
  ]);
459
447
  packetBytes[0] |= 1 << EXT_FLAG_SHIFT; // Extension flag
460
448
 
@@ -476,10 +464,8 @@ describe('DataTrackPacket', () => {
476
464
  2,
477
465
 
478
466
  // User timestamp extension
479
- 0, // ID 2 (big endian)
480
- 2,
481
- 0, // Length 7 (big endian)
482
- 7,
467
+ 2, // ID 2
468
+ 7, // Length 7
483
469
  0x44, // Timestamp (big endian)
484
470
  0x11,
485
471
  0x22,
@@ -488,6 +474,9 @@ describe('DataTrackPacket', () => {
488
474
  0x11,
489
475
  0x88,
490
476
  0x11,
477
+
478
+ 0, // Padding
479
+ 0,
491
480
  ]);
492
481
  packetBytes[0] |= 1 << EXT_FLAG_SHIFT; // Extension flag
493
482
 
@@ -508,19 +497,19 @@ describe('DataTrackPacket', () => {
508
497
  2,
509
498
 
510
499
  // Unknown / potential future extension
511
- 0, // ID 8 (big endian)
512
- 8,
513
- 0, // Length 12 (big endian)
514
- 6,
500
+ 8, // ID 8
501
+ 6, // Length 6
515
502
  0x1, // Payload
516
503
  0x2,
517
504
  0x3,
518
505
  0x4,
519
506
  0x5,
520
507
  0x6,
508
+ 0x0,
521
509
 
522
510
  0x0, // Padding
523
511
  0x0,
512
+ 0x0,
524
513
  ]);
525
514
  packetBytes[0] |= 1 << EXT_FLAG_SHIFT; // Extension flag
526
515
 
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
- import { DataTrackFrame } from './frame';
3
+ import { DataTrackFrameInternal } from './frame';
4
4
  import { DataTrackHandle } from './handle';
5
5
  import { FrameMarker } from './packet';
6
6
  import { DataTrackExtensions } from './packet/extensions';
@@ -90,7 +90,7 @@ describe('DataTrackPacketizer', () => {
90
90
  ])('should test packetizer edge cases', (payloadSizeBytes, mtuSizeBytes, label) => {
91
91
  const packetizer = new DataTrackPacketizer(DataTrackHandle.fromNumber(1), mtuSizeBytes);
92
92
 
93
- const frame: DataTrackFrame = {
93
+ const frame: DataTrackFrameInternal = {
94
94
  payload: new Uint8Array(payloadSizeBytes).fill(0xab),
95
95
  extensions: new DataTrackExtensions(),
96
96
  };
@@ -1,6 +1,6 @@
1
1
  import type { Throws } from '@livekit/throws-transformer/throws';
2
2
  import { LivekitReasonedError } from '../errors';
3
- import { type DataTrackFrame } from './frame';
3
+ import { type DataTrackFrameInternal } from './frame';
4
4
  import { DataTrackHandle } from './handle';
5
5
  import { DataTrackPacket, DataTrackPacketHeader, FrameMarker } from './packet';
6
6
  import { DataTrackClock, DataTrackTimestamp, WrapAroundUnsignedInt } from './utils';
@@ -38,7 +38,7 @@ export enum DataTrackPacketizerReason {
38
38
  MtuTooShort = 0,
39
39
  }
40
40
 
41
- /** A packetizer takes a {@link DataTrackFrame} as input and generates a series
41
+ /** A packetizer takes a {@link DataTrackFrameInternal} as input and generates a series
42
42
  * of {@link DataTrackPacket}s for transmission to other clients over webrtc. */
43
43
  export default class DataTrackPacketizer {
44
44
  private handle: DataTrackHandle;
@@ -70,13 +70,13 @@ export default class DataTrackPacketizer {
70
70
  }
71
71
  }
72
72
 
73
- /** Generates a series of packets for the specified {@link DataTrackPacketizerFrame}.
73
+ /** Generates a series of packets for the specified {@link DataTrackFrameInternal}.
74
74
  *
75
75
  * NOTE: The return value of this function is a generator, so it can be lazily ran if desired,
76
76
  * or converted to an array with {@link Array.from}.
77
77
  */
78
78
  *packetize(
79
- frame: DataTrackFrame,
79
+ frame: DataTrackFrameInternal,
80
80
  options?: PacketizeOptions,
81
81
  ): Throws<Generator<DataTrackPacket>, DataTrackPacketizerError> {
82
82
  const frameNumber = this.frameNumber.getThenIncrement();
@@ -45,7 +45,7 @@ export const DataTrackSymbol: symbol = Symbol.for('lk.data-track');
45
45
  export interface IDataTrack extends ITrack {
46
46
  readonly typeSymbol: typeof DataTrackSymbol;
47
47
 
48
- readonly info: DataTrackInfo;
48
+ readonly info?: DataTrackInfo;
49
49
  }
50
50
 
51
51
  export function isDataTrack(subject: unknown): subject is IDataTrack {
@@ -1,3 +1,4 @@
1
+ import { Encryption_Type, DataTrackInfo as ProtocolDataTrackInfo } from '@livekit/protocol';
1
2
  import { type DataTrackHandle } from './handle';
2
3
 
3
4
  export type DataTrackSid = string;
@@ -6,6 +7,25 @@ export type DataTrackSid = string;
6
7
  export type DataTrackInfo = {
7
8
  sid: DataTrackSid;
8
9
  pubHandle: DataTrackHandle;
9
- name: String;
10
+ name: string;
10
11
  usesE2ee: boolean;
11
12
  };
13
+
14
+ export const DataTrackInfo = {
15
+ from(protocolInfo: ProtocolDataTrackInfo): DataTrackInfo {
16
+ return {
17
+ sid: protocolInfo.sid,
18
+ pubHandle: protocolInfo.pubHandle,
19
+ name: protocolInfo.name,
20
+ usesE2ee: protocolInfo.encryption !== Encryption_Type.NONE,
21
+ };
22
+ },
23
+ toProtobuf(info: DataTrackInfo): ProtocolDataTrackInfo {
24
+ return new ProtocolDataTrackInfo({
25
+ sid: info.sid,
26
+ pubHandle: info.pubHandle,
27
+ name: info.name,
28
+ encryption: info.usesE2ee ? Encryption_Type.GCM : Encryption_Type.NONE,
29
+ });
30
+ },
31
+ };
@@ -332,7 +332,7 @@ export enum RoomEvent {
332
332
  EncryptionError = 'encryptionError',
333
333
  /**
334
334
  * Emits whenever the current buffer status of a data channel changes
335
- * args: (isLow: boolean, kind: [[DataPacket_Kind]])
335
+ * args: (isLow: boolean, kind: [[DataChannelKind]])
336
336
  */
337
337
  DCBufferStatusChanged = 'dcBufferStatusChanged',
338
338
 
@@ -352,6 +352,26 @@ export enum RoomEvent {
352
352
  * fired when the client receives connection metrics from other participants
353
353
  */
354
354
  MetricsReceived = 'metricsReceived',
355
+
356
+ /**
357
+ * Emits when a new data track has been published by a downstream participant.
358
+ */
359
+ DataTrackPublished = 'dataTrackPublished',
360
+
361
+ /**
362
+ * Emits when a new data track has been unpublished by a downstream participant.
363
+ */
364
+ DataTrackUnpublished = 'dataTrackUnpublished',
365
+
366
+ /**
367
+ * Emits when a new data track has been published locally.
368
+ */
369
+ LocalDataTrackPublished = 'localDataTrackPublished',
370
+
371
+ /**
372
+ * Emits when a new data track has been unpublished locally.
373
+ */
374
+ LocalDataTrackUnpublished = 'localDataTrackUnpublished',
355
375
  }
356
376
 
357
377
  export enum ParticipantEvent {
@@ -606,6 +626,11 @@ export enum EngineEvent {
606
626
  SignalRequestResponse = 'signalRequestResponse',
607
627
  SignalConnected = 'signalConnected',
608
628
  RoomMoved = 'roomMoved',
629
+ PublishDataTrackResponse = 'publishDataTrackResponse',
630
+ UnPublishDataTrackResponse = 'unPublishDataTrackResponse',
631
+ DataTrackSubscriberHandles = 'dataTrackSubscriberHandles',
632
+ DataTrackPacketReceived = 'dataTrackPacketReceived',
633
+ Joined = 'joined',
609
634
  }
610
635
 
611
636
  export enum TrackEvent {
@@ -1,3 +1,4 @@
1
+ import { Mutex } from '@livekit/mutex';
1
2
  import {
2
3
  AddTrackRequest,
3
4
  AudioTrackFeature,
@@ -27,8 +28,13 @@ import type { InternalRoomOptions } from '../../options';
27
28
  import TypedPromise from '../../utils/TypedPromise';
28
29
  import { PCTransportState } from '../PCTransportManager';
29
30
  import type RTCEngine from '../RTCEngine';
31
+ import { DataChannelKind } from '../RTCEngine';
30
32
  import type OutgoingDataStreamManager from '../data-stream/outgoing/OutgoingDataStreamManager';
31
33
  import type { TextStreamWriter } from '../data-stream/outgoing/StreamWriter';
34
+ import LocalDataTrack from '../data-track/LocalDataTrack';
35
+ import type OutgoingDataTrackManager from '../data-track/outgoing/OutgoingDataTrackManager';
36
+ import { DataTrackPublishError } from '../data-track/outgoing/errors';
37
+ import type { DataTrackOptions } from '../data-track/outgoing/types';
32
38
  import { defaultVideoCodec } from '../defaults';
33
39
  import {
34
40
  DeviceUnsupportedError,
@@ -138,6 +144,8 @@ export default class LocalParticipant extends Participant {
138
144
 
139
145
  private encryptionType: Encryption_Type = Encryption_Type.NONE;
140
146
 
147
+ private e2eeStateMutex = new Mutex();
148
+
141
149
  private reconnectFuture?: Future<void, Error>;
142
150
 
143
151
  private signalConnectedFuture?: Future<void, Error>;
@@ -150,6 +158,8 @@ export default class LocalParticipant extends Participant {
150
158
 
151
159
  private roomOutgoingDataStreamManager: OutgoingDataStreamManager;
152
160
 
161
+ private roomOutgoingDataTrackManager: OutgoingDataTrackManager;
162
+
153
163
  private pendingSignalRequests: Map<
154
164
  number,
155
165
  {
@@ -179,6 +189,7 @@ export default class LocalParticipant extends Participant {
179
189
  options: InternalRoomOptions,
180
190
  roomRpcHandlers: Map<string, (data: RpcInvocationData) => Promise<string>>,
181
191
  roomOutgoingDataStreamManager: OutgoingDataStreamManager,
192
+ roomOutgoingDataTrackManager: OutgoingDataTrackManager,
182
193
  ) {
183
194
  super(sid, identity, undefined, undefined, undefined, {
184
195
  loggerName: options.loggerName,
@@ -198,6 +209,7 @@ export default class LocalParticipant extends Participant {
198
209
  this.pendingSignalRequests = new Map();
199
210
  this.rpcHandlers = roomRpcHandlers;
200
211
  this.roomOutgoingDataStreamManager = roomOutgoingDataStreamManager;
212
+ this.roomOutgoingDataTrackManager = roomOutgoingDataTrackManager;
201
213
  }
202
214
 
203
215
  get lastCameraError(): Error | undefined {
@@ -311,6 +323,35 @@ export default class LocalParticipant extends Participant {
311
323
  }
312
324
  this.pendingSignalRequests.delete(requestId);
313
325
  }
326
+
327
+ switch (response.request.case) {
328
+ case 'publishDataTrack': {
329
+ let error;
330
+ switch (response.reason) {
331
+ case RequestResponse_Reason.NOT_ALLOWED:
332
+ error = DataTrackPublishError.notAllowed(response.message);
333
+ break;
334
+ case RequestResponse_Reason.DUPLICATE_NAME:
335
+ error = DataTrackPublishError.duplicateName(response.message);
336
+ break;
337
+ case RequestResponse_Reason.INVALID_NAME:
338
+ error = DataTrackPublishError.invalidName(response.message);
339
+ break;
340
+ case RequestResponse_Reason.LIMIT_EXCEEDED:
341
+ error = DataTrackPublishError.limitReached(response.message);
342
+ break;
343
+ default:
344
+ error = DataTrackPublishError.unknown(response.reason, response.message);
345
+ return;
346
+ }
347
+
348
+ this.roomOutgoingDataTrackManager.receivedSfuPublishResponse(
349
+ response.request.value.pubHandle,
350
+ { type: 'error', error },
351
+ );
352
+ break;
353
+ }
354
+ }
314
355
  };
315
356
 
316
357
  private handleDataPacket = (packet: DataPacket) => {
@@ -461,8 +502,20 @@ export default class LocalParticipant extends Participant {
461
502
 
462
503
  /** @internal */
463
504
  async setE2EEEnabled(enabled: boolean) {
464
- this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
465
- await this.republishAllTracks(undefined, false);
505
+ const unlock = await this.e2eeStateMutex.lock();
506
+ try {
507
+ this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
508
+ await Promise.all(this.pendingPublishPromises.values());
509
+ if (
510
+ this.trackPublications.size === 0 ||
511
+ Array.from(this.trackPublications.values()).every((pub) => pub.isEncrypted === enabled)
512
+ ) {
513
+ return;
514
+ }
515
+ await this.republishAllTracks(undefined, false);
516
+ } finally {
517
+ unlock();
518
+ }
466
519
  }
467
520
 
468
521
  /**
@@ -1648,7 +1701,8 @@ export default class LocalParticipant extends Participant {
1648
1701
  * @param options optionally specify a `reliable`, `topic` and `destination`
1649
1702
  */
1650
1703
  async publishData(data: Uint8Array, options: DataPublishOptions = {}): Promise<void> {
1651
- const kind = options.reliable ? DataPacket_Kind.RELIABLE : DataPacket_Kind.LOSSY;
1704
+ const kind = options.reliable ? DataChannelKind.RELIABLE : DataChannelKind.LOSSY;
1705
+ const dataPacketKind = options.reliable ? DataPacket_Kind.RELIABLE : DataPacket_Kind.LOSSY;
1652
1706
  const destinationIdentities = options.destinationIdentities;
1653
1707
  const topic = options.topic;
1654
1708
 
@@ -1660,7 +1714,7 @@ export default class LocalParticipant extends Participant {
1660
1714
  });
1661
1715
 
1662
1716
  const packet = new DataPacket({
1663
- kind: kind,
1717
+ kind: dataPacketKind,
1664
1718
  value: {
1665
1719
  case: 'user',
1666
1720
  value: userPacket,
@@ -1688,7 +1742,7 @@ export default class LocalParticipant extends Participant {
1688
1742
  },
1689
1743
  });
1690
1744
 
1691
- await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
1745
+ await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
1692
1746
  }
1693
1747
 
1694
1748
  /** @deprecated Consider migrating to {@link sendText} */
@@ -1708,7 +1762,7 @@ export default class LocalParticipant extends Participant {
1708
1762
  }),
1709
1763
  },
1710
1764
  });
1711
- await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
1765
+ await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
1712
1766
 
1713
1767
  this.emit(ParticipantEvent.ChatMessage, msg);
1714
1768
  return msg;
@@ -1731,7 +1785,7 @@ export default class LocalParticipant extends Participant {
1731
1785
  }),
1732
1786
  },
1733
1787
  });
1734
- await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
1788
+ await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
1735
1789
  this.emit(ParticipantEvent.ChatMessage, msg);
1736
1790
  return msg;
1737
1791
  }
@@ -1946,7 +2000,7 @@ export default class LocalParticipant extends Participant {
1946
2000
  },
1947
2001
  });
1948
2002
 
1949
- await this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
2003
+ await this.engine.sendDataPacket(packet, DataChannelKind.RELIABLE);
1950
2004
  }
1951
2005
 
1952
2006
  /** @internal */
@@ -2235,4 +2289,16 @@ export default class LocalParticipant extends Participant {
2235
2289
  await sleep(20);
2236
2290
  }
2237
2291
  }
2292
+
2293
+ /** Publishes a data track.
2294
+ *
2295
+ * Returns the published data track if successful. Use {@link LocalDataTrack#tryPush}
2296
+ * to send data frames on the track.
2297
+ */
2298
+ async publishDataTrack(options: DataTrackOptions): Promise<LocalDataTrack> {
2299
+ const track = new LocalDataTrack(options, this.roomOutgoingDataTrackManager);
2300
+ await track.publish();
2301
+
2302
+ return track;
2303
+ }
2238
2304
  }
@@ -5,6 +5,8 @@ import type {
5
5
  UpdateTrackSettings,
6
6
  } from '@livekit/protocol';
7
7
  import type { SignalClient } from '../../api/SignalClient';
8
+ import { DeferrableMap } from '../../utils/deferrable-map';
9
+ import type RemoteDataTrack from '../data-track/RemoteDataTrack';
8
10
  import { ParticipantEvent, TrackEvent } from '../events';
9
11
  import RemoteAudioTrack from '../track/RemoteAudioTrack';
10
12
  import type RemoteTrack from '../track/RemoteTrack';
@@ -27,6 +29,14 @@ export default class RemoteParticipant extends Participant {
27
29
 
28
30
  trackPublications: Map<string, RemoteTrackPublication>;
29
31
 
32
+ /** A map of data track name to the corresponding {@link RemoteDataTrack}.
33
+ * @example
34
+ * // An already existing data track:
35
+ * const track = remoteParticipant.dataTracks.get("data track name");
36
+ * // Wait for a data track which will be published soon:
37
+ * const track = await remoteParticipant.dataTracks.getDeferred("data track name"); */
38
+ dataTracks: DeferrableMap<RemoteDataTrack['info']['name'], RemoteDataTrack>;
39
+
30
40
  signalClient: SignalClient;
31
41
 
32
42
  private volumeMap: Map<Track.Source, number>;
@@ -75,6 +85,7 @@ export default class RemoteParticipant extends Participant {
75
85
  this.trackPublications = new Map();
76
86
  this.audioTrackPublications = new Map();
77
87
  this.videoTrackPublications = new Map();
88
+ this.dataTracks = new DeferrableMap();
78
89
  this.volumeMap = new Map();
79
90
  }
80
91
 
@@ -376,6 +387,20 @@ export default class RemoteParticipant extends Participant {
376
387
  await Promise.all(promises);
377
388
  }
378
389
 
390
+ /** @internal */
391
+ addRemoteDataTrack(remoteDataTrack: RemoteDataTrack) {
392
+ this.dataTracks.set(remoteDataTrack.info.name, remoteDataTrack);
393
+ }
394
+
395
+ /** @internal */
396
+ removeRemoteDataTrack(remoteDataTrackSid: RemoteDataTrack['info']['sid']) {
397
+ for (const [name, dataTrack] of this.dataTracks.entries()) {
398
+ if (remoteDataTrackSid === dataTrack.info.sid) {
399
+ this.dataTracks.delete(name);
400
+ }
401
+ }
402
+ }
403
+
379
404
  /** @internal */
380
405
  emit<E extends keyof ParticipantEventCallbacks>(
381
406
  event: E,
package/src/room/utils.ts CHANGED
@@ -775,3 +775,7 @@ export function extractMaxAgeFromRequestHeaders(headers: Headers): number | unde
775
775
  }
776
776
  return undefined;
777
777
  }
778
+
779
+ export function isCompressionStreamSupported() {
780
+ return typeof CompressionStream !== 'undefined';
781
+ }
@@ -0,0 +1,109 @@
1
+ import { type Throws } from '@livekit/throws-transformer/throws';
2
+ import { Future } from '../room/utils';
3
+
4
+ /** An error which is thrown if a {@link DeferrableMap#getDeferred} call is aborted midway
5
+ * through. */
6
+ export class DeferrableMapAbortError extends DOMException {
7
+ reason: unknown;
8
+
9
+ constructor(message: string, reason?: unknown) {
10
+ super(message, 'AbortError');
11
+ this.reason = reason;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * A Map-like container keyed by unique strings that supports the ability to wait
17
+ * for future keys to show up in the map.
18
+ *
19
+ * @example
20
+ * // An already existing key:
21
+ * const value = map.get("key");
22
+ * // Wait for a key which will be added soon:
23
+ * const value = await map.getDeferred("key");
24
+ */
25
+ export class DeferrableMap<K, V> extends Map<K, V> {
26
+ private pending: Map<K, Array<Future<V, DeferrableMapAbortError>>> = new Map();
27
+
28
+ set(key: K, value: V): this {
29
+ super.set(key, value);
30
+
31
+ // Resolve any futures waiting on this key.
32
+ const futures = this.pending.get(key);
33
+ if (futures) {
34
+ for (const future of futures) {
35
+ if (!future.isResolved) {
36
+ future.resolve?.(value);
37
+ }
38
+ }
39
+ this.pending.delete(key);
40
+ }
41
+
42
+ return this;
43
+ }
44
+
45
+ get [Symbol.toStringTag](): string {
46
+ return 'WaitableMap';
47
+ }
48
+
49
+ /**
50
+ * Returns the value for `key` immediately if it exists, otherwise returns a
51
+ * promise that resolves once `set(key, value)` is called.
52
+ *
53
+ * If an `AbortSignal` is provided and it is aborted before the key appears,
54
+ * the returned promise rejects with an {@link DeferrableMapAbortError}.
55
+ */
56
+ getDeferred(key: K): Promise<V>;
57
+ getDeferred(key: K, signal: AbortSignal): Promise<Throws<V, DeferrableMapAbortError>>;
58
+ async getDeferred(key: K, signal?: AbortSignal) {
59
+ const existing = this.get(key);
60
+ if (typeof existing !== 'undefined') {
61
+ return existing;
62
+ }
63
+
64
+ // Bail out immediately if the signal is already aborted.
65
+ if (signal?.aborted) {
66
+ throw new DeferrableMapAbortError('The operation was aborted.', signal.reason);
67
+ }
68
+
69
+ const future = new Future<V, DeferrableMapAbortError>(undefined, () => {
70
+ // Clean up the pending list when the future settles.
71
+ const futures = this.pending.get(key);
72
+ if (!futures) {
73
+ return;
74
+ }
75
+
76
+ const idx = futures.indexOf(future);
77
+ if (idx !== -1) {
78
+ futures.splice(idx, 1);
79
+ }
80
+ if (futures.length === 0) {
81
+ this.pending.delete(key);
82
+ }
83
+ });
84
+
85
+ const existingFutures = this.pending.get(key);
86
+ if (existingFutures) {
87
+ existingFutures.push(future);
88
+ } else {
89
+ this.pending.set(key, [future]);
90
+ }
91
+
92
+ // If a signal was provided, listen for abort and reject the future.
93
+ if (signal) {
94
+ const onAbort = () => {
95
+ if (!future.isResolved) {
96
+ future.reject?.(new DeferrableMapAbortError('The operation was aborted.', signal.reason));
97
+ }
98
+ };
99
+ signal.addEventListener('abort', onAbort, { once: true });
100
+
101
+ // Clean up the listener once the future settles (resolved or rejected).
102
+ future.promise.finally(() => {
103
+ signal.removeEventListener('abort', onAbort);
104
+ });
105
+ }
106
+
107
+ return future.promise;
108
+ }
109
+ }