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
@@ -1,27 +1,27 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import type { Throws } from '@livekit/throws-transformer/throws';
3
3
  import type TypedEmitter from 'typed-emitter';
4
+ import type { BaseE2EEManager } from '../../../e2ee/E2eeManager';
4
5
  import { LoggerNames, getLogger } from '../../../logger';
5
6
  import { abortSignalAny, abortSignalTimeout } from '../../../utils/abort-signal-polyfill';
6
7
  import { Future } from '../../utils';
7
8
  import LocalDataTrack from '../LocalDataTrack';
8
- import { type EncryptionProvider } from '../e2ee';
9
- import type { DataTrackFrame } from '../frame';
9
+ import type { DataTrackFrameInternal } from '../frame';
10
10
  import { DataTrackHandle, DataTrackHandleAllocator } from '../handle';
11
- import { DataTrackExtensions } from '../packet/extensions';
12
11
  import { type DataTrackInfo } from '../types';
13
12
  import {
14
13
  DataTrackPublishError,
15
- DataTrackPublishErrorReason,
16
14
  DataTrackPushFrameError,
17
15
  DataTrackPushFrameErrorReason,
18
16
  } from './errors';
19
17
  import DataTrackOutgoingPipeline from './pipeline';
20
18
  import {
21
19
  type DataTrackOptions,
22
- type EventPacketsAvailable,
20
+ type EventPacketAvailable,
23
21
  type EventSfuPublishRequest,
24
22
  type EventSfuUnpublishRequest,
23
+ type EventTrackPublished,
24
+ type EventTrackUnpublished,
25
25
  type SfuPublishResponseResult,
26
26
  } from './types';
27
27
 
@@ -29,20 +29,15 @@ const log = getLogger(LoggerNames.DataTracks);
29
29
 
30
30
  export type PendingDescriptor = {
31
31
  type: 'pending';
32
- completionFuture: Future<
33
- LocalDataTrack,
34
- | DataTrackPublishError<DataTrackPublishErrorReason.NotAllowed>
35
- | DataTrackPublishError<DataTrackPublishErrorReason.DuplicateName>
36
- | DataTrackPublishError<DataTrackPublishErrorReason.Timeout>
37
- | DataTrackPublishError<DataTrackPublishErrorReason.LimitReached>
38
- | DataTrackPublishError<DataTrackPublishErrorReason.Disconnected>
39
- | DataTrackPublishError<DataTrackPublishErrorReason.Cancelled>
40
- >;
32
+ /** Resolves when the descriptor is fully published. */
33
+ completionFuture: Future<void, DataTrackPublishError>;
41
34
  };
42
35
  export type ActiveDescriptor = {
43
36
  type: 'active';
44
37
  info: DataTrackInfo;
45
38
 
39
+ publishState: 'published' | 'republishing' | 'unpublished';
40
+
46
41
  pipeline: DataTrackOutgoingPipeline;
47
42
 
48
43
  /** Resolves when the descriptor is unpublished. */
@@ -57,11 +52,12 @@ export const Descriptor = {
57
52
  completionFuture: new Future(),
58
53
  };
59
54
  },
