@livekit/rtc-node 0.13.24 → 0.13.26
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/audio_filter.cjs +2 -2
- package/dist/audio_filter.cjs.map +1 -1
- package/dist/audio_filter.js +1 -1
- package/dist/audio_filter.js.map +1 -1
- package/dist/audio_frame.cjs +9 -5
- package/dist/audio_frame.cjs.map +1 -1
- package/dist/audio_frame.d.cts +2 -7
- package/dist/audio_frame.d.ts +2 -7
- package/dist/audio_frame.d.ts.map +1 -1
- package/dist/audio_frame.js +8 -4
- package/dist/audio_frame.js.map +1 -1
- package/dist/audio_mixer.cjs +10 -4
- package/dist/audio_mixer.cjs.map +1 -1
- package/dist/audio_mixer.d.cts +4 -7
- package/dist/audio_mixer.d.ts +4 -7
- package/dist/audio_mixer.d.ts.map +1 -1
- package/dist/audio_mixer.js +10 -4
- package/dist/audio_mixer.js.map +1 -1
- package/dist/audio_resampler.cjs +20 -12
- package/dist/audio_resampler.cjs.map +1 -1
- package/dist/audio_resampler.d.cts +6 -6
- package/dist/audio_resampler.d.ts +6 -6
- package/dist/audio_resampler.d.ts.map +1 -1
- package/dist/audio_resampler.js +12 -4
- package/dist/audio_resampler.js.map +1 -1
- package/dist/audio_source.cjs +11 -7
- package/dist/audio_source.cjs.map +1 -1
- package/dist/audio_source.d.cts +1 -7
- package/dist/audio_source.d.ts +1 -7
- package/dist/audio_source.d.ts.map +1 -1
- package/dist/audio_source.js +8 -3
- package/dist/audio_source.js.map +1 -1
- package/dist/audio_stream.cjs +16 -6
- package/dist/audio_stream.cjs.map +1 -1
- package/dist/audio_stream.d.cts +1 -14
- package/dist/audio_stream.d.ts +1 -14
- package/dist/audio_stream.d.ts.map +1 -1
- package/dist/audio_stream.js +15 -5
- package/dist/audio_stream.js.map +1 -1
- package/dist/data_streams/index.d.cts +2 -10
- package/dist/data_streams/index.d.ts +2 -10
- package/dist/data_streams/stream_reader.cjs +8 -0
- package/dist/data_streams/stream_reader.cjs.map +1 -1
- package/dist/data_streams/stream_reader.d.cts +2 -10
- package/dist/data_streams/stream_reader.d.ts +2 -10
- package/dist/data_streams/stream_reader.d.ts.map +1 -1
- package/dist/data_streams/stream_reader.js +8 -0
- package/dist/data_streams/stream_reader.js.map +1 -1
- package/dist/data_streams/stream_writer.d.cts +2 -10
- package/dist/data_streams/stream_writer.d.ts +2 -10
- package/dist/data_streams/types.cjs.map +1 -1
- package/dist/data_streams/types.d.cts +2 -10
- package/dist/data_streams/types.d.ts +2 -10
- package/dist/data_streams/types.d.ts.map +1 -1
- package/dist/e2ee.cjs +21 -21
- package/dist/e2ee.cjs.map +1 -1
- package/dist/e2ee.d.cts +1 -2
- package/dist/e2ee.d.ts +1 -2
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/e2ee.js +2 -2
- package/dist/e2ee.js.map +1 -1
- package/dist/ffi_client.cjs +30 -15
- package/dist/ffi_client.cjs.map +1 -1
- package/dist/ffi_client.d.cts +6 -17
- package/dist/ffi_client.d.ts +6 -17
- package/dist/ffi_client.d.ts.map +1 -1
- package/dist/ffi_client.js +24 -6
- package/dist/ffi_client.js.map +1 -1
- package/dist/frame_processor.cjs +13 -2
- package/dist/frame_processor.cjs.map +1 -1
- package/dist/frame_processor.d.cts +5 -8
- package/dist/frame_processor.d.ts +5 -8
- package/dist/frame_processor.d.ts.map +1 -1
- package/dist/frame_processor.js +10 -1
- package/dist/frame_processor.js.map +1 -1
- package/dist/index.cjs +25 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -16
- package/dist/index.d.ts +3 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/participant.cjs +146 -101
- package/dist/participant.cjs.map +1 -1
- package/dist/participant.d.cts +4 -16
- package/dist/participant.d.ts +4 -16
- package/dist/participant.d.ts.map +1 -1
- package/dist/participant.js +120 -75
- package/dist/participant.js.map +1 -1
- package/dist/room.cjs +116 -58
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.cts +6 -15
- package/dist/room.d.ts +6 -15
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +113 -55
- package/dist/room.js.map +1 -1
- package/dist/rpc.cjs.map +1 -1
- package/dist/rpc.d.cts +1 -2
- package/dist/rpc.d.ts +1 -2
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js.map +1 -1
- package/dist/{stream_reader-Ch2zvjU3.d.cts → stream_reader-CuG4b8Y-.d.cts} +1 -1
- package/dist/{stream_reader-DRyR29vo.d.ts → stream_reader-CuG4b8Y-.d.ts} +1 -1
- package/dist/track.cjs +3 -3
- package/dist/track.cjs.map +1 -1
- package/dist/track.d.cts +1 -14
- package/dist/track.d.ts +1 -14
- package/dist/track.d.ts.map +1 -1
- package/dist/track.js +1 -1
- package/dist/track.js.map +1 -1
- package/dist/track_publication.cjs +4 -4
- package/dist/track_publication.cjs.map +1 -1
- package/dist/track_publication.d.cts +1 -14
- package/dist/track_publication.d.ts +1 -14
- package/dist/track_publication.d.ts.map +1 -1
- package/dist/track_publication.js +2 -2
- package/dist/track_publication.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/video_frame.cjs +42 -42
- package/dist/video_frame.cjs.map +1 -1
- package/dist/video_frame.d.cts +1 -6
- package/dist/video_frame.d.ts +1 -6
- package/dist/video_frame.d.ts.map +1 -1
- package/dist/video_frame.js +2 -2
- package/dist/video_frame.js.map +1 -1
- package/dist/video_source.cjs +5 -5
- package/dist/video_source.cjs.map +1 -1
- package/dist/video_source.d.cts +1 -14
- package/dist/video_source.d.ts +1 -14
- package/dist/video_source.d.ts.map +1 -1
- package/dist/video_source.js +2 -2
- package/dist/video_source.js.map +1 -1
- package/dist/video_stream.cjs +12 -4
- package/dist/video_stream.cjs.map +1 -1
- package/dist/video_stream.d.cts +1 -14
- package/dist/video_stream.d.ts +1 -14
- package/dist/video_stream.d.ts.map +1 -1
- package/dist/video_stream.js +10 -2
- package/dist/video_stream.js.map +1 -1
- package/package.json +7 -37
- package/src/audio_filter.ts +2 -2
- package/src/audio_frame.ts +17 -5
- package/src/audio_mixer.test.ts +102 -0
- package/src/audio_mixer.ts +10 -4
- package/src/audio_resampler.ts +14 -4
- package/src/audio_source.ts +13 -5
- package/src/audio_stream.ts +21 -6
- package/src/data_streams/stream_reader.ts +19 -1
- package/src/data_streams/types.ts +1 -1
- package/src/e2ee.ts +3 -3
- package/src/ffi_client.ts +33 -8
- package/src/frame_processor.ts +21 -0
- package/src/index.ts +6 -5
- package/src/participant.ts +117 -66
- package/src/room.ts +161 -69
- package/src/rpc.ts +1 -1
- package/src/tests/e2e.test.ts +179 -8
- package/src/track.ts +4 -4
- package/src/track_publication.ts +6 -6
- package/src/version.ts +1 -1
- package/src/video_frame.ts +3 -3
- package/src/video_source.ts +3 -3
- package/src/video_stream.ts +15 -3
- package/Cargo.toml +0 -25
- package/build.rs +0 -9
- package/dist/napi/native.cjs +0 -301
- package/dist/napi/native.d.cjs +0 -17
- package/dist/napi/native.d.cjs.map +0 -1
- package/dist/napi/native.d.d.cts +0 -21
- package/dist/napi/native.d.d.ts +0 -21
- package/dist/napi/native.d.js +0 -1
- package/dist/napi/native.d.js.map +0 -1
- package/dist/napi/native.d.ts +0 -19
- package/dist/napi/native.js +0 -15
- package/dist/native.cjs +0 -301
- package/dist/native.d.ts +0 -19
- package/dist/native.js +0 -15
- package/dist/proto/audio_frame_pb.cjs +0 -1357
- package/dist/proto/audio_frame_pb.cjs.map +0 -1
- package/dist/proto/audio_frame_pb.d.cts +0 -1183
- package/dist/proto/audio_frame_pb.d.ts +0 -1183
- package/dist/proto/audio_frame_pb.d.ts.map +0 -1
- package/dist/proto/audio_frame_pb.js +0 -1283
- package/dist/proto/audio_frame_pb.js.map +0 -1
- package/dist/proto/data_stream_pb.cjs +0 -1638
- package/dist/proto/data_stream_pb.cjs.map +0 -1
- package/dist/proto/data_stream_pb.d.cts +0 -1449
- package/dist/proto/data_stream_pb.d.ts +0 -1449
- package/dist/proto/data_stream_pb.d.ts.map +0 -1
- package/dist/proto/data_stream_pb.js +0 -1559
- package/dist/proto/data_stream_pb.js.map +0 -1
- package/dist/proto/e2ee_pb.cjs +0 -746
- package/dist/proto/e2ee_pb.cjs.map +0 -1
- package/dist/proto/e2ee_pb.d.cts +0 -653
- package/dist/proto/e2ee_pb.d.ts +0 -653
- package/dist/proto/e2ee_pb.d.ts.map +0 -1
- package/dist/proto/e2ee_pb.js +0 -696
- package/dist/proto/e2ee_pb.js.map +0 -1
- package/dist/proto/ffi_pb.cjs +0 -477
- package/dist/proto/ffi_pb.cjs.map +0 -1
- package/dist/proto/ffi_pb.d.cts +0 -1321
- package/dist/proto/ffi_pb.d.ts +0 -1321
- package/dist/proto/ffi_pb.d.ts.map +0 -1
- package/dist/proto/ffi_pb.js +0 -444
- package/dist/proto/ffi_pb.js.map +0 -1
- package/dist/proto/handle_pb.cjs +0 -53
- package/dist/proto/handle_pb.cjs.map +0 -1
- package/dist/proto/handle_pb.d.cts +0 -31
- package/dist/proto/handle_pb.d.ts +0 -31
- package/dist/proto/handle_pb.d.ts.map +0 -1
- package/dist/proto/handle_pb.js +0 -29
- package/dist/proto/handle_pb.js.map +0 -1
- package/dist/proto/participant_pb.cjs +0 -173
- package/dist/proto/participant_pb.cjs.map +0 -1
- package/dist/proto/participant_pb.d.cts +0 -218
- package/dist/proto/participant_pb.d.ts +0 -218
- package/dist/proto/participant_pb.d.ts.map +0 -1
- package/dist/proto/participant_pb.js +0 -145
- package/dist/proto/participant_pb.js.map +0 -1
- package/dist/proto/room_pb.cjs +0 -3374
- package/dist/proto/room_pb.cjs.map +0 -1
- package/dist/proto/room_pb.d.cts +0 -3028
- package/dist/proto/room_pb.d.ts +0 -3028
- package/dist/proto/room_pb.d.ts.map +0 -1
- package/dist/proto/room_pb.js +0 -3232
- package/dist/proto/room_pb.js.map +0 -1
- package/dist/proto/rpc_pb.cjs +0 -328
- package/dist/proto/rpc_pb.cjs.map +0 -1
- package/dist/proto/rpc_pb.d.cts +0 -267
- package/dist/proto/rpc_pb.d.ts +0 -267
- package/dist/proto/rpc_pb.d.ts.map +0 -1
- package/dist/proto/rpc_pb.js +0 -294
- package/dist/proto/rpc_pb.js.map +0 -1
- package/dist/proto/stats_pb.cjs +0 -1370
- package/dist/proto/stats_pb.cjs.map +0 -1
- package/dist/proto/stats_pb.d.cts +0 -1764
- package/dist/proto/stats_pb.d.ts +0 -1764
- package/dist/proto/stats_pb.d.ts.map +0 -1
- package/dist/proto/stats_pb.js +0 -1300
- package/dist/proto/stats_pb.js.map +0 -1
- package/dist/proto/track_pb.cjs +0 -630
- package/dist/proto/track_pb.cjs.map +0 -1
- package/dist/proto/track_pb.d.cts +0 -542
- package/dist/proto/track_pb.d.ts +0 -542
- package/dist/proto/track_pb.d.ts.map +0 -1
- package/dist/proto/track_pb.js +0 -584
- package/dist/proto/track_pb.js.map +0 -1
- package/dist/proto/track_publication_pb.cjs +0 -194
- package/dist/proto/track_publication_pb.cjs.map +0 -1
- package/dist/proto/track_publication_pb.d.cts +0 -135
- package/dist/proto/track_publication_pb.d.ts +0 -135
- package/dist/proto/track_publication_pb.d.ts.map +0 -1
- package/dist/proto/track_publication_pb.js +0 -164
- package/dist/proto/track_publication_pb.js.map +0 -1
- package/dist/proto/video_frame_pb.cjs +0 -723
- package/dist/proto/video_frame_pb.cjs.map +0 -1
- package/dist/proto/video_frame_pb.d.cts +0 -667
- package/dist/proto/video_frame_pb.d.ts +0 -667
- package/dist/proto/video_frame_pb.d.ts.map +0 -1
- package/dist/proto/video_frame_pb.js +0 -673
- package/dist/proto/video_frame_pb.js.map +0 -1
- package/src/lib.rs +0 -9
- package/src/napi/native.cjs +0 -301
- package/src/napi/native.d.ts +0 -19
- package/src/napi/native.js +0 -15
- package/src/nodejs.rs +0 -134
- package/src/proto/audio_frame_pb.ts +0 -2270
- package/src/proto/data_stream_pb.ts +0 -2674
- package/src/proto/e2ee_pb.ts +0 -1231
- package/src/proto/ffi_pb.ts +0 -1700
- package/src/proto/handle_pb.ts +0 -69
- package/src/proto/participant_pb.ts +0 -351
- package/src/proto/room_pb.ts +0 -5661
- package/src/proto/rpc_pb.ts +0 -539
- package/src/proto/stats_pb.ts +0 -3050
- package/src/proto/track_pb.ts +0 -1040
- package/src/proto/track_publication_pb.ts +0 -284
- package/src/proto/video_frame_pb.ts +0 -1236
package/src/audio_mixer.ts
CHANGED
|
@@ -239,7 +239,7 @@ export class AudioMixer {
|
|
|
239
239
|
|
|
240
240
|
for (const result of results) {
|
|
241
241
|
if (result.status !== 'fulfilled') {
|
|
242
|
-
log.warn('AudioMixer: Stream contribution failed
|
|
242
|
+
log.warn({ reason: result.reason }, 'AudioMixer: Stream contribution failed');
|
|
243
243
|
continue;
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -311,7 +311,7 @@ export class AudioMixer {
|
|
|
311
311
|
// Accumulate data until we have at least chunkSize samples
|
|
312
312
|
while (buf.length < this.chunkSize * this.numChannels && !exhausted && !this.closed) {
|
|
313
313
|
try {
|
|
314
|
-
const result = await
|
|
314
|
+
const result = await this.timeoutRace(iterator.next(), this.streamTimeoutMs);
|
|
315
315
|
|
|
316
316
|
if (result === 'timeout') {
|
|
317
317
|
console.warn(`AudioMixer: stream timeout after ${this.streamTimeoutMs}ms`);
|
|
@@ -412,7 +412,13 @@ export class AudioMixer {
|
|
|
412
412
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
|
|
416
|
-
|
|
415
|
+
/** Race a promise against a timeout. The losing setTimeout is automatically
|
|
416
|
+
* cleared via `.finally()` so callers don't need to manage cleanup. */
|
|
417
|
+
private timeoutRace<T>(promise: Promise<T>, ms: number): Promise<T | 'timeout'> {
|
|
418
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
419
|
+
const timeoutPromise = new Promise<'timeout'>((resolve) => {
|
|
420
|
+
timer = setTimeout(() => resolve('timeout'), ms);
|
|
421
|
+
});
|
|
422
|
+
return Promise.race([promise.finally(() => clearTimeout(timer)), timeoutPromise]);
|
|
417
423
|
}
|
|
418
424
|
}
|
package/src/audio_resampler.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import { AudioFrame } from './audio_frame.js';
|
|
5
|
-
import { FfiClient, FfiHandle } from './ffi_client.js';
|
|
6
4
|
import type {
|
|
7
5
|
FlushSoxResamplerResponse,
|
|
8
6
|
NewSoxResamplerResponse,
|
|
9
7
|
PushSoxResamplerResponse,
|
|
10
|
-
} from '
|
|
8
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
11
9
|
import {
|
|
12
10
|
FlushSoxResamplerRequest,
|
|
13
11
|
NewSoxResamplerRequest,
|
|
14
12
|
PushSoxResamplerRequest,
|
|
15
13
|
SoxQualityRecipe,
|
|
16
14
|
SoxResamplerDataType,
|
|
17
|
-
} from '
|
|
15
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
16
|
+
import { AudioFrame } from './audio_frame.js';
|
|
17
|
+
import { FfiClient, FfiHandle } from './ffi_client.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Resampler quality. Higher quality settings result in better audio quality but require more
|
|
@@ -97,6 +97,14 @@ export class AudioResampler {
|
|
|
97
97
|
return this.#channels;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Releases the underlying native resampler handle. Must be called when
|
|
102
|
+
* the resampler is no longer needed to avoid leaking the FD.
|
|
103
|
+
*/
|
|
104
|
+
close() {
|
|
105
|
+
this.#ffiHandle.dispose();
|
|
106
|
+
}
|
|
107
|
+
|
|
100
108
|
/**
|
|
101
109
|
* Push audio data into the resampler and retrieve any available resampled data.
|
|
102
110
|
*
|
|
@@ -131,12 +139,14 @@ export class AudioResampler {
|
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
const outputData = FfiClient.instance.copyBuffer(res.outputPtr, res.size!);
|
|
142
|
+
|
|
134
143
|
return [
|
|
135
144
|
new AudioFrame(
|
|
136
145
|
new Int16Array(outputData.buffer),
|
|
137
146
|
this.#outputRate,
|
|
138
147
|
this.#channels,
|
|
139
148
|
Math.trunc(outputData.length / this.#channels / 2),
|
|
149
|
+
data.userdata,
|
|
140
150
|
),
|
|
141
151
|
];
|
|
142
152
|
}
|
package/src/audio_source.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import type { AudioFrame } from './audio_frame.js';
|
|
5
|
-
import { FfiClient } from './ffi_client.js';
|
|
6
|
-
import { FfiHandle } from './napi/native.js';
|
|
7
4
|
import type {
|
|
8
5
|
AudioSourceInfo,
|
|
9
6
|
CaptureAudioFrameCallback,
|
|
10
7
|
CaptureAudioFrameResponse,
|
|
11
8
|
ClearAudioBufferResponse,
|
|
12
9
|
NewAudioSourceResponse,
|
|
13
|
-
} from '
|
|
10
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
14
11
|
import {
|
|
15
12
|
AudioSourceType,
|
|
16
13
|
CaptureAudioFrameRequest,
|
|
17
14
|
ClearAudioBufferRequest,
|
|
15
|
+
FfiHandle,
|
|
18
16
|
NewAudioSourceRequest,
|
|
19
|
-
} from '
|
|
17
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
18
|
+
import type { AudioFrame } from './audio_frame.js';
|
|
19
|
+
import { FfiClient } from './ffi_client.js';
|
|
20
20
|
|
|
21
21
|
export class AudioSource {
|
|
22
22
|
/** @internal */
|
|
@@ -145,6 +145,14 @@ export class AudioSource {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
async close() {
|
|
148
|
+
// Clear any pending playout timeout so its callback doesn't fire after
|
|
149
|
+
// the handle is disposed, which would reference freed native state.
|
|
150
|
+
if (this.timeout) {
|
|
151
|
+
clearTimeout(this.timeout);
|
|
152
|
+
this.timeout = undefined;
|
|
153
|
+
}
|
|
154
|
+
// Resolve any pending waitForPlayout() promise so callers don't hang.
|
|
155
|
+
this.release();
|
|
148
156
|
this.ffiHandle.dispose();
|
|
149
157
|
this.closed = true;
|
|
150
158
|
}
|
package/src/audio_stream.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { NewAudioStreamResponse } from '@livekit/rtc-ffi-bindings';
|
|
5
|
+
import { AudioStreamType, NewAudioStreamRequest } from '@livekit/rtc-ffi-bindings';
|
|
4
6
|
import type { UnderlyingSource } from 'node:stream/web';
|
|
5
7
|
import { AudioFrame } from './audio_frame.js';
|
|
6
8
|
import type { FfiEvent } from './ffi_client.js';
|
|
7
9
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
8
|
-
import { FrameProcessor } from './frame_processor.js';
|
|
10
|
+
import { type FrameProcessor, isFrameProcessor } from './frame_processor.js';
|
|
9
11
|
import { log } from './log.js';
|
|
10
|
-
import type { NewAudioStreamResponse } from './proto/audio_frame_pb.js';
|
|
11
|
-
import { AudioStreamType, NewAudioStreamRequest } from './proto/audio_frame_pb.js';
|
|
12
12
|
import type { Track } from './track.js';
|
|
13
13
|
|
|
14
14
|
export interface AudioStreamOptions {
|
|
@@ -27,6 +27,7 @@ export interface NoiseCancellationOptions {
|
|
|
27
27
|
class AudioStreamSource implements UnderlyingSource<AudioFrame> {
|
|
28
28
|
private controller?: ReadableStreamDefaultController<AudioFrame>;
|
|
29
29
|
private ffiHandle: FfiHandle;
|
|
30
|
+
private disposed = false;
|
|
30
31
|
private sampleRate: number;
|
|
31
32
|
private numChannels: number;
|
|
32
33
|
private legacyNcOptions?: NoiseCancellationOptions;
|
|
@@ -41,7 +42,7 @@ class AudioStreamSource implements UnderlyingSource<AudioFrame> {
|
|
|
41
42
|
if (sampleRateOrOptions !== undefined && typeof sampleRateOrOptions !== 'number') {
|
|
42
43
|
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48000;
|
|
43
44
|
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
|
|
44
|
-
if (sampleRateOrOptions.noiseCancellation
|
|
45
|
+
if (isFrameProcessor(sampleRateOrOptions.noiseCancellation)) {
|
|
45
46
|
this.frameProcessor = sampleRateOrOptions.noiseCancellation;
|
|
46
47
|
} else {
|
|
47
48
|
this.legacyNcOptions = sampleRateOrOptions.noiseCancellation;
|
|
@@ -106,7 +107,15 @@ class AudioStreamSource implements UnderlyingSource<AudioFrame> {
|
|
|
106
107
|
case 'eos':
|
|
107
108
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
108
109
|
this.controller.close();
|
|
109
|
-
|
|
110
|
+
// Dispose the native handle so the FD is released on stream end,
|
|
111
|
+
// not just when cancel() is called explicitly by the consumer.
|
|
112
|
+
// Guard against double-dispose if cancel() is called after EOS
|
|
113
|
+
// while buffered frames are still in the ReadableStream queue.
|
|
114
|
+
if (!this.disposed) {
|
|
115
|
+
this.disposed = true;
|
|
116
|
+
this.ffiHandle.dispose();
|
|
117
|
+
this.frameProcessor?.close();
|
|
118
|
+
}
|
|
110
119
|
break;
|
|
111
120
|
}
|
|
112
121
|
};
|
|
@@ -117,7 +126,13 @@ class AudioStreamSource implements UnderlyingSource<AudioFrame> {
|
|
|
117
126
|
|
|
118
127
|
cancel() {
|
|
119
128
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
120
|
-
this.
|
|
129
|
+
if (!this.disposed) {
|
|
130
|
+
this.disposed = true;
|
|
131
|
+
this.ffiHandle.dispose();
|
|
132
|
+
// Also close the frame processor on cancel for symmetry with the EOS path,
|
|
133
|
+
// so resources are released regardless of how the stream ends.
|
|
134
|
+
this.frameProcessor?.close();
|
|
135
|
+
}
|
|
121
136
|
}
|
|
122
137
|
}
|
|
123
138
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { DataStream_Chunk } from '@livekit/rtc-ffi-bindings';
|
|
4
5
|
import { log } from '../log.js';
|
|
5
|
-
import type { DataStream_Chunk } from '../proto/room_pb.js';
|
|
6
6
|
import { bigIntToNumber } from '../utils.js';
|
|
7
7
|
import type { BaseStreamInfo, ByteStreamInfo, TextStreamInfo } from './types.js';
|
|
8
8
|
|
|
@@ -53,12 +53,18 @@ export class ByteStreamReader extends BaseStreamReader<ByteStreamInfo> {
|
|
|
53
53
|
try {
|
|
54
54
|
const { done, value } = await reader.read();
|
|
55
55
|
if (done) {
|
|
56
|
+
// Release the lock when the stream is exhausted so the
|
|
57
|
+
// underlying ReadableStream can be garbage-collected.
|
|
58
|
+
reader.releaseLock();
|
|
56
59
|
return { done: true, value: undefined as unknown };
|
|
57
60
|
} else {
|
|
58
61
|
this.handleChunkReceived(value);
|
|
59
62
|
return { done: false, value: value.content! };
|
|
60
63
|
}
|
|
61
64
|
} catch (error: unknown) {
|
|
65
|
+
// Release the lock on error so it doesn't stay held when the
|
|
66
|
+
// consumer never calls return() (e.g. breaking out of for-await).
|
|
67
|
+
reader.releaseLock();
|
|
62
68
|
log.error('error processing stream update: %s', error);
|
|
63
69
|
return { done: true, value: undefined as unknown };
|
|
64
70
|
}
|
|
@@ -121,12 +127,18 @@ export class TextStreamReader extends BaseStreamReader<TextStreamInfo> {
|
|
|
121
127
|
[Symbol.asyncIterator]() {
|
|
122
128
|
const reader = this.reader.getReader();
|
|
123
129
|
const decoder = new TextDecoder();
|
|
130
|
+
const receivedChunks = this.receivedChunks;
|
|
124
131
|
|
|
125
132
|
return {
|
|
126
133
|
next: async (): Promise<IteratorResult<string>> => {
|
|
127
134
|
try {
|
|
128
135
|
const { done, value } = await reader.read();
|
|
129
136
|
if (done) {
|
|
137
|
+
// Release the lock when the stream is exhausted so the
|
|
138
|
+
// underlying ReadableStream can be garbage-collected.
|
|
139
|
+
reader.releaseLock();
|
|
140
|
+
// Clear received chunks so the buffered data can be GC'd.
|
|
141
|
+
receivedChunks.clear();
|
|
130
142
|
return { done: true, value: undefined };
|
|
131
143
|
} else {
|
|
132
144
|
this.handleChunkReceived(value);
|
|
@@ -136,6 +148,10 @@ export class TextStreamReader extends BaseStreamReader<TextStreamInfo> {
|
|
|
136
148
|
};
|
|
137
149
|
}
|
|
138
150
|
} catch (error: unknown) {
|
|
151
|
+
// Release the lock on error so it doesn't stay held when the
|
|
152
|
+
// consumer never calls return() (e.g. breaking out of for-await).
|
|
153
|
+
reader.releaseLock();
|
|
154
|
+
receivedChunks.clear();
|
|
139
155
|
log.error('error processing stream update: %s', error);
|
|
140
156
|
return { done: true, value: undefined };
|
|
141
157
|
}
|
|
@@ -143,6 +159,8 @@ export class TextStreamReader extends BaseStreamReader<TextStreamInfo> {
|
|
|
143
159
|
|
|
144
160
|
return(): IteratorResult<string> {
|
|
145
161
|
reader.releaseLock();
|
|
162
|
+
// Clear received chunks so the buffered data can be GC'd.
|
|
163
|
+
receivedChunks.clear();
|
|
146
164
|
return { done: true, value: undefined };
|
|
147
165
|
},
|
|
148
166
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import type { DataStream_Chunk, DataStream_Header } from '
|
|
4
|
+
import type { DataStream_Chunk, DataStream_Header } from '@livekit/rtc-ffi-bindings';
|
|
5
5
|
import type { ByteStreamReader, TextStreamReader } from './stream_reader.js';
|
|
6
6
|
|
|
7
7
|
export interface StreamController<T extends DataStream_Chunk> {
|
package/src/e2ee.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import { FfiClient } from './ffi_client.js';
|
|
5
4
|
import type {
|
|
6
5
|
E2eeManagerGetFrameCryptorsResponse,
|
|
7
6
|
E2eeResponse,
|
|
@@ -9,7 +8,7 @@ import type {
|
|
|
9
8
|
GetSharedKeyResponse,
|
|
10
9
|
RatchetKeyResponse,
|
|
11
10
|
RatchetSharedKeyResponse,
|
|
12
|
-
} from '
|
|
11
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
13
12
|
import {
|
|
14
13
|
E2eeManagerSetEnabledRequest,
|
|
15
14
|
E2eeRequest,
|
|
@@ -22,7 +21,8 @@ import {
|
|
|
22
21
|
RatchetSharedKeyRequest,
|
|
23
22
|
SetKeyRequest,
|
|
24
23
|
SetSharedKeyRequest,
|
|
25
|
-
} from '
|
|
24
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
25
|
+
import { FfiClient } from './ffi_client.js';
|
|
26
26
|
|
|
27
27
|
const DEFAULT_RATCHET_SALT = new TextEncoder().encode('LKFrameEncryptionKey');
|
|
28
28
|
const DEFAULT_RATCHET_WINDOW_SIZE = 16;
|
package/src/ffi_client.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import type { PartialMessage } from '@bufbuild/protobuf';
|
|
5
|
-
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
6
|
-
import EventEmitter from 'events';
|
|
7
4
|
import {
|
|
5
|
+
FfiEvent,
|
|
8
6
|
FfiHandle,
|
|
7
|
+
FfiRequest,
|
|
8
|
+
FfiResponse,
|
|
9
|
+
type PartialMessage,
|
|
9
10
|
livekitCopyBuffer,
|
|
10
11
|
livekitDispose,
|
|
11
12
|
livekitFfiRequest,
|
|
12
13
|
livekitInitialize,
|
|
13
14
|
livekitRetrievePtr,
|
|
14
|
-
} from '
|
|
15
|
-
import {
|
|
15
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
16
|
+
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
17
|
+
import EventEmitter from 'events';
|
|
16
18
|
import { SDK_VERSION } from './version.js';
|
|
17
19
|
|
|
18
20
|
export { FfiHandle, type FfiEvent, type FfiResponse, FfiRequest, livekitDispose as dispose };
|
|
@@ -68,14 +70,37 @@ export class FfiClient extends (EventEmitter as new () => TypedEmitter<FfiClient
|
|
|
68
70
|
return livekitRetrievePtr(data);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
async waitFor<T>(
|
|
72
|
-
|
|
73
|
+
async waitFor<T>(
|
|
74
|
+
predicate: (ev: FfiEvent) => boolean,
|
|
75
|
+
options?: { signal?: AbortSignal },
|
|
76
|
+
): Promise<T> {
|
|
77
|
+
return new Promise<T>((resolve, reject) => {
|
|
73
78
|
const listener = (ev: FfiEvent) => {
|
|
74
79
|
if (predicate(ev)) {
|
|
75
|
-
|
|
80
|
+
cleanup();
|
|
76
81
|
resolve(ev.message.value as T);
|
|
77
82
|
}
|
|
78
83
|
};
|
|
84
|
+
|
|
85
|
+
const cleanup = () => {
|
|
86
|
+
this.off(FfiClientEvent.FfiEvent, listener);
|
|
87
|
+
options?.signal?.removeEventListener('abort', onAbort);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// If an AbortSignal is provided, remove the listener when the signal
|
|
91
|
+
// fires so that pending waitFor() calls don't leak listeners after
|
|
92
|
+
// the room disconnects or the operation is cancelled.
|
|
93
|
+
const onAbort = () => {
|
|
94
|
+
cleanup();
|
|
95
|
+
reject(options?.signal?.reason ?? new Error('waitFor aborted'));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (options?.signal?.aborted) {
|
|
99
|
+
reject(options.signal.reason ?? new Error('waitFor aborted'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
options?.signal?.addEventListener('abort', onAbort);
|
|
79
104
|
this.on(FfiClientEvent.FfiEvent, listener);
|
|
80
105
|
});
|
|
81
106
|
}
|
package/src/frame_processor.ts
CHANGED
|
@@ -15,7 +15,28 @@ export type FrameProcessorCredentials = {
|
|
|
15
15
|
url: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
export const FrameProcessorSymbol = Symbol.for('lk.frame-processor');
|
|
19
|
+
|
|
20
|
+
export function isFrameProcessor<
|
|
21
|
+
T extends FrameProcessor<AudioFrame> | FrameProcessor<VideoFrame> | unknown,
|
|
22
|
+
>(
|
|
23
|
+
maybeProcessor: unknown,
|
|
24
|
+
): maybeProcessor is T extends FrameProcessor<AudioFrame>
|
|
25
|
+
? FrameProcessor<AudioFrame>
|
|
26
|
+
: T extends FrameProcessor<VideoFrame>
|
|
27
|
+
? FrameProcessor<VideoFrame>
|
|
28
|
+
: FrameProcessor<AudioFrame> | FrameProcessor<VideoFrame> {
|
|
29
|
+
return (
|
|
30
|
+
maybeProcessor !== null &&
|
|
31
|
+
typeof maybeProcessor === 'object' &&
|
|
32
|
+
'symbol' in maybeProcessor &&
|
|
33
|
+
maybeProcessor.symbol === FrameProcessorSymbol
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
18
37
|
export abstract class FrameProcessor<Frame extends VideoFrame | AudioFrame> {
|
|
38
|
+
readonly symbol = FrameProcessorSymbol;
|
|
39
|
+
|
|
19
40
|
abstract isEnabled(): boolean;
|
|
20
41
|
abstract setEnabled(enabled: boolean): void;
|
|
21
42
|
|
package/src/index.ts
CHANGED
|
@@ -14,8 +14,8 @@ export { E2EEManager, FrameCryptor, KeyProvider } from './e2ee.js';
|
|
|
14
14
|
export type { E2EEOptions, KeyProviderOptions } from './e2ee.js';
|
|
15
15
|
export { dispose } from './ffi_client.js';
|
|
16
16
|
export { LocalParticipant, Participant, RemoteParticipant } from './participant.js';
|
|
17
|
-
export { EncryptionState, EncryptionType } from '
|
|
18
|
-
export { DisconnectReason, ParticipantKind } from '
|
|
17
|
+
export { EncryptionState, EncryptionType } from '@livekit/rtc-ffi-bindings';
|
|
18
|
+
export { DisconnectReason, ParticipantKind } from '@livekit/rtc-ffi-bindings';
|
|
19
19
|
export {
|
|
20
20
|
ConnectionQuality,
|
|
21
21
|
ConnectionState,
|
|
@@ -24,9 +24,9 @@ export {
|
|
|
24
24
|
IceServer,
|
|
25
25
|
IceTransportType,
|
|
26
26
|
TrackPublishOptions,
|
|
27
|
-
} from '
|
|
28
|
-
export { StreamState, TrackKind, TrackSource } from '
|
|
29
|
-
export { VideoBufferType, VideoCodec, VideoRotation } from '
|
|
27
|
+
} from '@livekit/rtc-ffi-bindings';
|
|
28
|
+
export { StreamState, TrackKind, TrackSource } from '@livekit/rtc-ffi-bindings';
|
|
29
|
+
export { VideoBufferType, VideoCodec, VideoRotation } from '@livekit/rtc-ffi-bindings';
|
|
30
30
|
export { ConnectError, Room, RoomEvent, type RoomOptions, type RtcConfiguration } from './room.js';
|
|
31
31
|
export { RpcError, type PerformRpcParams, type RpcInvocationData } from './rpc.js';
|
|
32
32
|
export {
|
|
@@ -54,4 +54,5 @@ export {
|
|
|
54
54
|
FrameProcessor,
|
|
55
55
|
type FrameProcessorStreamInfo,
|
|
56
56
|
type FrameProcessorCredentials,
|
|
57
|
+
isFrameProcessor,
|
|
57
58
|
} from './frame_processor.js';
|