@livekit/rtc-node 0.13.24 → 0.13.25
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 +1 -1
- package/dist/audio_mixer.cjs.map +1 -1
- package/dist/audio_mixer.d.cts +1 -6
- package/dist/audio_mixer.d.ts +1 -6
- package/dist/audio_mixer.js +1 -1
- 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 +11 -12
- package/dist/ffi_client.cjs.map +1 -1
- package/dist/ffi_client.d.cts +3 -16
- package/dist/ffi_client.d.ts +3 -16
- package/dist/ffi_client.d.ts.map +1 -1
- package/dist/ffi_client.js +5 -3
- 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 +38 -38
- package/dist/participant.cjs.map +1 -1
- package/dist/participant.d.cts +2 -15
- package/dist/participant.d.ts +2 -15
- package/dist/participant.d.ts.map +1 -1
- package/dist/participant.js +12 -12
- package/dist/participant.js.map +1 -1
- package/dist/room.cjs +14 -14
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.cts +2 -15
- package/dist/room.d.ts +2 -15
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +10 -10
- 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.ts +1 -1
- 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 +7 -5
- package/src/frame_processor.ts +21 -0
- package/src/index.ts +6 -5
- package/src/participant.ts +17 -17
- package/src/room.ts +22 -22
- package/src/rpc.ts +1 -1
- package/src/tests/e2e.test.ts +11 -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/dist/video_source.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { FfiClient, FfiHandle } from "./ffi_client.js";
|
|
2
1
|
import {
|
|
3
2
|
CaptureVideoFrameRequest,
|
|
4
3
|
NewVideoSourceRequest,
|
|
5
4
|
VideoRotation,
|
|
6
5
|
VideoSourceType
|
|
7
|
-
} from "
|
|
6
|
+
} from "@livekit/rtc-ffi-bindings";
|
|
7
|
+
import { FfiClient, FfiHandle } from "./ffi_client.js";
|
|
8
8
|
class VideoSource {
|
|
9
9
|
constructor(width, height) {
|
|
10
10
|
/** @internal */
|
package/dist/video_source.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/video_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport
|
|
1
|
+
{"version":3,"sources":["../src/video_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n CaptureVideoFrameResponse,\n NewVideoSourceResponse,\n VideoSourceInfo,\n} from '@livekit/rtc-ffi-bindings';\nimport {\n CaptureVideoFrameRequest,\n NewVideoSourceRequest,\n VideoRotation,\n VideoSourceType,\n} from '@livekit/rtc-ffi-bindings';\nimport { FfiClient, FfiHandle } from './ffi_client.js';\nimport type { VideoFrame } from './video_frame.js';\n\nexport class VideoSource {\n /** @internal */\n info?: VideoSourceInfo;\n /** @internal */\n ffiHandle: FfiHandle;\n /** @internal */\n closed = false;\n\n width: number;\n height: number;\n\n constructor(width: number, height: number) {\n this.width = width;\n this.height = height;\n\n const req = new NewVideoSourceRequest({\n type: VideoSourceType.VIDEO_SOURCE_NATIVE,\n resolution: {\n width: width,\n height: height,\n },\n });\n\n const res = FfiClient.instance.request<NewVideoSourceResponse>({\n message: {\n case: 'newVideoSource',\n value: req,\n },\n });\n\n this.info = res.source?.info;\n this.ffiHandle = new FfiHandle(res.source!.handle!.id!);\n }\n\n captureFrame(\n frame: VideoFrame,\n timestampUs = BigInt(0),\n rotation = VideoRotation.VIDEO_ROTATION_0,\n ) {\n if (this.closed) {\n throw new Error('VideoSource is closed');\n }\n const req = new CaptureVideoFrameRequest({\n sourceHandle: this.ffiHandle.handle,\n buffer: frame.protoInfo(),\n rotation,\n timestampUs,\n });\n\n FfiClient.instance.request<CaptureVideoFrameResponse>({\n message: { case: 'captureVideoFrame', value: req },\n });\n }\n\n async close() {\n this.ffiHandle.dispose();\n this.closed = true;\n }\n}\n"],"mappings":"AAQA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW,iBAAiB;AAG9B,MAAM,YAAY;AAAA,EAWvB,YAAY,OAAe,QAAgB;AAL3C;AAAA,kBAAS;AAvBX;AA6BI,SAAK,QAAQ;AACb,SAAK,SAAS;AAEd,UAAM,MAAM,IAAI,sBAAsB;AAAA,MACpC,MAAM,gBAAgB;AAAA,MACtB,YAAY;AAAA,QACV;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,MAAM,UAAU,SAAS,QAAgC;AAAA,MAC7D,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,QAAO,SAAI,WAAJ,mBAAY;AACxB,SAAK,YAAY,IAAI,UAAU,IAAI,OAAQ,OAAQ,EAAG;AAAA,EACxD;AAAA,EAEA,aACE,OACA,cAAc,OAAO,CAAC,GACtB,WAAW,cAAc,kBACzB;AACA,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AACA,UAAM,MAAM,IAAI,yBAAyB;AAAA,MACvC,cAAc,KAAK,UAAU;AAAA,MAC7B,QAAQ,MAAM,UAAU;AAAA,MACxB;AAAA,MACA;AAAA,IACF,CAAC;AAED,cAAU,SAAS,QAAmC;AAAA,MACpD,SAAS,EAAE,MAAM,qBAAqB,OAAO,IAAI;AAAA,IACnD,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ;AACZ,SAAK,UAAU,QAAQ;AACvB,SAAK,SAAS;AAAA,EAChB;AACF;","names":[]}
|
package/dist/video_stream.cjs
CHANGED
|
@@ -21,11 +21,12 @@ __export(video_stream_exports, {
|
|
|
21
21
|
VideoStream: () => VideoStream
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(video_stream_exports);
|
|
24
|
+
var import_rtc_ffi_bindings = require("@livekit/rtc-ffi-bindings");
|
|
24
25
|
var import_ffi_client = require("./ffi_client.cjs");
|
|
25
|
-
var import_video_frame_pb = require("./proto/video_frame_pb.cjs");
|
|
26
26
|
var import_video_frame = require("./video_frame.cjs");
|
|
27
27
|
class VideoStreamSource {
|
|
28
28
|
constructor(track) {
|
|
29
|
+
this.disposed = false;
|
|
29
30
|
this.onEvent = (ev) => {
|
|
30
31
|
if (!this.controller) {
|
|
31
32
|
throw new Error("Stream controller not initialized");
|
|
@@ -50,11 +51,15 @@ class VideoStreamSource {
|
|
|
50
51
|
case "eos":
|
|
51
52
|
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
52
53
|
this.controller.close();
|
|
54
|
+
if (!this.disposed) {
|
|
55
|
+
this.disposed = true;
|
|
56
|
+
this.ffiHandle.dispose();
|
|
57
|
+
}
|
|
53
58
|
break;
|
|
54
59
|
}
|
|
55
60
|
};
|
|
56
|
-
const req = new
|
|
57
|
-
type:
|
|
61
|
+
const req = new import_rtc_ffi_bindings.NewVideoStreamRequest({
|
|
62
|
+
type: import_rtc_ffi_bindings.VideoStreamType.VIDEO_STREAM_NATIVE,
|
|
58
63
|
trackHandle: track.ffi_handle.handle
|
|
59
64
|
});
|
|
60
65
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
@@ -71,7 +76,10 @@ class VideoStreamSource {
|
|
|
71
76
|
}
|
|
72
77
|
cancel() {
|
|
73
78
|
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
74
|
-
this.
|
|
79
|
+
if (!this.disposed) {
|
|
80
|
+
this.disposed = true;
|
|
81
|
+
this.ffiHandle.dispose();
|
|
82
|
+
}
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
class VideoStream extends ReadableStream {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { NewVideoStreamResponse, VideoRotation } from '@livekit/rtc-ffi-bindings';\nimport { NewVideoStreamRequest, VideoStreamType } from '@livekit/rtc-ffi-bindings';\nimport type { UnderlyingSource } from 'node:stream/web';\nimport type { FfiEvent } from './ffi_client.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport type { Track } from './track.js';\nimport { VideoFrame } from './video_frame.js';\n\nexport type VideoFrameEvent = {\n frame: VideoFrame;\n timestampUs: bigint;\n rotation: VideoRotation;\n};\n\nclass VideoStreamSource implements UnderlyingSource<VideoFrameEvent> {\n private controller?: ReadableStreamDefaultController<VideoFrameEvent>;\n private ffiHandle: FfiHandle;\n private disposed = false;\n\n constructor(track: Track) {\n const req = new NewVideoStreamRequest({\n type: VideoStreamType.VIDEO_STREAM_NATIVE,\n trackHandle: track.ffi_handle.handle,\n });\n\n const res = FfiClient.instance.request<NewVideoStreamResponse>({\n message: {\n case: 'newVideoStream',\n value: req,\n },\n });\n\n this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);\n FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);\n }\n\n private onEvent = (ev: FfiEvent) => {\n if (!this.controller) {\n throw new Error('Stream controller not initialized');\n }\n\n if (\n ev.message.case != 'videoStreamEvent' ||\n ev.message.value.streamHandle != this.ffiHandle.handle\n ) {\n return;\n }\n\n const streamEvent = ev.message.value.message;\n switch (streamEvent.case) {\n case 'frameReceived':\n const rotation = streamEvent.value.rotation;\n const timestampUs = streamEvent.value.timestampUs;\n const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer!);\n const value = { rotation, timestampUs, frame };\n const videoFrameEvent = {\n frame: value.frame,\n timestampUs: value.timestampUs!,\n rotation: value.rotation!,\n };\n this.controller.enqueue(videoFrameEvent);\n break;\n case 'eos':\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n this.controller.close();\n // Dispose the native handle so the FD is released on stream end,\n // not just when cancel() is called explicitly by the consumer.\n // Guard against double-dispose if cancel() is called after EOS\n // while buffered frames are still in the ReadableStream queue.\n if (!this.disposed) {\n this.disposed = true;\n this.ffiHandle.dispose();\n }\n break;\n }\n };\n\n start(controller: ReadableStreamDefaultController<VideoFrameEvent>) {\n this.controller = controller;\n }\n\n cancel() {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n if (!this.disposed) {\n this.disposed = true;\n this.ffiHandle.dispose();\n }\n }\n}\n\nexport class VideoStream extends ReadableStream<VideoFrameEvent> {\n constructor(track: Track) {\n super(new VideoStreamSource(track));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,8BAAuD;AAGvD,wBAAqD;AAErD,yBAA2B;AAQ3B,MAAM,kBAA+D;AAAA,EAKnE,YAAY,OAAc;AAF1B,SAAQ,WAAW;AAmBnB,SAAQ,UAAU,CAAC,OAAiB;AAClC,UAAI,CAAC,KAAK,YAAY;AACpB,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,UACE,GAAG,QAAQ,QAAQ,sBACnB,GAAG,QAAQ,MAAM,gBAAgB,KAAK,UAAU,QAChD;AACA;AAAA,MACF;AAEA,YAAM,cAAc,GAAG,QAAQ,MAAM;AACrC,cAAQ,YAAY,MAAM;AAAA,QACxB,KAAK;AACH,gBAAM,WAAW,YAAY,MAAM;AACnC,gBAAM,cAAc,YAAY,MAAM;AACtC,gBAAM,QAAQ,8BAAW,cAAc,YAAY,MAAM,MAAO;AAChE,gBAAM,QAAQ,EAAE,UAAU,aAAa,MAAM;AAC7C,gBAAM,kBAAkB;AAAA,YACtB,OAAO,MAAM;AAAA,YACb,aAAa,MAAM;AAAA,YACnB,UAAU,MAAM;AAAA,UAClB;AACA,eAAK,WAAW,QAAQ,eAAe;AACvC;AAAA,QACF,KAAK;AACH,sCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,eAAK,WAAW,MAAM;AAKtB,cAAI,CAAC,KAAK,UAAU;AAClB,iBAAK,WAAW;AAChB,iBAAK,UAAU,QAAQ;AAAA,UACzB;AACA;AAAA,MACJ;AAAA,IACF;AAvDE,UAAM,MAAM,IAAI,8CAAsB;AAAA,MACpC,MAAM,wCAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,4BAAU,SAAS,QAAgC;AAAA,MAC7D,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,YAAY,IAAI,4BAAU,IAAI,OAAQ,OAAQ,EAAG;AACtD,gCAAU,SAAS,GAAG,iCAAe,UAAU,KAAK,OAAO;AAAA,EAC7D;AAAA,EA2CA,MAAM,YAA8D;AAClE,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AACP,gCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,WAAW;AAChB,WAAK,UAAU,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;AAEO,MAAM,oBAAoB,eAAgC;AAAA,EAC/D,YAAY,OAAc;AACxB,UAAM,IAAI,kBAAkB,KAAK,CAAC;AAAA,EACpC;AACF;","names":[]}
|
package/dist/video_stream.d.cts
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import { VideoRotation } from '
|
|
1
|
+
import { VideoRotation } from '@livekit/rtc-ffi-bindings';
|
|
2
2
|
import { Track } from './track.cjs';
|
|
3
3
|
import { VideoFrame } from './video_frame.cjs';
|
|
4
|
-
import '@bufbuild/protobuf';
|
|
5
|
-
import './proto/track_pb.cjs';
|
|
6
|
-
import './proto/stats_pb.cjs';
|
|
7
|
-
import './proto/e2ee_pb.cjs';
|
|
8
|
-
import './proto/handle_pb.cjs';
|
|
9
4
|
import './audio_source.cjs';
|
|
10
5
|
import './audio_frame.cjs';
|
|
11
|
-
import './proto/audio_frame_pb.cjs';
|
|
12
|
-
import './napi/native.d.cjs';
|
|
13
6
|
import './ffi_client.cjs';
|
|
14
7
|
import '@livekit/typed-emitter';
|
|
15
|
-
import './proto/ffi_pb.cjs';
|
|
16
|
-
import './proto/room_pb.cjs';
|
|
17
|
-
import './proto/participant_pb.cjs';
|
|
18
|
-
import './proto/data_stream_pb.cjs';
|
|
19
|
-
import './proto/rpc_pb.cjs';
|
|
20
|
-
import './proto/track_publication_pb.cjs';
|
|
21
8
|
import './video_source.cjs';
|
|
22
9
|
|
|
23
10
|
type VideoFrameEvent = {
|
package/dist/video_stream.d.ts
CHANGED
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import { VideoRotation } from '
|
|
1
|
+
import { VideoRotation } from '@livekit/rtc-ffi-bindings';
|
|
2
2
|
import { Track } from './track.js';
|
|
3
3
|
import { VideoFrame } from './video_frame.js';
|
|
4
|
-
import '@bufbuild/protobuf';
|
|
5
|
-
import './proto/track_pb.js';
|
|
6
|
-
import './proto/stats_pb.js';
|
|
7
|
-
import './proto/e2ee_pb.js';
|
|
8
|
-
import './proto/handle_pb.js';
|
|
9
4
|
import './audio_source.js';
|
|
10
5
|
import './audio_frame.js';
|
|
11
|
-
import './proto/audio_frame_pb.js';
|
|
12
|
-
import './napi/native.d.js';
|
|
13
6
|
import './ffi_client.js';
|
|
14
7
|
import '@livekit/typed-emitter';
|
|
15
|
-
import './proto/ffi_pb.js';
|
|
16
|
-
import './proto/room_pb.js';
|
|
17
|
-
import './proto/participant_pb.js';
|
|
18
|
-
import './proto/data_stream_pb.js';
|
|
19
|
-
import './proto/rpc_pb.js';
|
|
20
|
-
import './proto/track_publication_pb.js';
|
|
21
8
|
import './video_source.js';
|
|
22
9
|
|
|
23
10
|
type VideoFrameEvent = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA0B,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAKvF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AA8EF,qBAAa,WAAY,SAAQ,cAAc,CAAC,eAAe,CAAC;gBAClD,KAAK,EAAE,KAAK;CAGzB"}
|
package/dist/video_stream.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { NewVideoStreamRequest, VideoStreamType } from "@livekit/rtc-ffi-bindings";
|
|
1
2
|
import { FfiClient, FfiClientEvent, FfiHandle } from "./ffi_client.js";
|
|
2
|
-
import { NewVideoStreamRequest, VideoStreamType } from "./proto/video_frame_pb.js";
|
|
3
3
|
import { VideoFrame } from "./video_frame.js";
|
|
4
4
|
class VideoStreamSource {
|
|
5
5
|
constructor(track) {
|
|
6
|
+
this.disposed = false;
|
|
6
7
|
this.onEvent = (ev) => {
|
|
7
8
|
if (!this.controller) {
|
|
8
9
|
throw new Error("Stream controller not initialized");
|
|
@@ -27,6 +28,10 @@ class VideoStreamSource {
|
|
|
27
28
|
case "eos":
|
|
28
29
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
29
30
|
this.controller.close();
|
|
31
|
+
if (!this.disposed) {
|
|
32
|
+
this.disposed = true;
|
|
33
|
+
this.ffiHandle.dispose();
|
|
34
|
+
}
|
|
30
35
|
break;
|
|
31
36
|
}
|
|
32
37
|
};
|
|
@@ -48,7 +53,10 @@ class VideoStreamSource {
|
|
|
48
53
|
}
|
|
49
54
|
cancel() {
|
|
50
55
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
51
|
-
this.
|
|
56
|
+
if (!this.disposed) {
|
|
57
|
+
this.disposed = true;
|
|
58
|
+
this.ffiHandle.dispose();
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
class VideoStream extends ReadableStream {
|
package/dist/video_stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { NewVideoStreamResponse, VideoRotation } from '@livekit/rtc-ffi-bindings';\nimport { NewVideoStreamRequest, VideoStreamType } from '@livekit/rtc-ffi-bindings';\nimport type { UnderlyingSource } from 'node:stream/web';\nimport type { FfiEvent } from './ffi_client.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport type { Track } from './track.js';\nimport { VideoFrame } from './video_frame.js';\n\nexport type VideoFrameEvent = {\n frame: VideoFrame;\n timestampUs: bigint;\n rotation: VideoRotation;\n};\n\nclass VideoStreamSource implements UnderlyingSource<VideoFrameEvent> {\n private controller?: ReadableStreamDefaultController<VideoFrameEvent>;\n private ffiHandle: FfiHandle;\n private disposed = false;\n\n constructor(track: Track) {\n const req = new NewVideoStreamRequest({\n type: VideoStreamType.VIDEO_STREAM_NATIVE,\n trackHandle: track.ffi_handle.handle,\n });\n\n const res = FfiClient.instance.request<NewVideoStreamResponse>({\n message: {\n case: 'newVideoStream',\n value: req,\n },\n });\n\n this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);\n FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);\n }\n\n private onEvent = (ev: FfiEvent) => {\n if (!this.controller) {\n throw new Error('Stream controller not initialized');\n }\n\n if (\n ev.message.case != 'videoStreamEvent' ||\n ev.message.value.streamHandle != this.ffiHandle.handle\n ) {\n return;\n }\n\n const streamEvent = ev.message.value.message;\n switch (streamEvent.case) {\n case 'frameReceived':\n const rotation = streamEvent.value.rotation;\n const timestampUs = streamEvent.value.timestampUs;\n const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer!);\n const value = { rotation, timestampUs, frame };\n const videoFrameEvent = {\n frame: value.frame,\n timestampUs: value.timestampUs!,\n rotation: value.rotation!,\n };\n this.controller.enqueue(videoFrameEvent);\n break;\n case 'eos':\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n this.controller.close();\n // Dispose the native handle so the FD is released on stream end,\n // not just when cancel() is called explicitly by the consumer.\n // Guard against double-dispose if cancel() is called after EOS\n // while buffered frames are still in the ReadableStream queue.\n if (!this.disposed) {\n this.disposed = true;\n this.ffiHandle.dispose();\n }\n break;\n }\n };\n\n start(controller: ReadableStreamDefaultController<VideoFrameEvent>) {\n this.controller = controller;\n }\n\n cancel() {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n if (!this.disposed) {\n this.disposed = true;\n this.ffiHandle.dispose();\n }\n }\n}\n\nexport class VideoStream extends ReadableStream<VideoFrameEvent> {\n constructor(track: Track) {\n super(new VideoStreamSource(track));\n }\n}\n"],"mappings":"AAIA,SAAS,uBAAuB,uBAAuB;AAGvD,SAAS,WAAW,gBAAgB,iBAAiB;AAErD,SAAS,kBAAkB;AAQ3B,MAAM,kBAA+D;AAAA,EAKnE,YAAY,OAAc;AAF1B,SAAQ,WAAW;AAmBnB,SAAQ,UAAU,CAAC,OAAiB;AAClC,UAAI,CAAC,KAAK,YAAY;AACpB,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,UACE,GAAG,QAAQ,QAAQ,sBACnB,GAAG,QAAQ,MAAM,gBAAgB,KAAK,UAAU,QAChD;AACA;AAAA,MACF;AAEA,YAAM,cAAc,GAAG,QAAQ,MAAM;AACrC,cAAQ,YAAY,MAAM;AAAA,QACxB,KAAK;AACH,gBAAM,WAAW,YAAY,MAAM;AACnC,gBAAM,cAAc,YAAY,MAAM;AACtC,gBAAM,QAAQ,WAAW,cAAc,YAAY,MAAM,MAAO;AAChE,gBAAM,QAAQ,EAAE,UAAU,aAAa,MAAM;AAC7C,gBAAM,kBAAkB;AAAA,YACtB,OAAO,MAAM;AAAA,YACb,aAAa,MAAM;AAAA,YACnB,UAAU,MAAM;AAAA,UAClB;AACA,eAAK,WAAW,QAAQ,eAAe;AACvC;AAAA,QACF,KAAK;AACH,oBAAU,SAAS,IAAI,eAAe,UAAU,KAAK,OAAO;AAC5D,eAAK,WAAW,MAAM;AAKtB,cAAI,CAAC,KAAK,UAAU;AAClB,iBAAK,WAAW;AAChB,iBAAK,UAAU,QAAQ;AAAA,UACzB;AACA;AAAA,MACJ;AAAA,IACF;AAvDE,UAAM,MAAM,IAAI,sBAAsB;AAAA,MACpC,MAAM,gBAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,UAAU,SAAS,QAAgC;AAAA,MAC7D,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,YAAY,IAAI,UAAU,IAAI,OAAQ,OAAQ,EAAG;AACtD,cAAU,SAAS,GAAG,eAAe,UAAU,KAAK,OAAO;AAAA,EAC7D;AAAA,EA2CA,MAAM,YAA8D;AAClE,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AACP,cAAU,SAAS,IAAI,eAAe,UAAU,KAAK,OAAO;AAC5D,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,WAAW;AAChB,WAAK,UAAU,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;AAEO,MAAM,oBAAoB,eAAgC;AAAA,EAC/D,YAAY,OAAc;AACxB,UAAM,IAAI,kBAAkB,KAAK,CAAC;AAAA,EACpC;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "LiveKit RTC Node",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "LiveKit",
|
|
6
|
-
"version": "0.13.
|
|
6
|
+
"version": "0.13.25",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"require": "dist/index.cjs",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
@@ -27,62 +27,32 @@
|
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
|
-
"src"
|
|
31
|
-
"build.rs",
|
|
32
|
-
"Cargo.toml",
|
|
33
|
-
"Cargo.lock"
|
|
30
|
+
"src"
|
|
34
31
|
],
|
|
35
|
-
"napi": {
|
|
36
|
-
"name": "rtc-node",
|
|
37
|
-
"triples": {
|
|
38
|
-
"defaults": false,
|
|
39
|
-
"additional": [
|
|
40
|
-
"aarch64-apple-darwin",
|
|
41
|
-
"x86_64-apple-darwin",
|
|
42
|
-
"aarch64-unknown-linux-gnu",
|
|
43
|
-
"x86_64-unknown-linux-gnu",
|
|
44
|
-
"x86_64-pc-windows-msvc"
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
32
|
"dependencies": {
|
|
49
|
-
"@bufbuild/protobuf": "^1.10.1",
|
|
50
33
|
"@datastructures-js/deque": "1.0.8",
|
|
51
34
|
"@livekit/mutex": "^1.0.0",
|
|
52
35
|
"@livekit/typed-emitter": "^3.0.0",
|
|
36
|
+
"@livekit/rtc-ffi-bindings": "0.12.52-patch.0",
|
|
53
37
|
"pino": "^9.0.0",
|
|
54
38
|
"pino-pretty": "^13.0.0"
|
|
55
39
|
},
|
|
56
40
|
"devDependencies": {
|
|
57
|
-
"@bufbuild/
|
|
58
|
-
"@napi-rs/cli": "^2.18.0",
|
|
41
|
+
"@bufbuild/protobuf": "^1.10.1",
|
|
59
42
|
"@types/node": "^22.13.10",
|
|
60
43
|
"vitest": "^3.0.0",
|
|
61
44
|
"prettier": "^3.0.3",
|
|
62
45
|
"tsup": "^8.3.5",
|
|
63
46
|
"typescript": "5.8.2",
|
|
64
|
-
"livekit-server-sdk": "2.15.
|
|
65
|
-
},
|
|
66
|
-
"optionalDependencies": {
|
|
67
|
-
"@livekit/rtc-node-darwin-x64": "0.13.24",
|
|
68
|
-
"@livekit/rtc-node-darwin-arm64": "0.13.24",
|
|
69
|
-
"@livekit/rtc-node-linux-x64-gnu": "0.13.24",
|
|
70
|
-
"@livekit/rtc-node-linux-arm64-gnu": "0.13.24",
|
|
71
|
-
"@livekit/rtc-node-win32-x64-msvc": "0.13.24"
|
|
47
|
+
"livekit-server-sdk": "2.15.1"
|
|
72
48
|
},
|
|
73
49
|
"engines": {
|
|
74
50
|
"node": ">= 18"
|
|
75
51
|
},
|
|
76
52
|
"scripts": {
|
|
77
53
|
"prebuild": "node -p \"'export const SDK_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
|
|
78
|
-
"build
|
|
79
|
-
"build": "pnpm build:ts && napi build --platform --release --dts native.d.ts --js native.cjs --pipe \"prettier -w\" src/napi",
|
|
80
|
-
"artifacts": "pnpm build:ts && napi artifacts",
|
|
81
|
-
"build:debug": "napi build --platform",
|
|
54
|
+
"build": "pnpm prebuild && tsup --onSuccess \"tsc --declaration --emitDeclarationOnly\"",
|
|
82
55
|
"lint": "eslint -f unix \"src/**/*.ts\" --ignore-pattern \"src/proto/*\"",
|
|
83
|
-
"test": "vitest run"
|
|
84
|
-
"test:e2e": "vitest run src/tests/e2e.test.ts",
|
|
85
|
-
"universal": "napi universal",
|
|
86
|
-
"version": "napi version"
|
|
56
|
+
"test": "vitest run src"
|
|
87
57
|
}
|
|
88
58
|
}
|
package/src/audio_filter.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { LoadAudioFilterPluginResponse } from '@livekit/rtc-ffi-bindings';
|
|
5
|
+
import { LoadAudioFilterPluginRequest } from '@livekit/rtc-ffi-bindings';
|
|
4
6
|
import { FfiClient } from './ffi_client.js';
|
|
5
|
-
import type { LoadAudioFilterPluginResponse } from './proto/audio_frame_pb.js';
|
|
6
|
-
import { LoadAudioFilterPluginRequest } from './proto/audio_frame_pb.js';
|
|
7
7
|
|
|
8
8
|
export class AudioFilter {
|
|
9
9
|
constructor(moduleId: string, path: string, dependencies: string[] = []) {
|
package/src/audio_frame.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { OwnedAudioFrameBuffer } from '@livekit/rtc-ffi-bindings';
|
|
5
|
+
import { AudioFrameBufferInfo } from '@livekit/rtc-ffi-bindings';
|
|
4
6
|
import { FfiClient, FfiHandle } from './ffi_client.js';
|
|
5
|
-
import type { OwnedAudioFrameBuffer } from './proto/audio_frame_pb.js';
|
|
6
|
-
import { AudioFrameBufferInfo } from './proto/audio_frame_pb.js';
|
|
7
7
|
|
|
8
8
|
export class AudioFrame {
|
|
9
9
|
data: Int16Array;
|
|
@@ -32,9 +32,14 @@ export class AudioFrame {
|
|
|
32
32
|
this._userdata = userdata;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
static create(
|
|
35
|
+
static create(
|
|
36
|
+
sampleRate: number,
|
|
37
|
+
channels: number,
|
|
38
|
+
samplesPerChannel: number,
|
|
39
|
+
userdata?: Record<string, unknown>,
|
|
40
|
+
): AudioFrame {
|
|
36
41
|
const data = new Int16Array(channels * samplesPerChannel);
|
|
37
|
-
return new AudioFrame(data, sampleRate, channels, samplesPerChannel);
|
|
42
|
+
return new AudioFrame(data, sampleRate, channels, samplesPerChannel, userdata);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/** @internal */
|
|
@@ -103,5 +108,12 @@ export const combineAudioFrames = (buffer: AudioFrame | AudioFrame[]): AudioFram
|
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
const data = new Int16Array(buffer.map((x) => [...x.data]).flat());
|
|
106
|
-
|
|
111
|
+
|
|
112
|
+
// Merge userdata from all frames
|
|
113
|
+
const mergedUserdata: Record<string, unknown> = {};
|
|
114
|
+
for (const frame of buffer) {
|
|
115
|
+
Object.assign(mergedUserdata, frame.userdata);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return new AudioFrame(data, sampleRate, channels, totalSamplesPerChannel, mergedUserdata);
|
|
107
119
|
};
|
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
|
|
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;
|