60
- active(info: DataTrackInfo, encryptionProvider: EncryptionProvider | null): ActiveDescriptor {
55
+ active(info: DataTrackInfo, e2eeManager: BaseE2EEManager | null): ActiveDescriptor {
61
56
  return {
62
57
  type: 'active',
63
58
  info,
64
- pipeline: new DataTrackOutgoingPipeline({ info, encryptionProvider }),
59
+ publishState: 'published',
60
+ pipeline: new DataTrackOutgoingPipeline({ info, e2eeManager }),
65
61
  unpublishingFuture: new Future(),
66
62
  };
67
63
  },
@@ -72,24 +68,28 @@ export type DataTrackOutgoingManagerCallbacks = {
72
68
  sfuPublishRequest: (event: EventSfuPublishRequest) => void;
73
69
  /** Request sent to the SFU to unpublish a track. */
74
70
  sfuUnpublishRequest: (event: EventSfuUnpublishRequest) => void;
75
- /** Serialized packets are ready to be sent over the transport. */
76
- packetsAvailable: (event: EventPacketsAvailable) => void;
71
+ /** A serialized packet is ready to be sent over the transport. */
72
+ packetAvailable: (event: EventPacketAvailable) => void;
73
+ /** A new {@link LocalDataTrack} has been published */
74
+ trackPublished: (event: EventTrackPublished) => void;
75
+ /** A {@link LocalDataTrack} has been unpublished */
76
+ trackUnpublished: (event: EventTrackUnpublished) => void;
77
77
  };
78
78
 
79
79
  type OutgoingDataTrackManagerOptions = {
80
80
  /**
81
81
  * Provider to use for encrypting outgoing frame payloads.
82
82
  *
83
- * If none, end-to-end encryption will be disabled for all published tracks.
83
+ * If null, end-to-end encryption will be disabled for all published tracks.
84
84
  */
85
- encryptionProvider?: EncryptionProvider;
85
+ e2eeManager?: BaseE2EEManager;
86
86
  };
87
87
 
88
88
  /** How long to wait when attempting to publish before timing out. */
89
89
  const PUBLISH_TIMEOUT_MILLISECONDS = 10_000;
90
90
 
91
91
  export default class OutgoingDataTrackManager extends (EventEmitter as new () => TypedEmitter<DataTrackOutgoingManagerCallbacks>) {
92
- private encryptionProvider: EncryptionProvider | null;
92
+ private e2eeManager: BaseE2EEManager | null;
93
93
 
94
94
  private handleAllocator = new DataTrackHandleAllocator();
95
95
 
@@ -97,7 +97,7 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
97
97
 
98
98
  constructor(options?: OutgoingDataTrackManagerOptions) {
99
99
  super();
100
- this.encryptionProvider = options?.encryptionProvider ?? null;
100
+ this.e2eeManager = options?.e2eeManager ?? null;
101
101
  }
102
102
 
103
103
  static withDescriptors(descriptors: Map<DataTrackHandle, Descriptor>) {
@@ -106,6 +106,18 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
106
106
  return manager;
107
107
  }
108
108
 
109
+ /** @internal */
110
+ updateE2eeManager(e2eeManager: BaseE2EEManager | null) {
111
+ this.e2eeManager = e2eeManager;
112
+
113
+ // Propegate downwards to all pre-existing pipelines
114
+ for (const descriptor of this.descriptors.values()) {
115
+ if (descriptor.type === 'active') {
116
+ descriptor.pipeline.updateE2eeManager(e2eeManager);
117
+ }
118
+ }
119
+ }
120
+
109
121
  /**
110
122
  * Used by attached {@link LocalDataTrack} instances to query their associated descriptor info.
111
123
  * @internal
@@ -114,39 +126,35 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
114
126
  return this.descriptors.get(handle) ?? null;
115
127
  }
116
128
 
117
- createLocalDataTrack(handle: DataTrackHandle) {
118
- const descriptor = this.getDescriptor(handle);
119
- if (descriptor?.type !== 'active') {
120
- return null;
121
- }
122
- return new LocalDataTrack(descriptor.info, this);
123
- }
124
-
125
129
  /** Used by attached {@link LocalDataTrack} instances to broadcast data track packets to other
126
130
  * subscribers.
127
131
  * @internal
128
132
  */
129
- tryProcessAndSend(
133
+ async tryProcessAndSend(
130
134
  handle: DataTrackHandle,
131
- payload: Uint8Array,
132
- ): Throws<
133
- void,
134
- | DataTrackPushFrameError<DataTrackPushFrameErrorReason.Dropped>
135
- | DataTrackPushFrameError<DataTrackPushFrameErrorReason.TrackUnpublished>
135
+ frame: DataTrackFrameInternal,
136
+ ): Promise<
137
+ Throws<
138
+ void,
139
+ | DataTrackPushFrameError<DataTrackPushFrameErrorReason.Dropped>
140
+ | DataTrackPushFrameError<DataTrackPushFrameErrorReason.TrackUnpublished>
141
+ >
136
142
  > {
137
143
  const descriptor = this.getDescriptor(handle);
138
144
  if (descriptor?.type !== 'active') {
139
145
  throw DataTrackPushFrameError.trackUnpublished();
140
146
  }
141
147
 
142
- const frame: DataTrackFrame = {
143
- payload,
144
- extensions: new DataTrackExtensions(),
145
- };
148
+ if (descriptor.publishState === 'unpublished') {
149
+ throw DataTrackPushFrameError.trackUnpublished();
150
+ }
151
+ if (descriptor.publishState === 'republishing') {
152
+ throw DataTrackPushFrameError.dropped('Data track republishing');
153
+ }
146
154
 
147
155
  try {
148
- for (const packet of descriptor.pipeline.processFrame(frame)) {
149
- this.emit('packetsAvailable', { bytes: packet.toBinary() });
156
+ for await (const packet of descriptor.pipeline.processFrame(frame)) {
157
+ this.emit('packetAvailable', { bytes: packet.toBinary() });
150
158
  }
151
159
  } catch (err) {
152
160
  // NOTE: In the rust implementation this "dropped" error means something different (not enough room
@@ -155,8 +163,18 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
155
163
  }
156
164
  }
157
165
 
158
- /** Client requested to publish a track. */
159
- async publishRequest(options: DataTrackOptions, signal?: AbortSignal) {
166
+ /**
167
+ * Client requested to publish a track.
168
+ *
169
+ * If the LiveKit server is too old and doesn't support data tracks, a
170
+ * {@link DataTrackPublishError#timeout} will be thrown.
171
+ *
172
+ * @internal
173
+ **/
174
+ async publishRequest(
175
+ options: DataTrackOptions,
176
+ signal?: AbortSignal,
177
+ ): Promise<Throws<DataTrackHandle, DataTrackPublishError>> {
160
178
  const handle = this.handleAllocator.get();
161
179
  if (!handle) {
162
180
  throw DataTrackPublishError.limitReached();
@@ -195,23 +213,33 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
195
213
  };
196
214
  if (combinedSignal.aborted) {
197
215
  onAbort(); // NOTE: this rejects `completionFuture`; the next line just returns the rejection
198
- return descriptor.completionFuture.promise;
216
+ return descriptor.completionFuture.promise.then(
217
+ () => handle /* no-op, makes typescript happy */,
218
+ );
199
219
  }
200
220
  combinedSignal.addEventListener('abort', onAbort);
201
221
 
202
222
  this.emit('sfuPublishRequest', {
203
223
  handle,
204
224
  name: options.name,
205
- usesE2ee: this.encryptionProvider !== null,
225
+ usesE2ee: this.e2eeManager !== null,
206
226
  });
207
227
 
208
- const localDataTrack = await descriptor.completionFuture.promise;
228
+ await descriptor.completionFuture.promise;
209
229
  combinedSignal.removeEventListener('abort', onAbort);
210
- return localDataTrack;
230
+
231
+ this.emit('trackPublished', {
232
+ track: LocalDataTrack.withExplicitHandle(options, this, handle),
233
+ });
234
+
235
+ return handle;
211
236
  }
212
237
 
213
- /** Get information about all currently published tracks. */
214
- async queryPublished() {
238
+ /**
239
+ * Get information about all currently published tracks.
240
+ * @internal
241
+ **/
242
+ queryPublished() {
215
243
  const descriptorInfos = Array.from(this.descriptors.values())
216
244
  .filter((descriptor): descriptor is ActiveDescriptor => descriptor.type === 'active')
217
245
  .map((descriptor) => descriptor.info);
@@ -219,7 +247,10 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
219
247
  return descriptorInfos;
220
248
  }
221
249
 
222
- /** Client request to unpublish a track. */
250
+ /**
251
+ * Client request to unpublish a track.
252
+ * @internal
253
+ **/
223
254
  async unpublishRequest(handle: DataTrackHandle) {
224
255
  const descriptor = this.descriptors.get(handle);
225
256
  if (!descriptor) {
@@ -234,9 +265,14 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
234
265
  this.emit('sfuUnpublishRequest', { handle });
235
266
 
236
267
  await descriptor.unpublishingFuture.promise;
268
+
269
+ this.emit('trackUnpublished', { sid: descriptor.info.sid });
237
270
  }
238
271
 
239
- /** SFU responded to a request to publish a data track. */
272
+ /**
273
+ * SFU responded to a request to publish a data track.
274
+ * @internal
275
+ **/
240
276
  receivedSfuPublishResponse(handle: DataTrackHandle, result: SfuPublishResponseResult) {
241
277
  const descriptor = this.descriptors.get(handle);
242
278
  if (!descriptor) {
@@ -245,32 +281,41 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
245
281
  }
246
282
  this.descriptors.delete(handle);
247
283
 
248
- if (descriptor.type !== 'pending') {
249
- log.warn(`Track ${handle} already active`);
250
- return;
251
- }
252
-
253
- if (result.type === 'ok') {
254
- const info = result.data;
255
-
256
- const encryptionProvider = info.usesE2ee ? this.encryptionProvider : null;
257
- this.descriptors.set(info.pubHandle, Descriptor.active(info, encryptionProvider));
258
-
259
- const localDataTrack = this.createLocalDataTrack(info.pubHandle);
260
- if (!localDataTrack) {
261
- // @throws-transformer ignore - this should be treated as a "panic" and not be caught
262
- throw new Error(
263
- 'DataTrackOutgoingManager.handleSfuPublishResponse: localDataTrack was not created after active descriptor stored.',
264
- );
284
+ switch (descriptor.type) {
285
+ case 'pending': {
286
+ if (result.type === 'ok') {
287
+ const info = result.data;
288
+ const e2eeManager = info.usesE2ee ? this.e2eeManager : null;
289
+ this.descriptors.set(info.pubHandle, Descriptor.active(info, e2eeManager));
290
+
291
+ descriptor.completionFuture.resolve?.();
292
+ } else {
293
+ descriptor.completionFuture.reject?.(result.error);
294
+ }
295
+ return;
296
+ }
297
+ case 'active': {
298
+ if (descriptor.publishState !== 'republishing') {
299
+ log.warn(`Track ${handle} already active`);
300
+ return;
301
+ }
302
+ if (result.type === 'error') {
303
+ log.warn(`Republish failed for track ${handle}`);
304
+ return;
305
+ }
306
+
307
+ log.debug(`Track ${handle} republished`);
308
+ descriptor.info.sid = result.data.sid;
309
+ descriptor.publishState = 'published';
310
+ this.descriptors.set(descriptor.info.pubHandle, descriptor);
265
311
  }
266
-
267
- descriptor.completionFuture.resolve?.(localDataTrack);
268
- } else {
269
- descriptor.completionFuture.reject?.(result.error);
270
312
  }
271
313
  }
272
314
 
273
- /** SFU notification that a track has been unpublished. */
315
+ /**
316
+ * SFU notification that a track has been unpublished.
317
+ * @internal
318
+ **/
274
319
  receivedSfuUnpublishResponse(handle: DataTrackHandle) {
275
320
  const descriptor = this.descriptors.get(handle);
276
321
  if (!descriptor) {
@@ -284,10 +329,40 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
284
329
  return;
285
330
  }
286
331
 
332
+ descriptor.publishState = 'unpublished';
287
333
  descriptor.unpublishingFuture.resolve?.();
288
334
  }
289
335
 
290
- /** Shuts down the manager and all associated tracks. */
336
+ /** Republish all tracks.
337
+ *
338
+ * This must be sent after a full reconnect in order for existing publications
339
+ * to be recognized by the SFU. Each republished track will be assigned a new SID.
340
+ * @internal
341
+ */
342
+ sfuWillRepublishTracks() {
343
+ for (const [handle, descriptor] of this.descriptors.entries()) {
344
+ switch (descriptor.type) {
345
+ case 'pending':
346
+ // TODO: support republish for pending publications
347
+ this.descriptors.delete(handle);
348
+ descriptor.completionFuture.reject?.(DataTrackPublishError.disconnected());
349
+ break;
350
+ case 'active':
351
+ descriptor.publishState = 'republishing';
352
+
353
+ this.emit('sfuPublishRequest', {
354
+ handle: descriptor.info.pubHandle,
355
+ name: descriptor.info.name,
356
+ usesE2ee: descriptor.info.usesE2ee,
357
+ });
358
+ }
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Shuts down the manager and all associated tracks.
364
+ * @internal
365
+ **/
291
366
  async shutdown() {
292
367
  for (const descriptor of this.descriptors.values()) {
293
368
  switch (descriptor.type) {
@@ -23,10 +23,17 @@ export enum DataTrackPublishErrorReason {
23
23
 
24
24
  // NOTE: this was introduced by web / there isn't a corresponding case in the rust version.
25
25
  Cancelled = 5,
26
+
27
+ /** The name requested is not able to be used when creating the data track. */
28
+ InvalidName = 6,
29
+
30
+ /** There was an error publishing, but it was not something that could be sorted into a known
31
+ * category. */
32
+ Unknown = 7,
26
33
  }
27
34
 
28
35
  export class DataTrackPublishError<
29
- Reason extends DataTrackPublishErrorReason,
36
+ Reason extends DataTrackPublishErrorReason = DataTrackPublishErrorReason,
30
37
  > extends LivekitReasonedError<Reason> {
31
38
  readonly name = 'DataTrackPublishError';
32
39
 
@@ -34,37 +41,59 @@ export class DataTrackPublishError<
34
41
 
35
42
  reasonName: string;
36
43
 
37
- constructor(message: string, reason: Reason, options?: { cause?: unknown }) {
44
+ /** Underling message from the SFU, if one was provided */
45
+ rawMessage?: string;
46
+
47
+ constructor(message: string, reason: Reason, options?: { rawMessage?: string; cause?: unknown }) {
38
48
  super(21, message, options);
39
49
  this.reason = reason;
40
50
  this.reasonName = DataTrackPublishErrorReason[reason];
51
+ this.rawMessage = options?.rawMessage;
41
52
  }
42
53
 
43
- static notAllowed() {
54
+ static notAllowed(rawMessage?: string) {
44
55
  return new DataTrackPublishError(
45
56
  'Data track publishing unauthorized',
46
57
  DataTrackPublishErrorReason.NotAllowed,
58
+ { rawMessage },
47
59
  );
48
60
  }
49
61
 
50
- static duplicateName() {
62
+ static duplicateName(rawMessage?: string) {
51
63
  return new DataTrackPublishError(
52
64
  'Track name already taken',
53
65
  DataTrackPublishErrorReason.DuplicateName,
66
+ { rawMessage },
67
+ );
68
+ }
69
+
70
+ static invalidName(rawMessage?: string) {
71
+ return new DataTrackPublishError(
72
+ 'Track name is invalid',
73
+ DataTrackPublishErrorReason.InvalidName,
74
+ { rawMessage },
54
75
  );
55
76
  }
56
77
 
57
78
  static timeout() {
58
79
  return new DataTrackPublishError(
59
- 'Publish data track timed-out',
80
+ 'Publish data track timed-out. Does the LiveKit server support data tracks?',
60
81
  DataTrackPublishErrorReason.Timeout,
61
82
  );
62
83
  }
63
84
 
64
- static limitReached() {
85
+ static limitReached(rawMessage?: string) {
65
86
  return new DataTrackPublishError(
66
87
  'Data track publication limit reached',
67
88
  DataTrackPublishErrorReason.LimitReached,
89
+ { rawMessage },
90
+ );
91
+ }
92
+
93
+ static unknown(reason: number, message: string) {
94
+ return new DataTrackPublishError(
95
+ `Received RequestResponse for publishDataTrack, but reason was unrecognised (${reason}, ${message})`,
96
+ DataTrackPublishErrorReason.Unknown,
68
97
  );
69
98
  }
70
99
 
@@ -112,7 +141,7 @@ export class DataTrackPushFrameError<
112
141
  );
113
142
  }
114
143
 
115
- static dropped(cause: unknown) {
144
+ static dropped(cause?: unknown) {
116
145
  return new DataTrackPushFrameError('Frame was dropped', DataTrackPushFrameErrorReason.Dropped, {
117
146
  cause,
118
147
  });
@@ -1,6 +1,6 @@
1
1
  import { type Throws } from '@livekit/throws-transformer/throws';
2
- import { type EncryptedPayload, type EncryptionProvider } from '../e2ee';
3
- import { type DataTrackFrame } from '../frame';
2
+ import type { BaseE2EEManager } from '../../../e2ee/E2eeManager';
3
+ import { type DataTrackFrameInternal } from '../frame';
4
4
  import { DataTrackPacket } from '../packet';
5
5
  import { DataTrackE2eeExtension } from '../packet/extensions';
6
6
  import DataTrackPacketizer, { DataTrackPacketizerError } from '../packetizer';
@@ -9,12 +9,12 @@ import { DataTrackOutgoingPipelineError, DataTrackOutgoingPipelineErrorReason }
9
9
 
10
10
  type Options = {
11
11
  info: DataTrackInfo;
12
- encryptionProvider: EncryptionProvider | null;
12
+ e2eeManager: BaseE2EEManager | null;
13
13
  };
14
14
 
15
15
  /** Processes outgoing frames into final packets for distribution to the SFU. */
16
16
  export default class DataTrackOutgoingPipeline {
17
- private encryptionProvider: EncryptionProvider | null;
17
+ private e2eeManager: BaseE2EEManager | null;
18
18
 
19
19
  private packetizer: DataTrackPacketizer;
20
20
 
@@ -22,17 +22,21 @@ export default class DataTrackOutgoingPipeline {
22
22
  private static TRANSPORT_MTU_BYTES = 16_000;
23
23
 
24
24
  constructor(options: Options) {
25
- this.encryptionProvider = options.encryptionProvider;
25
+ this.e2eeManager = options.e2eeManager;
26
26
  this.packetizer = new DataTrackPacketizer(
27
27
  options.info.pubHandle,
28
28
  DataTrackOutgoingPipeline.TRANSPORT_MTU_BYTES,
29
29
  );
30
30
  }
31
31
 
32
- *processFrame(
33
- frame: DataTrackFrame,
34
- ): Throws<Generator<DataTrackPacket>, DataTrackOutgoingPipelineError> {
35
- const encryptedFrame = this.encryptIfNeeded(frame);
32
+ updateE2eeManager(e2eeManager: BaseE2EEManager | null) {
33
+ this.e2eeManager = e2eeManager;
34
+ }
35
+
36
+ async *processFrame(
37
+ frame: DataTrackFrameInternal,
38
+ ): Throws<AsyncGenerator<DataTrackPacket>, DataTrackOutgoingPipelineError> {
39
+ const encryptedFrame = await this.encryptIfNeeded(frame);
36
40
 
37
41
  try {
38
42
  yield* this.packetizer.packetize(encryptedFrame);
@@ -44,19 +48,21 @@ export default class DataTrackOutgoingPipeline {
44
48
  }
45
49
  }
46
50
 
47
- encryptIfNeeded(
48
- frame: DataTrackFrame,
49
- ): Throws<
50
- DataTrackFrame,
51
- DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
51
+ async encryptIfNeeded(
52
+ frame: DataTrackFrameInternal,
53
+ ): Promise<
54
+ Throws<
55
+ DataTrackFrameInternal,
56
+ DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
57
+ >
52
58
  > {
53
- if (!this.encryptionProvider) {
59
+ if (!this.e2eeManager) {
54
60
  return frame;
55
61
  }
56
62
 
57
- let encryptedResult: EncryptedPayload;
63
+ let encryptedResult;
58
64
  try {
59
- encryptedResult = this.encryptionProvider.encrypt(frame.payload);
65
+ encryptedResult = await this.e2eeManager.encryptData(frame.payload);
60
66
  } catch (err) {
61
67
  throw DataTrackOutgoingPipelineError.encryption(err);
62
68
  }
@@ -1,5 +1,6 @@
1
+ import type LocalDataTrack from '../LocalDataTrack';
1
2
  import { type DataTrackHandle } from '../handle';
2
- import { type DataTrackInfo } from '../types';
3
+ import { type DataTrackInfo, type DataTrackSid } from '../types';
3
4
  import { type DataTrackPublishError, type DataTrackPublishErrorReason } from './errors';
4
5
 
5
6
  /** Options for publishing a data track. */
@@ -15,6 +16,7 @@ export type SfuPublishResponseResult =
15
16
  error:
16
17
  | DataTrackPublishError<DataTrackPublishErrorReason.NotAllowed>
17
18
  | DataTrackPublishError<DataTrackPublishErrorReason.DuplicateName>
19
+ | DataTrackPublishError<DataTrackPublishErrorReason.InvalidName>
18
20
  | DataTrackPublishError<DataTrackPublishErrorReason.LimitReached>;
19
21
  };
20
22
 
@@ -30,7 +32,14 @@ export type EventSfuUnpublishRequest = {
30
32
  handle: DataTrackHandle;
31
33
  };
32
34
 
33
- /** Serialized packets are ready to be sent over the transport. */
34
- export type EventPacketsAvailable = {
35
+ /** A serialized packet is ready to be sent over the transport. */
36
+ export type EventPacketAvailable = {
35
37
  bytes: Uint8Array;
36
38
  };
39
+
40
+ /** A track has been created by a local participant and is available to be
41
+ * subscribed to. */
42
+ export type EventTrackPublished = { track: LocalDataTrack };
43
+
44
+ /** A track has been unpublished by a remote participant and can no longer be subscribed to. */
45
+ export type EventTrackUnpublished = { sid: DataTrackSid };
@@ -1,6 +1,6 @@
1
1
  import { type Throws } from '@livekit/throws-transformer/throws';
2
2
  import { coerceToDataView } from '../utils';
3
- import { EXT_TAG_PADDING, U8_LENGTH_BYTES, U16_LENGTH_BYTES, U64_LENGTH_BYTES } from './constants';
3
+ import { EXT_TAG_PADDING, U8_LENGTH_BYTES, U64_LENGTH_BYTES } from './constants';
4
4
  import { DataTrackDeserializeError, DataTrackDeserializeErrorReason } from './errors';
5
5
  import Serializable from './serializable';
6
6
 
@@ -29,8 +29,8 @@ export class DataTrackUserTimestampExtension extends DataTrackExtension {
29
29
 
30
30
  toBinaryLengthBytes(): number {
31
31
  return (
32
- U16_LENGTH_BYTES /* tag */ +
33
- U16_LENGTH_BYTES /* length */ +
32
+ U8_LENGTH_BYTES /* tag */ +
33
+ U8_LENGTH_BYTES /* length */ +
34
34
  DataTrackUserTimestampExtension.lengthBytes
35
35
  );
36
36
  }
@@ -38,12 +38,11 @@ export class DataTrackUserTimestampExtension extends DataTrackExtension {
38
38
  toBinaryInto(dataView: DataView): Throws<number, never> {
39
39
  let byteIndex = 0;
40
40
 
41
- dataView.setUint16(byteIndex, DataTrackUserTimestampExtension.tag);
42
- byteIndex += U16_LENGTH_BYTES;
41
+ dataView.setUint8(byteIndex, DataTrackUserTimestampExtension.tag);
42
+ byteIndex += U8_LENGTH_BYTES;
43
43
 
44
- const rtpOrientedLength = DataTrackUserTimestampExtension.lengthBytes - 1;
45
- dataView.setUint16(byteIndex, rtpOrientedLength);
46
- byteIndex += U16_LENGTH_BYTES;
44
+ dataView.setUint8(byteIndex, DataTrackUserTimestampExtension.lengthBytes);
45
+ byteIndex += U8_LENGTH_BYTES;
47
46
 
48
47
  dataView.setBigUint64(byteIndex, this.timestamp);
49
48
  byteIndex += U64_LENGTH_BYTES;
@@ -86,21 +85,18 @@ export class DataTrackE2eeExtension extends DataTrackExtension {
86
85
 
87
86
  toBinaryLengthBytes(): number {
88
87
  return (
89
- U16_LENGTH_BYTES /* tag */ +
90
- U16_LENGTH_BYTES /* length */ +
91
- DataTrackE2eeExtension.lengthBytes
88
+ U8_LENGTH_BYTES /* tag */ + U8_LENGTH_BYTES /* length */ + DataTrackE2eeExtension.lengthBytes
92
89
  );
93
90
  }
94
91
 
95
92
  toBinaryInto(dataView: DataView): Throws<number, never> {
96
93
  let byteIndex = 0;
97
94
 
98
- dataView.setUint16(byteIndex, DataTrackE2eeExtension.tag);
99
- byteIndex += U16_LENGTH_BYTES;
95
+ dataView.setUint8(byteIndex, DataTrackE2eeExtension.tag);
96
+ byteIndex += U8_LENGTH_BYTES;
100
97
 
101
- const rtpOrientedLength = DataTrackE2eeExtension.lengthBytes - 1;
102
- dataView.setUint16(byteIndex, rtpOrientedLength);
103
- byteIndex += U16_LENGTH_BYTES;
98
+ dataView.setUint8(byteIndex, DataTrackE2eeExtension.lengthBytes);
99
+ byteIndex += U8_LENGTH_BYTES;
104
100
 
105
101
  dataView.setUint8(byteIndex, this.keyIndex);
106
102
  byteIndex += U8_LENGTH_BYTES;
@@ -194,13 +190,12 @@ export class DataTrackExtensions extends Serializable {
194
190
  let e2ee: DataTrackE2eeExtension | undefined;
195
191
 
196
192
  let byteIndex = 0;
197
- while (dataView.byteLength - byteIndex >= U16_LENGTH_BYTES + U16_LENGTH_BYTES) {
198
- const tag = dataView.getUint16(byteIndex);
199
- byteIndex += U16_LENGTH_BYTES;
193
+ while (dataView.byteLength - byteIndex >= U8_LENGTH_BYTES + U8_LENGTH_BYTES) {
194
+ const tag = dataView.getUint8(byteIndex);
195
+ byteIndex += U8_LENGTH_BYTES;
200
196
 
201
- const rtpOrientedLength = dataView.getUint16(byteIndex);
202
- const lengthBytes = rtpOrientedLength + 1;
203
- byteIndex += U16_LENGTH_BYTES;
197
+ const lengthBytes = dataView.getUint8(byteIndex);
198
+ byteIndex += U8_LENGTH_BYTES;
204
199
 
205
200
  if (tag === EXT_TAG_PADDING) {
206
201
  // Skip padding