livekit-client 2.17.3 → 2.18.1

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 (183) 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 +7823 -5772
  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/e2ee/constants.d.ts.map +1 -1
  12. package/dist/src/e2ee/types.d.ts +6 -0
  13. package/dist/src/e2ee/types.d.ts.map +1 -1
  14. package/dist/src/e2ee/utils.d.ts +2 -1
  15. package/dist/src/e2ee/utils.d.ts.map +1 -1
  16. package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
  17. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  18. package/dist/src/index.d.ts +5 -4
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/room/PCTransport.d.ts +5 -0
  21. package/dist/src/room/PCTransport.d.ts.map +1 -1
  22. package/dist/src/room/PCTransportManager.d.ts +1 -1
  23. package/dist/src/room/PCTransportManager.d.ts.map +1 -1
  24. package/dist/src/room/RTCEngine.d.ts +27 -9
  25. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  26. package/dist/src/room/Room.d.ts +13 -3
  27. package/dist/src/room/Room.d.ts.map +1 -1
  28. package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
  29. package/dist/src/room/data-track/LocalDataTrack.d.ts +48 -0
  30. package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -0
  31. package/dist/src/room/data-track/RemoteDataTrack.d.ts +46 -0
  32. package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -0
  33. package/dist/src/room/data-track/depacketizer.d.ts +6 -6
  34. package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
  35. package/dist/src/room/data-track/frame.d.ts +14 -0
  36. package/dist/src/room/data-track/frame.d.ts.map +1 -1
  37. package/dist/src/room/data-track/handle.d.ts +2 -2
  38. package/dist/src/room/data-track/handle.d.ts.map +1 -1
  39. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +104 -0
  40. package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -0
  41. package/dist/src/room/data-track/incoming/errors.d.ts +24 -0
  42. package/dist/src/room/data-track/incoming/errors.d.ts.map +1 -0
  43. package/dist/src/room/data-track/incoming/pipeline.d.ts +38 -0
  44. package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -0
  45. package/dist/src/room/data-track/incoming/types.d.ts +20 -0
  46. package/dist/src/room/data-track/incoming/types.d.ts.map +1 -0
  47. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +63 -28
  48. package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
  49. package/dist/src/room/data-track/outgoing/errors.d.ts +20 -10
  50. package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
  51. package/dist/src/room/data-track/outgoing/pipeline.d.ts +9 -8
  52. package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
  53. package/dist/src/room/data-track/outgoing/types.d.ts +16 -7
  54. package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
  55. package/dist/src/room/data-track/packet/errors.d.ts +2 -4
  56. package/dist/src/room/data-track/packet/errors.d.ts.map +1 -1
  57. package/dist/src/room/data-track/packet/extensions.d.ts +4 -4
  58. package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
  59. package/dist/src/room/data-track/packet/index.d.ts +5 -5
  60. package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
  61. package/dist/src/room/data-track/packet/serializable.d.ts +4 -4
  62. package/dist/src/room/data-track/packet/serializable.d.ts.map +1 -1
  63. package/dist/src/room/data-track/packetizer.d.ts +6 -6
  64. package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
  65. package/dist/src/room/data-track/track-interfaces.d.ts +23 -0
  66. package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -0
  67. package/dist/src/room/data-track/types.d.ts +15 -0
  68. package/dist/src/room/data-track/types.d.ts.map +1 -0
  69. package/dist/src/room/events.d.ts +24 -3
  70. package/dist/src/room/events.d.ts.map +1 -1
  71. package/dist/src/room/participant/LocalParticipant.d.ts +11 -1
  72. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  73. package/dist/src/room/participant/RemoteParticipant.d.ts +14 -1
  74. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  75. package/dist/src/room/token-source/TokenSource.d.ts +1 -1
  76. package/dist/src/room/token-source/TokenSource.d.ts.map +1 -1
  77. package/dist/src/room/token-source/types.d.ts +1 -0
  78. package/dist/src/room/token-source/types.d.ts.map +1 -1
  79. package/dist/src/room/utils.d.ts +2 -1
  80. package/dist/src/room/utils.d.ts.map +1 -1
  81. package/dist/src/utils/abort-signal-polyfill.d.ts +13 -0
  82. package/dist/src/utils/abort-signal-polyfill.d.ts.map +1 -0
  83. package/dist/src/utils/deferrable-map.d.ts +32 -0
  84. package/dist/src/utils/deferrable-map.d.ts.map +1 -0
  85. package/dist/src/utils/subscribeToEvents.d.ts +3 -0
  86. package/dist/src/utils/subscribeToEvents.d.ts.map +1 -1
  87. package/dist/ts4.2/api/SignalClient.d.ts +12 -4
  88. package/dist/ts4.2/e2ee/types.d.ts +6 -0
  89. package/dist/ts4.2/e2ee/utils.d.ts +2 -1
  90. package/dist/ts4.2/index.d.ts +5 -4
  91. package/dist/ts4.2/room/PCTransport.d.ts +5 -0
  92. package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
  93. package/dist/ts4.2/room/RTCEngine.d.ts +27 -9
  94. package/dist/ts4.2/room/Room.d.ts +13 -3
  95. package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +48 -0
  96. package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +46 -0
  97. package/dist/ts4.2/room/data-track/depacketizer.d.ts +6 -6
  98. package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
  99. package/dist/ts4.2/room/data-track/handle.d.ts +2 -2
  100. package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +110 -0
  101. package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -0
  102. package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +38 -0
  103. package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -0
  104. package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +63 -29
  105. package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +20 -10
  106. package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +9 -8
  107. package/dist/ts4.2/room/data-track/outgoing/types.d.ts +16 -7
  108. package/dist/ts4.2/room/data-track/packet/errors.d.ts +2 -4
  109. package/dist/ts4.2/room/data-track/packet/extensions.d.ts +4 -4
  110. package/dist/ts4.2/room/data-track/packet/index.d.ts +5 -6
  111. package/dist/ts4.2/room/data-track/packet/serializable.d.ts +4 -4
  112. package/dist/ts4.2/room/data-track/packetizer.d.ts +6 -6
  113. package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -0
  114. package/dist/ts4.2/room/data-track/types.d.ts +15 -0
  115. package/dist/ts4.2/room/events.d.ts +24 -3
  116. package/dist/ts4.2/room/participant/LocalParticipant.d.ts +11 -1
  117. package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +14 -1
  118. package/dist/ts4.2/room/token-source/TokenSource.d.ts +1 -1
  119. package/dist/ts4.2/room/token-source/types.d.ts +1 -0
  120. package/dist/ts4.2/room/utils.d.ts +2 -1
  121. package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -0
  122. package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
  123. package/dist/ts4.2/utils/subscribeToEvents.d.ts +3 -0
  124. package/package.json +4 -2
  125. package/src/api/SignalClient.test.ts +9 -4
  126. package/src/api/SignalClient.ts +116 -9
  127. package/src/e2ee/constants.ts +1 -0
  128. package/src/e2ee/types.ts +6 -0
  129. package/src/e2ee/utils.ts +4 -3
  130. package/src/e2ee/worker/DataCryptor.ts +1 -4
  131. package/src/e2ee/worker/FrameCryptor.ts +1 -4
  132. package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
  133. package/src/index.ts +6 -4
  134. package/src/room/PCTransport.ts +41 -1
  135. package/src/room/PCTransportManager.ts +1 -1
  136. package/src/room/RTCEngine.ts +274 -112
  137. package/src/room/Room.ts +152 -15
  138. package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +9 -9
  139. package/src/room/data-track/LocalDataTrack.ts +126 -0
  140. package/src/room/data-track/RemoteDataTrack.ts +80 -0
  141. package/src/room/data-track/depacketizer.ts +23 -26
  142. package/src/room/data-track/frame.ts +28 -2
  143. package/src/room/data-track/handle.ts +2 -8
  144. package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +555 -0
  145. package/src/room/data-track/incoming/IncomingDataTrackManager.ts +589 -0
  146. package/src/room/data-track/incoming/errors.ts +57 -0
  147. package/src/room/data-track/incoming/pipeline.ts +121 -0
  148. package/src/room/data-track/incoming/types.ts +22 -0
  149. package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +240 -27
  150. package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +165 -84
  151. package/src/room/data-track/outgoing/errors.ts +40 -11
  152. package/src/room/data-track/outgoing/pipeline.ts +25 -23
  153. package/src/room/data-track/outgoing/types.ts +14 -6
  154. package/src/room/data-track/packet/errors.ts +2 -14
  155. package/src/room/data-track/packet/extensions.ts +21 -26
  156. package/src/room/data-track/packet/index.test.ts +22 -33
  157. package/src/room/data-track/packet/index.ts +4 -6
  158. package/src/room/data-track/packet/serializable.ts +4 -4
  159. package/src/room/data-track/packetizer.test.ts +2 -2
  160. package/src/room/data-track/packetizer.ts +7 -10
  161. package/src/room/data-track/track-interfaces.ts +53 -0
  162. package/src/room/data-track/types.ts +31 -0
  163. package/src/room/events.ts +26 -1
  164. package/src/room/participant/LocalParticipant.ts +57 -6
  165. package/src/room/participant/RemoteParticipant.ts +26 -1
  166. package/src/room/token-source/TokenSource.ts +8 -2
  167. package/src/room/token-source/types.ts +4 -0
  168. package/src/room/utils.ts +5 -1
  169. package/src/utils/abort-signal-polyfill.ts +63 -0
  170. package/src/utils/deferrable-map.ts +109 -0
  171. package/src/utils/subscribeToEvents.ts +18 -1
  172. package/dist/src/room/data-track/e2ee.d.ts +0 -12
  173. package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
  174. package/dist/src/room/data-track/track.d.ts +0 -30
  175. package/dist/src/room/data-track/track.d.ts.map +0 -1
  176. package/dist/src/utils/throws.d.ts +0 -36
  177. package/dist/src/utils/throws.d.ts.map +0 -1
  178. package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
  179. package/dist/ts4.2/room/data-track/track.d.ts +0 -30
  180. package/dist/ts4.2/utils/throws.d.ts +0 -39
  181. package/src/room/data-track/e2ee.ts +0 -14
  182. package/src/room/data-track/track.ts +0 -50
  183. package/src/utils/throws.ts +0 -42
