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.
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +8 -7
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +8026 -5883
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +12 -4
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +6 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +2 -1
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +5 -0
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts +1 -1
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +28 -11
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +14 -3
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts +28 -5
- package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/RemoteDataTrack.d.ts +5 -5
- package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -1
- package/dist/src/room/data-track/depacketizer.d.ts +4 -4
- package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
- package/dist/src/room/data-track/frame.d.ts +14 -0
- package/dist/src/room/data-track/frame.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +19 -11
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/pipeline.d.ts +6 -5
- package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/errors.d.ts +16 -6
- package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/pipeline.d.ts +7 -6
- package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/types.d.ts +14 -4
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packetizer.d.ts +4 -4
- package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
- package/dist/src/room/data-track/track-interfaces.d.ts +1 -1
- package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -1
- package/dist/src/room/data-track/types.d.ts +6 -1
- package/dist/src/room/data-track/types.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +24 -3
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +12 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +13 -0
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/deferrable-map.d.ts +32 -0
- package/dist/src/utils/deferrable-map.d.ts.map +1 -0
- package/dist/src/utils/serializer.d.ts +48 -0
- package/dist/src/utils/serializer.d.ts.map +1 -0
- package/dist/ts4.2/api/SignalClient.d.ts +12 -4
- package/dist/ts4.2/connectionHelper/ConnectionCheck.d.ts +2 -1
- package/dist/ts4.2/e2ee/types.d.ts +6 -0
- package/dist/ts4.2/e2ee/utils.d.ts +2 -1
- package/dist/ts4.2/index.d.ts +7 -4
- package/dist/ts4.2/room/PCTransport.d.ts +5 -0
- package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +28 -11
- package/dist/ts4.2/room/Room.d.ts +14 -3
- package/dist/ts4.2/room/data-stream/incoming/IncomingDataStreamManager.d.ts +5 -1
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +27 -4
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +4 -4
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +21 -10
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +6 -5
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +57 -23
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +16 -6
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +7 -6
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +14 -4
- package/dist/ts4.2/room/data-track/packetizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +1 -1
- package/dist/ts4.2/room/data-track/types.d.ts +6 -1
- package/dist/ts4.2/room/events.d.ts +24 -3
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +12 -1
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +13 -0
- package/dist/ts4.2/room/utils.d.ts +1 -0
- package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
- package/dist/ts4.2/utils/serializer.d.ts +48 -0
- package/package.json +1 -1
- package/src/api/SignalClient.test.ts +9 -4
- package/src/api/SignalClient.ts +116 -9
- package/src/connectionHelper/ConnectionCheck.ts +1 -1
- package/src/e2ee/constants.ts +1 -0
- package/src/e2ee/types.ts +6 -0
- package/src/e2ee/utils.ts +4 -3
- package/src/e2ee/worker/DataCryptor.ts +1 -4
- package/src/e2ee/worker/FrameCryptor.ts +1 -4
- package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
- package/src/index.ts +13 -4
- package/src/room/PCTransport.ts +41 -1
- package/src/room/PCTransportManager.ts +1 -1
- package/src/room/RTCEngine.ts +312 -125
- package/src/room/Room.ts +168 -35
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +26 -2
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +7 -7
- package/src/room/data-track/LocalDataTrack.ts +83 -10
- package/src/room/data-track/RemoteDataTrack.ts +7 -9
- package/src/room/data-track/depacketizer.ts +21 -12
- package/src/room/data-track/frame.ts +28 -2
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +58 -73
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +139 -80
- package/src/room/data-track/incoming/pipeline.ts +29 -24
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +225 -32
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +150 -75
- package/src/room/data-track/outgoing/errors.ts +36 -7
- package/src/room/data-track/outgoing/pipeline.ts +23 -17
- package/src/room/data-track/outgoing/types.ts +12 -3
- package/src/room/data-track/packet/extensions.ts +17 -22
- package/src/room/data-track/packet/index.test.ts +22 -33
- package/src/room/data-track/packetizer.test.ts +2 -2
- package/src/room/data-track/packetizer.ts +4 -4
- package/src/room/data-track/track-interfaces.ts +1 -1
- package/src/room/data-track/types.ts +21 -1
- package/src/room/events.ts +26 -1
- package/src/room/participant/LocalParticipant.ts +74 -8
- package/src/room/participant/RemoteParticipant.ts +25 -0
- package/src/room/utils.ts +4 -0
- package/src/utils/deferrable-map.ts +109 -0
- package/src/utils/serializer.ts +72 -0
- package/dist/src/room/data-track/e2ee.d.ts +0 -12
- package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
- package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
- 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 {
|
|
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
|
|
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
|
-
|
|
33
|
-
|
|
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,
|
|
55
|
+
active(info: DataTrackInfo, e2eeManager: BaseE2EEManager | null): ActiveDescriptor {
|
|
61
56
|
return {
|
|
62
57
|
type: 'active',
|
|
63
58
|
info,
|
|
64
|
-
|
|
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
|
-
/**
|
|
76
|
-
|
|
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
|
|
83
|
+
* If null, end-to-end encryption will be disabled for all published tracks.
|
|
84
84
|
*/
|
|
85
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
132
|
-
):
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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('
|
|
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
|
-
/**
|
|
159
|
-
|
|
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.
|
|
225
|
+
usesE2ee: this.e2eeManager !== null,
|
|
206
226
|
});
|
|
207
227
|
|
|
208
|
-
|
|
228
|
+
await descriptor.completionFuture.promise;
|
|
209
229
|
combinedSignal.removeEventListener('abort', onAbort);
|
|
210
|
-
|
|
230
|
+
|
|
231
|
+
this.emit('trackPublished', {
|
|
232
|
+
track: LocalDataTrack.withExplicitHandle(options, this, handle),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return handle;
|
|
211
236
|
}
|
|
212
237
|
|
|
213
|
-
/**
|
|
214
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
|
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
|
|
3
|
-
import { type
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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:
|
|
49
|
-
):
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
async encryptIfNeeded(
|
|
52
|
+
frame: DataTrackFrameInternal,
|
|
53
|
+
): Promise<
|
|
54
|
+
Throws<
|
|
55
|
+
DataTrackFrameInternal,
|
|
56
|
+
DataTrackOutgoingPipelineError<DataTrackOutgoingPipelineErrorReason.Encryption>
|
|
57
|
+
>
|
|
52
58
|
> {
|
|
53
|
-
if (!this.
|
|
59
|
+
if (!this.e2eeManager) {
|
|
54
60
|
return frame;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
let encryptedResult
|
|
63
|
+
let encryptedResult;
|
|
58
64
|
try {
|
|
59
|
-
encryptedResult = this.
|
|
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
|
-
/**
|
|
34
|
-
export type
|
|
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,
|
|
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
|
-
|
|
33
|
-
|
|
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.
|
|
42
|
-
byteIndex +=
|
|
41
|
+
dataView.setUint8(byteIndex, DataTrackUserTimestampExtension.tag);
|
|
42
|
+
byteIndex += U8_LENGTH_BYTES;
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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.
|
|
99
|
-
byteIndex +=
|
|
95
|
+
dataView.setUint8(byteIndex, DataTrackE2eeExtension.tag);
|
|
96
|
+
byteIndex += U8_LENGTH_BYTES;
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
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 >=
|
|
198
|
-
const tag = dataView.
|
|
199
|
-
byteIndex +=
|
|
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
|
|
202
|
-
|
|
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
|