livekit-client 2.17.2 → 2.18.0
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/README.md +7 -5
- 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 +20 -14
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +295 -193
- 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/e2ee/E2eeManager.d.ts +2 -0
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +2 -0
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/events.d.ts +1 -1
- package/dist/src/e2ee/events.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +1 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +2 -2
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +1 -4
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +3 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/IncomingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-stream/incoming/StreamReader.d.ts +2 -4
- package/dist/src/room/data-stream/incoming/StreamReader.d.ts.map +1 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts +25 -0
- package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -0
- package/dist/src/room/data-track/RemoteDataTrack.d.ts +46 -0
- package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -0
- 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/e2ee.d.ts +12 -0
- package/dist/src/room/data-track/e2ee.d.ts.map +1 -0
- package/dist/src/room/data-track/handle.d.ts +7 -8
- package/dist/src/room/data-track/handle.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +96 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/errors.d.ts +24 -0
- package/dist/src/room/data-track/incoming/errors.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/pipeline.d.ts +37 -0
- package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/types.d.ts +20 -0
- package/dist/src/room/data-track/incoming/types.d.ts.map +1 -0
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +77 -0
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -0
- package/dist/src/room/data-track/outgoing/errors.d.ts +64 -0
- package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -0
- package/dist/src/room/data-track/outgoing/pipeline.d.ts +22 -0
- package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -0
- package/dist/src/room/data-track/outgoing/types.d.ts +30 -0
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -0
- package/dist/src/room/data-track/packet/errors.d.ts +2 -4
- package/dist/src/room/data-track/packet/errors.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/index.d.ts +5 -5
- package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/serializable.d.ts +4 -4
- package/dist/src/room/data-track/packet/serializable.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 +23 -0
- package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -0
- package/dist/src/room/data-track/types.d.ts +10 -0
- package/dist/src/room/data-track/types.d.ts.map +1 -0
- package/dist/src/room/debounce.d.ts +11 -0
- package/dist/src/room/debounce.d.ts.map +1 -0
- package/dist/src/room/events.d.ts +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts.map +1 -1
- package/dist/src/room/token-source/types.d.ts +1 -0
- package/dist/src/room/token-source/types.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +0 -2
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +6 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/abort-signal-polyfill.d.ts +13 -0
- package/dist/src/utils/abort-signal-polyfill.d.ts.map +1 -0
- package/dist/src/utils/subscribeToEvents.d.ts +15 -0
- package/dist/src/utils/subscribeToEvents.d.ts.map +1 -0
- package/dist/ts4.2/e2ee/E2eeManager.d.ts +2 -0
- package/dist/ts4.2/e2ee/KeyProvider.d.ts +2 -0
- package/dist/ts4.2/e2ee/events.d.ts +1 -1
- package/dist/ts4.2/e2ee/types.d.ts +1 -0
- package/dist/ts4.2/e2ee/worker/ParticipantKeyHandler.d.ts +2 -2
- package/dist/ts4.2/index.d.ts +2 -2
- package/dist/ts4.2/room/PCTransport.d.ts +1 -6
- package/dist/ts4.2/room/RTCEngine.d.ts +3 -1
- package/dist/ts4.2/room/data-stream/incoming/StreamReader.d.ts +2 -4
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +25 -0
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +46 -0
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/e2ee.d.ts +12 -0
- package/dist/ts4.2/room/data-track/handle.d.ts +7 -8
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +99 -0
- package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -0
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +37 -0
- package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -0
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +77 -0
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +64 -0
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +22 -0
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +30 -0
- package/dist/ts4.2/room/data-track/packet/errors.d.ts +2 -4
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/ts4.2/room/data-track/packet/index.d.ts +5 -6
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +4 -4
- package/dist/ts4.2/room/data-track/packetizer.d.ts +4 -4
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -0
- package/dist/ts4.2/room/data-track/types.d.ts +10 -0
- package/dist/ts4.2/room/debounce.d.ts +11 -0
- package/dist/ts4.2/room/events.d.ts +1 -1
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +1 -1
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +1 -1
- package/dist/ts4.2/room/token-source/types.d.ts +1 -0
- package/dist/ts4.2/room/track/LocalAudioTrack.d.ts +1 -1
- package/dist/ts4.2/room/track/LocalTrack.d.ts +1 -1
- package/dist/ts4.2/room/types.d.ts +0 -2
- package/dist/ts4.2/room/utils.d.ts +6 -1
- package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -0
- package/dist/ts4.2/utils/subscribeToEvents.d.ts +15 -0
- package/package.json +7 -6
- package/src/e2ee/E2eeManager.ts +9 -5
- package/src/e2ee/KeyProvider.ts +10 -1
- package/src/e2ee/events.ts +1 -1
- package/src/e2ee/types.ts +1 -0
- package/src/e2ee/worker/ParticipantKeyHandler.ts +7 -4
- package/src/e2ee/worker/e2ee.worker.ts +20 -10
- package/src/index.ts +2 -2
- package/src/room/PCTransport.ts +1 -1
- package/src/room/RTCEngine.ts +8 -1
- package/src/room/Room.ts +13 -7
- package/src/room/data-stream/incoming/IncomingDataStreamManager.ts +5 -25
- package/src/room/data-stream/incoming/StreamReader.ts +56 -73
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +2 -2
- package/src/room/data-track/LocalDataTrack.ts +53 -0
- package/src/room/data-track/RemoteDataTrack.ts +82 -0
- package/src/room/data-track/depacketizer.test.ts +1 -1
- package/src/room/data-track/depacketizer.ts +5 -17
- package/src/room/data-track/e2ee.ts +15 -0
- package/src/room/data-track/frame.ts +1 -1
- package/src/room/data-track/handle.test.ts +1 -1
- package/src/room/data-track/handle.ts +10 -21
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +570 -0
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +537 -0
- package/src/room/data-track/incoming/errors.ts +57 -0
- package/src/room/data-track/incoming/pipeline.ts +116 -0
- package/src/room/data-track/incoming/types.ts +22 -0
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +412 -0
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +308 -0
- package/src/room/data-track/outgoing/errors.ts +157 -0
- package/src/room/data-track/outgoing/pipeline.ts +72 -0
- package/src/room/data-track/outgoing/types.ts +36 -0
- package/src/room/data-track/packet/errors.ts +2 -14
- package/src/room/data-track/packet/extensions.ts +4 -4
- package/src/room/data-track/packet/index.ts +6 -8
- package/src/room/data-track/packet/serializable.ts +4 -4
- package/src/room/data-track/packetizer.test.ts +3 -3
- package/src/room/data-track/packetizer.ts +9 -8
- package/src/room/data-track/track-interfaces.ts +53 -0
- package/src/room/data-track/types.ts +11 -0
- package/src/room/debounce.ts +115 -0
- package/src/room/events.ts +1 -1
- package/src/room/participant/LocalParticipant.ts +2 -0
- package/src/room/participant/RemoteParticipant.ts +1 -1
- package/src/room/token-source/TokenSource.ts +8 -2
- package/src/room/token-source/types.ts +4 -0
- package/src/room/track/LocalAudioTrack.ts +6 -3
- package/src/room/track/LocalTrack.ts +10 -5
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteVideoTrack.ts +1 -1
- package/src/room/types.ts +0 -2
- package/src/room/utils.ts +7 -2
- package/src/utils/abort-signal-polyfill.ts +63 -0
- package/src/utils/subscribeToEvents.ts +80 -0
- package/dist/src/utils/throws.d.ts +0 -36
- package/dist/src/utils/throws.d.ts.map +0 -1
- package/dist/ts4.2/utils/throws.d.ts +0 -39
- package/src/utils/throws.ts +0 -42
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DataStream_Chunk } from '@livekit/protocol';
|
|
2
2
|
import { DataStreamError, DataStreamErrorReason } from '../../errors';
|
|
3
3
|
import type { BaseStreamInfo, ByteStreamInfo, TextStreamInfo } from '../../types';
|
|
4
|
-
import {
|
|
4
|
+
import { bigIntToNumber } from '../../utils';
|
|
5
5
|
|
|
6
6
|
export type BaseStreamReaderReadAllOpts = {
|
|
7
7
|
/** An AbortSignal can be used to terminate reads early. */
|
|
@@ -17,8 +17,6 @@ abstract class BaseStreamReader<T extends BaseStreamInfo> {
|
|
|
17
17
|
|
|
18
18
|
protected bytesReceived: number;
|
|
19
19
|
|
|
20
|
-
protected outOfBandFailureRejectingFuture?: Future<never, Error>;
|
|
21
|
-
|
|
22
20
|
get info() {
|
|
23
21
|
return this._info;
|
|
24
22
|
}
|
|
@@ -42,17 +40,11 @@ abstract class BaseStreamReader<T extends BaseStreamInfo> {
|
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
constructor(
|
|
46
|
-
info: T,
|
|
47
|
-
stream: ReadableStream<DataStream_Chunk>,
|
|
48
|
-
totalByteSize?: number,
|
|
49
|
-
outOfBandFailureRejectingFuture?: Future<never, Error>,
|
|
50
|
-
) {
|
|
43
|
+
constructor(info: T, stream: ReadableStream<DataStream_Chunk>, totalByteSize?: number) {
|
|
51
44
|
this.reader = stream;
|
|
52
45
|
this.totalByteSize = totalByteSize;
|
|
53
46
|
this._info = info;
|
|
54
47
|
this.bytesReceived = 0;
|
|
55
|
-
this.outOfBandFailureRejectingFuture = outOfBandFailureRejectingFuture;
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
protected abstract handleChunkReceived(chunk: DataStream_Chunk): void;
|
|
@@ -79,48 +71,44 @@ export class ByteStreamReader extends BaseStreamReader<ByteStreamInfo> {
|
|
|
79
71
|
|
|
80
72
|
[Symbol.asyncIterator]() {
|
|
81
73
|
const reader = this.reader.getReader();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let onAbort: (() => void) | null = null;
|
|
86
|
-
if (this.signal) {
|
|
87
|
-
const signal = this.signal;
|
|
88
|
-
onAbort = () => {
|
|
89
|
-
rejectingSignalFuture.reject?.(signal.reason);
|
|
90
|
-
};
|
|
91
|
-
signal.addEventListener('abort', onAbort);
|
|
92
|
-
activeSignal = signal;
|
|
93
|
-
}
|
|
74
|
+
// Suppress unhandled rejection on reader.closed — errors are
|
|
75
|
+
// already propagated through reader.read() to the consumer.
|
|
76
|
+
reader.closed.catch(() => {});
|
|
94
77
|
|
|
95
78
|
const cleanup = () => {
|
|
96
79
|
reader.releaseLock();
|
|
97
|
-
|
|
98
|
-
if (activeSignal && onAbort) {
|
|
99
|
-
activeSignal.removeEventListener('abort', onAbort);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
80
|
this.signal = undefined;
|
|
103
81
|
};
|
|
104
82
|
|
|
105
83
|
return {
|
|
106
84
|
next: async (): Promise<IteratorResult<Uint8Array>> => {
|
|
107
85
|
try {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
86
|
+
const signal = this.signal;
|
|
87
|
+
if (signal?.aborted) {
|
|
88
|
+
throw signal.reason;
|
|
89
|
+
}
|
|
90
|
+
const result = await new Promise<ReadableStreamReadResult<DataStream_Chunk>>(
|
|
91
|
+
(resolve, reject) => {
|
|
92
|
+
if (signal) {
|
|
93
|
+
const onAbort = () => reject(signal.reason);
|
|
94
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
95
|
+
reader
|
|
96
|
+
.read()
|
|
97
|
+
.then(resolve, reject)
|
|
98
|
+
.finally(() => {
|
|
99
|
+
signal.removeEventListener('abort', onAbort);
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
reader.read().then(resolve, reject);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
if (result.done) {
|
|
119
107
|
this.validateBytesReceived(true);
|
|
120
108
|
return { done: true, value: undefined as any };
|
|
121
109
|
} else {
|
|
122
|
-
this.handleChunkReceived(value);
|
|
123
|
-
return { done: false, value: value.content };
|
|
110
|
+
this.handleChunkReceived(result.value);
|
|
111
|
+
return { done: false, value: result.value.content };
|
|
124
112
|
}
|
|
125
113
|
} catch (err) {
|
|
126
114
|
cleanup();
|
|
@@ -175,9 +163,8 @@ export class TextStreamReader extends BaseStreamReader<TextStreamInfo> {
|
|
|
175
163
|
info: TextStreamInfo,
|
|
176
164
|
stream: ReadableStream<DataStream_Chunk>,
|
|
177
165
|
totalChunkCount?: number,
|
|
178
|
-
outOfBandFailureRejectingFuture?: Future<never, Error>,
|
|
179
166
|
) {
|
|
180
|
-
super(info, stream, totalChunkCount
|
|
167
|
+
super(info, stream, totalChunkCount);
|
|
181
168
|
this.receivedChunks = new Map();
|
|
182
169
|
}
|
|
183
170
|
|
|
@@ -211,55 +198,51 @@ export class TextStreamReader extends BaseStreamReader<TextStreamInfo> {
|
|
|
211
198
|
*/
|
|
212
199
|
[Symbol.asyncIterator]() {
|
|
213
200
|
const reader = this.reader.getReader();
|
|
201
|
+
// Suppress unhandled rejection on reader.closed — errors are
|
|
202
|
+
// already propagated through reader.read() to the consumer.
|
|
203
|
+
reader.closed.catch(() => {});
|
|
214
204
|
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
215
|
-
|
|
216
|
-
let rejectingSignalFuture = new Future<never, Error>();
|
|
217
|
-
let activeSignal: AbortSignal | null = null;
|
|
218
|
-
let onAbort: (() => void) | null = null;
|
|
219
|
-
if (this.signal) {
|
|
220
|
-
const signal = this.signal;
|
|
221
|
-
onAbort = () => {
|
|
222
|
-
rejectingSignalFuture.reject?.(signal.reason);
|
|
223
|
-
};
|
|
224
|
-
signal.addEventListener('abort', onAbort);
|
|
225
|
-
activeSignal = signal;
|
|
226
|
-
}
|
|
205
|
+
const signal = this.signal;
|
|
227
206
|
|
|
228
207
|
const cleanup = () => {
|
|
229
208
|
reader.releaseLock();
|
|
230
|
-
|
|
231
|
-
if (activeSignal && onAbort) {
|
|
232
|
-
activeSignal.removeEventListener('abort', onAbort);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
209
|
this.signal = undefined;
|
|
236
210
|
};
|
|
237
211
|
|
|
238
212
|
return {
|
|
239
213
|
next: async (): Promise<IteratorResult<string>> => {
|
|
240
214
|
try {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
215
|
+
if (signal?.aborted) {
|
|
216
|
+
throw signal.reason;
|
|
217
|
+
}
|
|
218
|
+
const result = await new Promise<ReadableStreamReadResult<DataStream_Chunk>>(
|
|
219
|
+
(resolve, reject) => {
|
|
220
|
+
if (signal) {
|
|
221
|
+
const onAbort = () => reject(signal.reason);
|
|
222
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
223
|
+
reader
|
|
224
|
+
.read()
|
|
225
|
+
.then(resolve, reject)
|
|
226
|
+
.finally(() => {
|
|
227
|
+
signal.removeEventListener('abort', onAbort);
|
|
228
|
+
});
|
|
229
|
+
} else {
|
|
230
|
+
reader.read().then(resolve, reject);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
if (result.done) {
|
|
252
235
|
this.validateBytesReceived(true);
|
|
253
236
|
return { done: true, value: undefined };
|
|
254
237
|
} else {
|
|
255
|
-
this.handleChunkReceived(value);
|
|
238
|
+
this.handleChunkReceived(result.value);
|
|
256
239
|
|
|
257
240
|
let decodedResult;
|
|
258
241
|
try {
|
|
259
|
-
decodedResult = decoder.decode(value.content);
|
|
242
|
+
decodedResult = decoder.decode(result.value.content);
|
|
260
243
|
} catch (err) {
|
|
261
244
|
throw new DataStreamError(
|
|
262
|
-
`Cannot decode datastream chunk ${value.chunkIndex} as text: ${err}`,
|
|
245
|
+
`Cannot decode datastream chunk ${result.value.chunkIndex} as text: ${err}`,
|
|
263
246
|
DataStreamErrorReason.DecodeFailed,
|
|
264
247
|
);
|
|
265
248
|
}
|
|
@@ -114,7 +114,7 @@ export default class OutgoingDataStreamManager {
|
|
|
114
114
|
mimeType: info.mimeType,
|
|
115
115
|
topic: info.topic,
|
|
116
116
|
timestamp: numberToBigInt(info.timestamp),
|
|
117
|
-
totalLength: numberToBigInt(
|
|
117
|
+
totalLength: numberToBigInt(info.size),
|
|
118
118
|
attributes: info.attributes,
|
|
119
119
|
contentHeader: {
|
|
120
120
|
case: 'textHeader',
|
|
@@ -240,7 +240,7 @@ export default class OutgoingDataStreamManager {
|
|
|
240
240
|
};
|
|
241
241
|
|
|
242
242
|
const header = new DataStream_Header({
|
|
243
|
-
totalLength: numberToBigInt(info.size
|
|
243
|
+
totalLength: numberToBigInt(info.size),
|
|
244
244
|
mimeType: info.mimeType,
|
|
245
245
|
streamId,
|
|
246
246
|
topic: info.topic,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DataTrackFrame } from './frame';
|
|
2
|
+
import type OutgoingDataTrackManager from './outgoing/OutgoingDataTrackManager';
|
|
3
|
+
import {
|
|
4
|
+
DataTrackSymbol,
|
|
5
|
+
type IDataTrack,
|
|
6
|
+
type ILocalTrack,
|
|
7
|
+
TrackSymbol,
|
|
8
|
+
} from './track-interfaces';
|
|
9
|
+
import type { DataTrackInfo } from './types';
|
|
10
|
+
|
|
11
|
+
export default class LocalDataTrack implements ILocalTrack, IDataTrack {
|
|
12
|
+
readonly trackSymbol = TrackSymbol;
|
|
13
|
+
|
|
14
|
+
readonly isLocal = true;
|
|
15
|
+
|
|
16
|
+
readonly typeSymbol = DataTrackSymbol;
|
|
17
|
+
|
|
18
|
+
info: DataTrackInfo;
|
|
19
|
+
|
|
20
|
+
protected manager: OutgoingDataTrackManager;
|
|
21
|
+
|
|
22
|
+
/** @internal */
|
|
23
|
+
constructor(info: DataTrackInfo, manager: OutgoingDataTrackManager) {
|
|
24
|
+
this.info = info;
|
|
25
|
+
this.manager = manager;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The raw descriptor from the manager containing the internal state for this local track. */
|
|
29
|
+
protected get descriptor() {
|
|
30
|
+
return this.manager.getDescriptor(this.info.pubHandle);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
isPublished() {
|
|
34
|
+
return this.descriptor?.type === 'active';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Try pushing a frame to subscribers of the track.
|
|
38
|
+
*
|
|
39
|
+
* Pushing a frame can fail for several reasons:
|
|
40
|
+
*
|
|
41
|
+
* - The track has been unpublished by the local participant or SFU
|
|
42
|
+
* - The room is no longer connected
|
|
43
|
+
*/
|
|
44
|
+
tryPush(payload: DataTrackFrame['payload']) {
|
|
45
|
+
try {
|
|
46
|
+
return this.manager.tryProcessAndSend(this.info.pubHandle, payload);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
// NOTE: wrapping in the bare try/catch like this means that the Throws<...> type doesn't
|
|
49
|
+
// propegate upwards into the public interface.
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type Participant from '../participant/Participant';
|
|
2
|
+
import type { DataTrackFrame } from './frame';
|
|
3
|
+
import type IncomingDataTrackManager from './incoming/IncomingDataTrackManager';
|
|
4
|
+
import {
|
|
5
|
+
DataTrackSymbol,
|
|
6
|
+
type IDataTrack,
|
|
7
|
+
type IRemoteTrack,
|
|
8
|
+
TrackSymbol,
|
|
9
|
+
} from './track-interfaces';
|
|
10
|
+
import { type DataTrackInfo } from './types';
|
|
11
|
+
|
|
12
|
+
type RemoteDataTrackOptions = {
|
|
13
|
+
publisherIdentity: Participant['identity'];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type RemoteDataTrackSubscribeOptions = {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
|
|
19
|
+
/** The number of {@link DataTrackFrame}s to hold in the ReadableStream before disgarding extra
|
|
20
|
+
* frames. Defaults to 4, but this may not be good enough for especially high frequency data. */
|
|
21
|
+
highWaterMark?: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default class RemoteDataTrack implements IRemoteTrack, IDataTrack {
|
|
25
|
+
readonly trackSymbol = TrackSymbol;
|
|
26
|
+
|
|
27
|
+
readonly isLocal = false;
|
|
28
|
+
|
|
29
|
+
readonly typeSymbol = DataTrackSymbol;
|
|
30
|
+
|
|
31
|
+
info: DataTrackInfo;
|
|
32
|
+
|
|
33
|
+
publisherIdentity: Participant['identity'];
|
|
34
|
+
|
|
35
|
+
protected manager: IncomingDataTrackManager;
|
|
36
|
+
|
|
37
|
+
/** @internal */
|
|
38
|
+
constructor(
|
|
39
|
+
info: DataTrackInfo,
|
|
40
|
+
manager: IncomingDataTrackManager,
|
|
41
|
+
options: RemoteDataTrackOptions,
|
|
42
|
+
) {
|
|
43
|
+
this.info = info;
|
|
44
|
+
this.manager = manager;
|
|
45
|
+
this.publisherIdentity = options.publisherIdentity;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Subscribes to the data track to receive frames.
|
|
49
|
+
*
|
|
50
|
+
* # Returns
|
|
51
|
+
*
|
|
52
|
+
* A stream that yields {@link DataTrackFrame}s as they arrive.
|
|
53
|
+
*
|
|
54
|
+
* # Multiple Subscriptions
|
|
55
|
+
*
|
|
56
|
+
* An application may call `subscribe` more than once to process frames in
|
|
57
|
+
* multiple places. For example, one async task might plot values on a graph
|
|
58
|
+
* while another writes them to a file.
|
|
59
|
+
*
|
|
60
|
+
* Internally, only the first call to `subscribe` communicates with the SFU and
|
|
61
|
+
* allocates the resources required to receive frames. Additional subscriptions
|
|
62
|
+
* reuse the same underlying pipeline and do not trigger additional signaling.
|
|
63
|
+
*
|
|
64
|
+
* Note that newly created subscriptions only receive frames published after
|
|
65
|
+
* the initial subscription is established.
|
|
66
|
+
*/
|
|
67
|
+
async subscribe(
|
|
68
|
+
options?: RemoteDataTrackSubscribeOptions,
|
|
69
|
+
): Promise<ReadableStream<DataTrackFrame>> {
|
|
70
|
+
try {
|
|
71
|
+
const stream = await this.manager.subscribeRequest(
|
|
72
|
+
this.info.sid,
|
|
73
|
+
options?.signal,
|
|
74
|
+
options?.highWaterMark,
|
|
75
|
+
);
|
|
76
|
+
return stream;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// NOTE: Rethrow errors to break Throws<...> type boundary
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import
|
|
3
|
+
import DataTrackDepacketizer from './depacketizer';
|
|
4
4
|
import { DataTrackHandle } from './handle';
|
|
5
5
|
import { DataTrackPacket, DataTrackPacketHeader, FrameMarker } from './packet';
|
|
6
6
|
import { DataTrackTimestamp, WrapAroundUnsignedInt } from './utils';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
1
2
|
import { LoggerNames, getLogger } from '../../logger';
|
|
2
|
-
import { type Throws } from '../../utils/throws';
|
|
3
3
|
import { LivekitReasonedError } from '../errors';
|
|
4
4
|
import { type DataTrackFrame } from './frame';
|
|
5
5
|
import { DataTrackPacket, FrameMarker } from './packet';
|
|
@@ -21,7 +21,7 @@ type PartialFrame = {
|
|
|
21
21
|
|
|
22
22
|
/** An error indicating a frame was dropped. */
|
|
23
23
|
export class DataTrackDepacketizerDropError<
|
|
24
|
-
Reason extends DataTrackDepacketizerDropReason,
|
|
24
|
+
Reason extends DataTrackDepacketizerDropReason = DataTrackDepacketizerDropReason,
|
|
25
25
|
> extends LivekitReasonedError<Reason> {
|
|
26
26
|
readonly name = 'DataTrackDepacketizerDropError';
|
|
27
27
|
|
|
@@ -85,7 +85,7 @@ type PushOptions = {
|
|
|
85
85
|
errorOnPartialFrames: boolean;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export class DataTrackDepacketizer {
|
|
88
|
+
export default class DataTrackDepacketizer {
|
|
89
89
|
/** Maximum number of packets to buffer per frame before dropping. */
|
|
90
90
|
static MAX_BUFFER_PACKETS = 128;
|
|
91
91
|
|
|
@@ -99,13 +99,7 @@ export class DataTrackDepacketizer {
|
|
|
99
99
|
push(
|
|
100
100
|
packet: DataTrackPacket,
|
|
101
101
|
options?: PushOptions,
|
|
102
|
-
): Throws<
|
|
103
|
-
DataTrackFrame | null,
|
|
104
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.Interrupted>
|
|
105
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.BufferFull>
|
|
106
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.UnknownFrame>
|
|
107
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.Incomplete>
|
|
108
|
-
> {
|
|
102
|
+
): Throws<DataTrackFrame | null, DataTrackDepacketizerDropError> {
|
|
109
103
|
switch (packet.header.marker) {
|
|
110
104
|
case FrameMarker.Single:
|
|
111
105
|
return this.frameFromSingle(packet, options);
|
|
@@ -191,13 +185,7 @@ export class DataTrackDepacketizer {
|
|
|
191
185
|
/** Push to the existing partial frame. */
|
|
192
186
|
private pushToPartial(
|
|
193
187
|
packet: DataTrackPacket,
|
|
194
|
-
): Throws<
|
|
195
|
-
DataTrackFrame | null,
|
|
196
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.Interrupted>
|
|
197
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.UnknownFrame>
|
|
198
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.BufferFull>
|
|
199
|
-
| DataTrackDepacketizerDropError<DataTrackDepacketizerDropReason.Incomplete>
|
|
200
|
-
> {
|
|
188
|
+
): Throws<DataTrackFrame | null, DataTrackDepacketizerDropError> {
|
|
201
189
|
if (packet.header.marker !== FrameMarker.Inter && packet.header.marker !== FrameMarker.Final) {
|
|
202
190
|
// @throws-transformer ignore - this should be treated as a "panic" and not be caught
|
|
203
191
|
throw new Error(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type EncryptedPayload = {
|
|
2
|
+
payload: Uint8Array;
|
|
3
|
+
iv: Uint8Array; // NOTE: should be 12 bytes long
|
|
4
|
+
keyIndex: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type EncryptionProvider = {
|
|
8
|
+
// FIXME: add in explicit `Throws<..., EncryptionError>`?
|
|
9
|
+
encrypt(payload: Uint8Array): EncryptedPayload;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type DecryptionProvider = {
|
|
13
|
+
// FIXME: add in explicit `Throws<..., DecryptionError>`?
|
|
14
|
+
decrypt(payload: EncryptedPayload, senderIdentity: string): Uint8Array;
|
|
15
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DataTrackExtensions } from './packet/extensions';
|
|
2
|
-
import
|
|
2
|
+
import DataTrackPacketizer from './packetizer';
|
|
3
3
|
|
|
4
4
|
/** A pair of payload bytes and packet extensions which can be fed into a {@link DataTrackPacketizer}. */
|
|
5
5
|
export type DataTrackFrame = {
|
|
@@ -4,7 +4,7 @@ import { DataTrackHandle } from './handle';
|
|
|
4
4
|
|
|
5
5
|
describe('DataTrackHandle', () => {
|
|
6
6
|
it('should parse handle raw inputs', () => {
|
|
7
|
-
expect(DataTrackHandle.fromNumber(3)
|
|
7
|
+
expect(DataTrackHandle.fromNumber(3)).toEqual(3);
|
|
8
8
|
expect(() => DataTrackHandle.fromNumber(0)).toThrow('0x0 is a reserved value');
|
|
9
9
|
expect(() => DataTrackHandle.fromNumber(9999999)).toThrow(
|
|
10
10
|
'Value too large to be a valid track handle',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Throws } from '
|
|
1
|
+
import { type Throws } from '@livekit/throws-transformer/throws';
|
|
2
2
|
import { LivekitReasonedError } from '../errors';
|
|
3
3
|
import { U16_MAX_SIZE } from './utils';
|
|
4
4
|
|
|
@@ -41,40 +41,29 @@ export class DataTrackHandleError<
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
static fromNumber(
|
|
48
|
-
raw: number,
|
|
49
|
-
): Throws<
|
|
50
|
-
DataTrackHandle,
|
|
51
|
-
| DataTrackHandleError<DataTrackHandleErrorReason.TooLarge>
|
|
52
|
-
| DataTrackHandleError<DataTrackHandleErrorReason.Reserved>
|
|
53
|
-
> {
|
|
44
|
+
export type DataTrackHandle = number;
|
|
45
|
+
export const DataTrackHandle = {
|
|
46
|
+
fromNumber(raw: number): Throws<DataTrackHandle, DataTrackHandleError> {
|
|
54
47
|
if (raw === 0) {
|
|
55
48
|
throw DataTrackHandleError.reserved(raw);
|
|
56
49
|
}
|
|
57
50
|
if (raw > U16_MAX_SIZE) {
|
|
58
51
|
throw DataTrackHandleError.tooLarge();
|
|
59
52
|
}
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
constructor(raw: number) {
|
|
64
|
-
this.value = raw;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
53
|
+
return raw;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
67
56
|
|
|
68
57
|
/** Manage allocating new handles which don't conflict over the lifetime of the client. */
|
|
69
58
|
export class DataTrackHandleAllocator {
|
|
70
|
-
|
|
59
|
+
value = 0;
|
|
71
60
|
|
|
72
61
|
/** Returns a unique track handle for the next publication, if one can be obtained. */
|
|
73
|
-
|
|
62
|
+
get(): DataTrackHandle | null {
|
|
74
63
|
this.value += 1;
|
|
75
64
|
if (this.value > U16_MAX_SIZE) {
|
|
76
65
|
return null;
|
|
77
66
|
}
|
|
78
|
-
return
|
|
67
|
+
return this.value;
|
|
79
68
|
}
|
|
80
69
|
}
|