@@ -1,25 +1,27 @@
1
1
  import { EventEmitter } from 'events';
2
+ import type { Throws } from '@livekit/throws-transformer/throws';
2
3
  import type TypedEmitter from 'typed-emitter';
4
+ import type { BaseE2EEManager } from '../../../e2ee/E2eeManager';
3
5
  import { LoggerNames, getLogger } from '../../../logger';
4
- import type { Throws } from '../../../utils/throws';
6
+ import { abortSignalAny, abortSignalTimeout } from '../../../utils/abort-signal-polyfill';
5
7
  import { Future } from '../../utils';
6
- import { type EncryptionProvider } from '../e2ee';
7
- import type { DataTrackFrame } from '../frame';
8
+ import LocalDataTrack from '../LocalDataTrack';
9
+ import type { DataTrackFrameInternal } from '../frame';
8
10
  import { DataTrackHandle, DataTrackHandleAllocator } from '../handle';
9
- import { DataTrackExtensions } from '../packet/extensions';
10
- import { type DataTrackInfo, LocalDataTrack } from '../track';
11
+ import { type DataTrackInfo } from '../types';
11
12
  import {
12
13
  DataTrackPublishError,
13
- DataTrackPublishErrorReason,
14
14
  DataTrackPushFrameError,
15
15
  DataTrackPushFrameErrorReason,
16
16
  } from './errors';
17
17
  import DataTrackOutgoingPipeline from './pipeline';
18
18
  import {
19
19
  type DataTrackOptions,
20
- type OutputEventPacketsAvailable,
21
- type OutputEventSfuPublishRequest,
22
- type OutputEventSfuUnpublishRequest,
20
+ type EventPacketAvailable,
21
+ type EventSfuPublishRequest,
22
+ type EventSfuUnpublishRequest,
23
+ type EventTrackPublished,
24
+ type EventTrackUnpublished,
23
25
  type SfuPublishResponseResult,
24
26
  } from './types';
25
27
 
@@ -27,20 +29,15 @@ const log = getLogger(LoggerNames.DataTracks);
27
29
 
28
30
  export type PendingDescriptor = {
29
31
  type: 'pending';
30
- completionFuture: Future<
31
- LocalDataTrack,
32
- | DataTrackPublishError<DataTrackPublishErrorReason.NotAllowed>
33
- | DataTrackPublishError<DataTrackPublishErrorReason.DuplicateName>
34
- | DataTrackPublishError<DataTrackPublishErrorReason.Timeout>
35
- | DataTrackPublishError<DataTrackPublishErrorReason.LimitReached>
36
- | DataTrackPublishError<DataTrackPublishErrorReason.Disconnected>
37
- | DataTrackPublishError<DataTrackPublishErrorReason.Cancelled>
38
- >;
32
+ /** Resolves when the descriptor is fully published. */
33
+ completionFuture: Future<void, DataTrackPublishError>;
39
34
  };
40
35
  export type ActiveDescriptor = {
41
36
  type: 'active';
42
37
  info: DataTrackInfo;
43
38
 
39
+ publishState: 'published' | 'republishing' | 'unpublished';
40
+
44
41
  pipeline: DataTrackOutgoingPipeline;
45
42
 
46
43
  /** Resolves when the descriptor is unpublished. */
@@ -55,11 +52,12 @@ export const Descriptor = {
55
52
  completionFuture: new Future(),
56
53
  };
57
54
  },
58
- active(info: DataTrackInfo, encryptionProvider: EncryptionProvider | null): ActiveDescriptor {
55
+ active(info: DataTrackInfo, e2eeManager: BaseE2EEManager | null): ActiveDescriptor {
59
56
  return {
60
57
  type: 'active',
61
58
  info,
62
- pipeline: new DataTrackOutgoingPipeline({ info, encryptionProvider }),
59
+ publishState: 'published',
60
+ pipeline: new DataTrackOutgoingPipeline({ info, e2eeManager }),
63
61
  unpublishingFuture: new Future(),
64
62
  };
65
63
  },
@@ -67,35 +65,39 @@ export const Descriptor = {
67
65
 
68
66
  export type DataTrackOutgoingManagerCallbacks = {
69
67
  /** Request sent to the SFU to publish a track. */
70
- sfuPublishRequest: (event: OutputEventSfuPublishRequest) => void;
68
+ sfuPublishRequest: (event: EventSfuPublishRequest) => void;
71
69
  /** Request sent to the SFU to unpublish a track. */
72
- sfuUnpublishRequest: (event: OutputEventSfuUnpublishRequest) => void;
73
- /** Serialized packets are ready to be sent over the transport. */
74
- packetsAvailable: (event: OutputEventPacketsAvailable) => void;
70
+ sfuUnpublishRequest: (event: EventSfuUnpublishRequest) => 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;
75
77
  };
76
78
 
77
- type DataTrackLocalManagerOptions = {
79
+ type OutgoingDataTrackManagerOptions = {
78
80
  /**
79
81
  * Provider to use for encrypting outgoing frame payloads.
80
82
  *
81
- * 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.
82
84
  */
83
- encryptionProvider?: EncryptionProvider;
85
+ e2eeManager?: BaseE2EEManager;
84
86
  };
85
87
 
86
88
  /** How long to wait when attempting to publish before timing out. */
87
89
  const PUBLISH_TIMEOUT_MILLISECONDS = 10_000;
88
90
 
89
91
  export default class OutgoingDataTrackManager extends (EventEmitter as new () => TypedEmitter<DataTrackOutgoingManagerCallbacks>) {
90
- private encryptionProvider: EncryptionProvider | null;
92
+ private e2eeManager: BaseE2EEManager | null;
91
93
 
92
94
  private handleAllocator = new DataTrackHandleAllocator();
93
95
 
94
96
  private descriptors = new Map<DataTrackHandle, Descriptor>();
95
97
 
96
- constructor(options?: DataTrackLocalManagerOptions) {
98
+ constructor(options?: OutgoingDataTrackManagerOptions) {
97
99
  super();
98
- this.encryptionProvider = options?.encryptionProvider ?? null;
100
+ this.e2eeManager = options?.e2eeManager ?? null;
99
101
  }
100
102
 
101
103
  static withDescriptors(descriptors: Map<DataTrackHandle, Descriptor>) {
@@ -104,6 +106,18 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
104
106
  return manager;
105
107
  }
106
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
+
107
121
  /**
108
122
  * Used by attached {@link LocalDataTrack} instances to query their associated descriptor info.
109
123
  * @internal
@@ -112,39 +126,35 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
112
126
  return this.descriptors.get(handle) ?? null;
113
127
  }
114
128
 
115
- createLocalDataTrack(handle: DataTrackHandle) {
116
- const descriptor = this.getDescriptor(handle);
117
- if (descriptor?.type !== 'active') {
118
- return null;
119
- }
120
- return new LocalDataTrack(descriptor.info, this);
121
- }
122
-
123
129
  /** Used by attached {@link LocalDataTrack} instances to broadcast data track packets to other
124
130
  * subscribers.
125
131
  * @internal
126
132
  */
127
- tryProcessAndSend(
133
+ async tryProcessAndSend(
128
134
  handle: DataTrackHandle,
129
- payload: Uint8Array,
130
- ): Throws<
131
- void,
132
- | DataTrackPushFrameError<DataTrackPushFrameErrorReason.Dropped>
133
- | DataTrackPushFrameError<DataTrackPushFrameErrorReason.TrackUnpublished>
135
+ frame: DataTrackFrameInternal,
136
+ ): Promise<
137
+ Throws<
138
+ void,
139
+ | DataTrackPushFrameError<DataTrackPushFrameErrorReason.Dropped>
140
+ | DataTrackPushFrameError<DataTrackPushFrameErrorReason.TrackUnpublished>
141
+ >
134
142
  > {
135
143
  const descriptor = this.getDescriptor(handle);
136
144
  if (descriptor?.type !== 'active') {
137
145
  throw DataTrackPushFrameError.trackUnpublished();
138
146
  }
139
147
 
140
- const frame: DataTrackFrame = {
141
- payload,
142
- extensions: new DataTrackExtensions(),
143
- };
148
+ if (descriptor.publishState === 'unpublished') {
149
+ throw DataTrackPushFrameError.trackUnpublished();
150
+ }
151
+ if (descriptor.publishState === 'republishing') {
152
+ throw DataTrackPushFrameError.dropped('Data track republishing');
153
+ }
144
154
 
145
155
  try {
146
- for (const packet of descriptor.pipeline.processFrame(frame)) {
147
- this.emit('packetsAvailable', { bytes: packet.toBinary() });
156
+ for await (const packet of descriptor.pipeline.processFrame(frame)) {
157
+ this.emit('packetAvailable', { bytes: packet.toBinary() });
148
158
  }
149
159
  } catch (err) {
150
160
  // NOTE: In the rust implementation this "dropped" error means something different (not enough room
@@ -153,15 +163,25 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
153
163
  }
154
164
  }
155
165
 
156
- /** Client requested to publish a track. */
157
- 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>> {
158
178
  const handle = this.handleAllocator.get();
159
179
  if (!handle) {
160
180
  throw DataTrackPublishError.limitReached();
161
181
  }
162
182
 
163
- const timeoutSignal = AbortSignal.timeout(PUBLISH_TIMEOUT_MILLISECONDS);
164
- const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
183
+ const timeoutSignal = abortSignalTimeout(PUBLISH_TIMEOUT_MILLISECONDS);
184
+ const combinedSignal = signal ? abortSignalAny([signal, timeoutSignal]) : timeoutSignal;
165
185
 
166
186
  if (this.descriptors.has(handle)) {
167
187
  // @throws-transformer ignore - this should be treated as a "panic" and not be caught
@@ -191,21 +211,35 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
191
211
  );
192
212
  }
193
213
  };
214
+ if (combinedSignal.aborted) {
215
+ onAbort(); // NOTE: this rejects `completionFuture`; the next line just returns the rejection
216
+ return descriptor.completionFuture.promise.then(
217
+ () => handle /* no-op, makes typescript happy */,
218
+ );
219
+ }
194
220
  combinedSignal.addEventListener('abort', onAbort);
195
221
 
196
222
  this.emit('sfuPublishRequest', {
197
223
  handle,
198
224
  name: options.name,
199
- usesE2ee: this.encryptionProvider !== null,
225
+ usesE2ee: this.e2eeManager !== null,
200
226
  });
201
227
 
202
- const localDataTrack = await descriptor.completionFuture.promise;
228
+ await descriptor.completionFuture.promise;
203
229
  combinedSignal.removeEventListener('abort', onAbort);
204
- return localDataTrack;
230
+
231
+ this.emit('trackPublished', {
232
+ track: LocalDataTrack.withExplicitHandle(options, this, handle),
233
+ });
234
+
235
+ return handle;
205
236
  }
206
237
 
207
- /** Get information about all currently published tracks. */
208
- async queryPublished() {
238
+ /**
239
+ * Get information about all currently published tracks.
240
+ * @internal
241
+ **/
242
+ queryPublished() {
209
243
  const descriptorInfos = Array.from(this.descriptors.values())
210
244
  .filter((descriptor): descriptor is ActiveDescriptor => descriptor.type === 'active')
211
245
  .map((descriptor) => descriptor.info);
@@ -213,7 +247,10 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
213
247
  return descriptorInfos;
214
248
  }
215
249
 
216
- /** Client request to unpublish a track. */
250
+ /**
251
+ * Client request to unpublish a track.
252
+ * @internal
253
+ **/
217
254
  async unpublishRequest(handle: DataTrackHandle) {
218
255
  const descriptor = this.descriptors.get(handle);
219
256
  if (!descriptor) {
@@ -228,9 +265,14 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
228
265
  this.emit('sfuUnpublishRequest', { handle });
229
266
 
230
267
  await descriptor.unpublishingFuture.promise;
268
+
269
+ this.emit('trackUnpublished', { sid: descriptor.info.sid });
231
270
  }
232
271
 
233
- /** 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
+ **/
234
276
  receivedSfuPublishResponse(handle: DataTrackHandle, result: SfuPublishResponseResult) {
235
277
  const descriptor = this.descriptors.get(handle);
236
278
  if (!descriptor) {
@@ -239,32 +281,41 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
239
281
  }
240
282
  this.descriptors.delete(handle);
241
283
 
242
- if (descriptor.type !== 'pending') {
243
- log.warn(`Track ${handle} already active`);
244
- return;
245
- }
246
-
247
- if (result.type === 'ok') {
248
- const info = result.data;
249
-
250
- const encryptionProvider = info.usesE2ee ? this.encryptionProvider : null;
251
- this.descriptors.set(info.pubHandle, Descriptor.active(info, encryptionProvider));
252
-
253
- const localDataTrack = this.createLocalDataTrack(info.pubHandle);
254
- if (!localDataTrack) {
255
- // @throws-transformer ignore - this should be treated as a "panic" and not be caught
256
- throw new Error(
257
- 'DataTrackOutgoingManager.handleSfuPublishResponse: localDataTrack was not created after active descriptor stored.',
258
- );
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);
259
311
  }
260
-
261
- descriptor.completionFuture.resolve?.(localDataTrack);
262
- } else {
263
- descriptor.completionFuture.reject?.(result.error);
264
312
  }
265
313
  }
266
314
 
267
- /** SFU notification that a track has been unpublished. */
315
+ /**
316
+ * SFU notification that a track has been unpublished.
317
+ * @internal
318
+ **/
268
319
  receivedSfuUnpublishResponse(handle: DataTrackHandle) {
269
320
  const descriptor = this.descriptors.get(handle);
270
321
  if (!descriptor) {
@@ -278,10 +329,40 @@ export default class OutgoingDataTrackManager extends (EventEmitter as new () =>
278
329
  return;
279
330
  }
280
331
 
332
+ descriptor.publishState = 'unpublished';
281
333
  descriptor.unpublishingFuture.resolve?.();
282
334
  }
283
335
 
284
- /** 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
+ **/
285
366
  async shutdown() {
286
367
  for (const descriptor of this.descriptors.values()) {
287
368
  switch (descriptor.type) {
@@ -1,5 +1,5 @@
1
1
  import { LivekitReasonedError } from '../../errors';
2
- import { DataTrackPacketizerError, DataTrackPacketizerReason } from '../packetizer';
2
+ import { DataTrackPacketizerError } from '../packetizer';
3
3
 
4
4
  export enum DataTrackPublishErrorReason {
5
5
  /**
@@ -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
 
@@ -91,7 +120,7 @@ export enum DataTrackPushFrameErrorReason {
91
120
  }
92
121
 
93
122
  export class DataTrackPushFrameError<
94
- Reason extends DataTrackPushFrameErrorReason,
123
+ Reason extends DataTrackPushFrameErrorReason = DataTrackPushFrameErrorReason,
95
124
  > extends LivekitReasonedError<Reason> {
96
125
  readonly name = 'DataTrackPushFrameError';
97
126
 
@@ -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
  });
@@ -125,7 +154,7 @@ export enum DataTrackOutgoingPipelineErrorReason {
125
154
  }
126
155
 
127
156
  export class DataTrackOutgoingPipelineError<
128
- Reason extends DataTrackOutgoingPipelineErrorReason,
157
+ Reason extends DataTrackOutgoingPipelineErrorReason = DataTrackOutgoingPipelineErrorReason,
129
158
  > extends LivekitReasonedError<Reason> {
130
159
  readonly name = 'DataTrackOutgoingPipelineError';
131
160
 
@@ -139,7 +168,7 @@ export class DataTrackOutgoingPipelineError<
139
168
  this.reasonName = DataTrackOutgoingPipelineErrorReason[reason];
140
169
  }
141
170
 
142
- static packetizer(cause: DataTrackPacketizerError<DataTrackPacketizerReason.MtuTooShort>) {
171
+ static packetizer(cause: DataTrackPacketizerError) {
143
172
  return new DataTrackOutgoingPipelineError(
144
173
  'Error packetizing frame',
145
174
  DataTrackOutgoingPipelineErrorReason.Packetizer,
@@ -1,20 +1,20 @@
1
- import { type Throws } from '../../../utils/throws';
2
- import { type EncryptedPayload, type EncryptionProvider } from '../e2ee';
3
- import { type DataTrackFrame } from '../frame';
1
+ import { type Throws } from '@livekit/throws-transformer/throws';
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';
7
- import type { DataTrackInfo } from '../track';
7
+ import type { DataTrackInfo } from '../types';
8
8
  import { DataTrackOutgoingPipelineError, DataTrackOutgoingPipelineErrorReason } from './errors';
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,21 +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<
35
- Generator<DataTrackPacket>,
36
- | DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Packetizer>
37
- | DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
38
- > {
39
- 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);
40
40
 
41
41
  try {
42
42
  yield* this.packetizer.packetize(encryptedFrame);
@@ -48,19 +48,21 @@ export default class DataTrackOutgoingPipeline {
48
48
  }
49
49
  }
50
50
 
51
- encryptIfNeeded(
52
- frame: DataTrackFrame,
53
- ): Throws<
54
- DataTrackFrame,
55
- DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
51
+ async encryptIfNeeded(
52
+ frame: DataTrackFrameInternal,
53
+ ): Promise<
54
+ Throws<
55
+ DataTrackFrameInternal,
56
+ DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
57
+ >
56
58
  > {
57
- if (!this.encryptionProvider) {
59
+ if (!this.e2eeManager) {
58
60
  return frame;
59
61
  }
60
62
 
61
- let encryptedResult: EncryptedPayload;
63
+ let encryptedResult;
62
64
  try {
63
- encryptedResult = this.encryptionProvider.encrypt(frame.payload);
65
+ encryptedResult = await this.e2eeManager.encryptData(frame.payload);
64
66
  } catch (err) {
65
67
  throw DataTrackOutgoingPipelineError.encryption(err);
66
68
  }
@@ -1,5 +1,6 @@
1
+ import type LocalDataTrack from '../LocalDataTrack';
1
2
  import { type DataTrackHandle } from '../handle';
2
- import { type DataTrackInfo } from '../track';
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,23 +16,30 @@ 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
 
21
23
  /** Request sent to the SFU to publish a track. */
22
- export type OutputEventSfuPublishRequest = {
24
+ export type EventSfuPublishRequest = {
23
25
  handle: DataTrackHandle;
24
26
  name: string;
25
27
  usesE2ee: boolean;
26
28
  };
27
29
 
28
30
  /** Request sent to the SFU to unpublish a track. */
29
- export type OutputEventSfuUnpublishRequest = {
31
+ 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 OutputEventPacketsAvailable = {
35
+ /** A serialized packet is ready to be sent over the transport. */
36
+ export type EventPacketAvailable = {
35
37
  bytes: Uint8Array;
36
- signal?: AbortSignal;
37
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 };