@livekit/rtc-node 0.13.23 → 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 +11 -7
- package/dist/audio_frame.cjs.map +1 -1
- package/dist/audio_frame.d.cts +3 -8
- package/dist/audio_frame.d.ts +3 -8
- package/dist/audio_frame.d.ts.map +1 -1
- package/dist/audio_frame.js +10 -6
- package/dist/audio_frame.js.map +1 -1
- package/dist/audio_mixer.cjs +11 -4
- 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.d.ts.map +1 -1
- package/dist/audio_mixer.js +11 -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 +17 -5
- 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 +16 -4
- 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/log.cjs +1 -10
- package/dist/log.cjs.map +1 -1
- package/dist/log.d.cts +4 -0
- package/dist/log.d.ts +4 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +1 -10
- package/dist/log.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 +48 -30
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.cts +11 -16
- package/dist/room.d.ts +11 -16
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +44 -26
- 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 +9 -35
- package/src/audio_filter.ts +2 -2
- package/src/audio_frame.ts +26 -7
- package/src/audio_mixer.ts +15 -4
- package/src/audio_resampler.ts +14 -4
- package/src/audio_source.ts +13 -5
- package/src/audio_stream.ts +21 -5
- 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/log.ts +14 -10
- package/src/participant.ts +17 -17
- package/src/room.ts +65 -38
- package/src/rpc.ts +1 -1
- package/src/tests/e2e.test.ts +517 -0
- 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/audio_resampler.cjs
CHANGED
|
@@ -22,15 +22,15 @@ __export(audio_resampler_exports, {
|
|
|
22
22
|
AudioResamplerQuality: () => AudioResamplerQuality
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(audio_resampler_exports);
|
|
25
|
+
var import_rtc_ffi_bindings = require("@livekit/rtc-ffi-bindings");
|
|
25
26
|
var import_audio_frame = require("./audio_frame.cjs");
|
|
26
27
|
var import_ffi_client = require("./ffi_client.cjs");
|
|
27
|
-
var import_audio_frame_pb = require("./proto/audio_frame_pb.cjs");
|
|
28
28
|
var AudioResamplerQuality = ((AudioResamplerQuality2) => {
|
|
29
|
-
AudioResamplerQuality2[AudioResamplerQuality2["QUICK"] =
|
|
30
|
-
AudioResamplerQuality2[AudioResamplerQuality2["LOW"] =
|
|
31
|
-
AudioResamplerQuality2[AudioResamplerQuality2["MEDIUM"] =
|
|
32
|
-
AudioResamplerQuality2[AudioResamplerQuality2["HIGH"] =
|
|
33
|
-
AudioResamplerQuality2[AudioResamplerQuality2["VERY_HIGH"] =
|
|
29
|
+
AudioResamplerQuality2[AudioResamplerQuality2["QUICK"] = import_rtc_ffi_bindings.SoxQualityRecipe.SOXR_QUALITY_QUICK] = "QUICK";
|
|
30
|
+
AudioResamplerQuality2[AudioResamplerQuality2["LOW"] = import_rtc_ffi_bindings.SoxQualityRecipe.SOXR_QUALITY_LOW] = "LOW";
|
|
31
|
+
AudioResamplerQuality2[AudioResamplerQuality2["MEDIUM"] = import_rtc_ffi_bindings.SoxQualityRecipe.SOXR_QUALITY_MEDIUM] = "MEDIUM";
|
|
32
|
+
AudioResamplerQuality2[AudioResamplerQuality2["HIGH"] = import_rtc_ffi_bindings.SoxQualityRecipe.SOXR_QUALITY_HIGH] = "HIGH";
|
|
33
|
+
AudioResamplerQuality2[AudioResamplerQuality2["VERY_HIGH"] = import_rtc_ffi_bindings.SoxQualityRecipe.SOXR_QUALITY_VERYHIGH] = "VERY_HIGH";
|
|
34
34
|
return AudioResamplerQuality2;
|
|
35
35
|
})(AudioResamplerQuality || {});
|
|
36
36
|
class AudioResampler {
|
|
@@ -51,13 +51,13 @@ class AudioResampler {
|
|
|
51
51
|
this.#inputRate = inputRate;
|
|
52
52
|
this.#outputRate = outputRate;
|
|
53
53
|
this.#channels = channels;
|
|
54
|
-
const req = new
|
|
54
|
+
const req = new import_rtc_ffi_bindings.NewSoxResamplerRequest({
|
|
55
55
|
inputRate,
|
|
56
56
|
outputRate,
|
|
57
57
|
numChannels: channels,
|
|
58
58
|
qualityRecipe: quality,
|
|
59
|
-
inputDataType:
|
|
60
|
-
outputDataType:
|
|
59
|
+
inputDataType: import_rtc_ffi_bindings.SoxResamplerDataType.SOXR_DATATYPE_INT16I,
|
|
60
|
+
outputDataType: import_rtc_ffi_bindings.SoxResamplerDataType.SOXR_DATATYPE_INT16I,
|
|
61
61
|
flags: 0
|
|
62
62
|
});
|
|
63
63
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
@@ -84,6 +84,13 @@ class AudioResampler {
|
|
|
84
84
|
get channels() {
|
|
85
85
|
return this.#channels;
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Releases the underlying native resampler handle. Must be called when
|
|
89
|
+
* the resampler is no longer needed to avoid leaking the FD.
|
|
90
|
+
*/
|
|
91
|
+
close() {
|
|
92
|
+
this.#ffiHandle.dispose();
|
|
93
|
+
}
|
|
87
94
|
/**
|
|
88
95
|
* Push audio data into the resampler and retrieve any available resampled data.
|
|
89
96
|
*
|
|
@@ -96,7 +103,7 @@ class AudioResampler {
|
|
|
96
103
|
* be empty if no output data is available yet.
|
|
97
104
|
*/
|
|
98
105
|
push(data) {
|
|
99
|
-
const req = new
|
|
106
|
+
const req = new import_rtc_ffi_bindings.PushSoxResamplerRequest({
|
|
100
107
|
resamplerHandle: this.#ffiHandle.handle,
|
|
101
108
|
dataPtr: data.protoInfo().dataPtr,
|
|
102
109
|
size: data.data.byteLength
|
|
@@ -119,7 +126,8 @@ class AudioResampler {
|
|
|
119
126
|
new Int16Array(outputData.buffer),
|
|
120
127
|
this.#outputRate,
|
|
121
128
|
this.#channels,
|
|
122
|
-
Math.trunc(outputData.length / this.#channels / 2)
|
|
129
|
+
Math.trunc(outputData.length / this.#channels / 2),
|
|
130
|
+
data.userdata
|
|
123
131
|
)
|
|
124
132
|
];
|
|
125
133
|
}
|
|
@@ -131,7 +139,7 @@ class AudioResampler {
|
|
|
131
139
|
* internal buffers are processed and all resampled data is output.
|
|
132
140
|
*/
|
|
133
141
|
flush() {
|
|
134
|
-
const req = new
|
|
142
|
+
const req = new import_rtc_ffi_bindings.FlushSoxResamplerRequest({
|
|
135
143
|
resamplerHandle: this.#ffiHandle.handle
|
|
136
144
|
});
|
|
137
145
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audio_resampler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport
|
|
1
|
+
{"version":3,"sources":["../src/audio_resampler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n FlushSoxResamplerResponse,\n NewSoxResamplerResponse,\n PushSoxResamplerResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport {\n FlushSoxResamplerRequest,\n NewSoxResamplerRequest,\n PushSoxResamplerRequest,\n SoxQualityRecipe,\n SoxResamplerDataType,\n} from '@livekit/rtc-ffi-bindings';\nimport { AudioFrame } from './audio_frame.js';\nimport { FfiClient, FfiHandle } from './ffi_client.js';\n\n/**\n * Resampler quality. Higher quality settings result in better audio quality but require more\n * processing power.\n */\nexport enum AudioResamplerQuality {\n QUICK = SoxQualityRecipe.SOXR_QUALITY_QUICK,\n LOW = SoxQualityRecipe.SOXR_QUALITY_LOW,\n MEDIUM = SoxQualityRecipe.SOXR_QUALITY_MEDIUM,\n HIGH = SoxQualityRecipe.SOXR_QUALITY_HIGH,\n VERY_HIGH = SoxQualityRecipe.SOXR_QUALITY_VERYHIGH,\n}\n\n/**\n * AudioResampler provides functionality to resample audio data from an input sample rate to\n * an output sample rate using the Sox resampling library. It supports multiple channels and\n * configurable resampling quality.\n */\nexport class AudioResampler {\n #inputRate: number;\n #outputRate: number;\n #channels: number;\n #ffiHandle: FfiHandle;\n\n /**\n * Initializes a new AudioResampler.\n *\n * @param inputRate - The sample rate of the input audio data (in Hz).\n * @param outputRate - The desired sample rate of the output audio data (in Hz).\n * @param channels - The number of audio channels (e.g., 1 for mono, 2 for stereo). Defaults to 1.\n * @param quality - The quality setting for the resampler. Defaults to\n * `AudioResamplerQuality.MEDIUM`.\n */\n constructor(\n inputRate: number,\n outputRate: number,\n channels = 1,\n quality = AudioResamplerQuality.MEDIUM,\n ) {\n this.#inputRate = inputRate;\n this.#outputRate = outputRate;\n this.#channels = channels;\n\n const req = new NewSoxResamplerRequest({\n inputRate,\n outputRate,\n numChannels: channels,\n qualityRecipe: quality as number as SoxQualityRecipe,\n inputDataType: SoxResamplerDataType.SOXR_DATATYPE_INT16I,\n outputDataType: SoxResamplerDataType.SOXR_DATATYPE_INT16I,\n flags: 0,\n });\n\n const res = FfiClient.instance.request<NewSoxResamplerResponse>({\n message: {\n case: 'newSoxResampler',\n value: req,\n },\n });\n\n switch (res.message.case) {\n case 'resampler':\n this.#ffiHandle = new FfiHandle(res.message.value.handle!.id!);\n break;\n case 'error':\n default:\n throw new Error(res.message.value);\n }\n }\n\n get inputRate(): number {\n return this.#inputRate;\n }\n\n get outputRate(): number {\n return this.#outputRate;\n }\n\n get channels(): number {\n return this.#channels;\n }\n\n /**\n * Releases the underlying native resampler handle. Must be called when\n * the resampler is no longer needed to avoid leaking the FD.\n */\n close() {\n this.#ffiHandle.dispose();\n }\n\n /**\n * Push audio data into the resampler and retrieve any available resampled data.\n *\n * This method accepts audio data, resamples it according to the configured input and output rates,\n * and returns any resampled data that is available after processing the input.\n *\n * @param data - The audio frame to resample\n *\n * @returns A list of {@link AudioFrame} objects containing the resampled audio data. The list may\n * be empty if no output data is available yet.\n */\n push(data: AudioFrame): AudioFrame[] {\n const req = new PushSoxResamplerRequest({\n resamplerHandle: this.#ffiHandle.handle,\n dataPtr: data.protoInfo().dataPtr,\n size: data.data.byteLength,\n });\n\n const res = FfiClient.instance.request<PushSoxResamplerResponse>({\n message: {\n case: 'pushSoxResampler',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(res.error);\n }\n\n if (!res.outputPtr) {\n return [];\n }\n\n const outputData = FfiClient.instance.copyBuffer(res.outputPtr, res.size!);\n\n return [\n new AudioFrame(\n new Int16Array(outputData.buffer),\n this.#outputRate,\n this.#channels,\n Math.trunc(outputData.length / this.#channels / 2),\n data.userdata,\n ),\n ];\n }\n\n /**\n * Flush any remaining audio data through the resampler and retrieve the resampled data.\n *\n * @remarks\n * This method should be called when no more input data will be provided to ensure that all\n * internal buffers are processed and all resampled data is output.\n */\n flush(): AudioFrame[] {\n const req = new FlushSoxResamplerRequest({\n resamplerHandle: this.#ffiHandle.handle,\n });\n\n const res = FfiClient.instance.request<FlushSoxResamplerResponse>({\n message: {\n case: 'flushSoxResampler',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(res.error);\n }\n\n if (!res.outputPtr) {\n return [];\n }\n\n const outputData = FfiClient.instance.copyBuffer(res.outputPtr, res.size!);\n return [\n new AudioFrame(\n new Int16Array(outputData.buffer),\n this.#outputRate,\n this.#channels,\n Math.trunc(outputData.length / this.#channels / 2),\n ),\n ];\n }\n}\n"],"mappings":"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","names":["AudioResamplerQuality"]}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { AudioFrame } from './audio_frame.cjs';
|
|
2
|
-
import '
|
|
3
|
-
import '@bufbuild/protobuf';
|
|
4
|
-
import './proto/track_pb.cjs';
|
|
5
|
-
import './proto/stats_pb.cjs';
|
|
6
|
-
import './proto/e2ee_pb.cjs';
|
|
7
|
-
import './proto/handle_pb.cjs';
|
|
2
|
+
import '@livekit/rtc-ffi-bindings';
|
|
8
3
|
|
|
9
4
|
/**
|
|
10
5
|
* Resampler quality. Higher quality settings result in better audio quality but require more
|
|
@@ -37,6 +32,11 @@ declare class AudioResampler {
|
|
|
37
32
|
get inputRate(): number;
|
|
38
33
|
get outputRate(): number;
|
|
39
34
|
get channels(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Releases the underlying native resampler handle. Must be called when
|
|
37
|
+
* the resampler is no longer needed to avoid leaking the FD.
|
|
38
|
+
*/
|
|
39
|
+
close(): void;
|
|
40
40
|
/**
|
|
41
41
|
* Push audio data into the resampler and retrieve any available resampled data.
|
|
42
42
|
*
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { AudioFrame } from './audio_frame.js';
|
|
2
|
-
import '
|
|
3
|
-
import '@bufbuild/protobuf';
|
|
4
|
-
import './proto/track_pb.js';
|
|
5
|
-
import './proto/stats_pb.js';
|
|
6
|
-
import './proto/e2ee_pb.js';
|
|
7
|
-
import './proto/handle_pb.js';
|
|
2
|
+
import '@livekit/rtc-ffi-bindings';
|
|
8
3
|
|
|
9
4
|
/**
|
|
10
5
|
* Resampler quality. Higher quality settings result in better audio quality but require more
|
|
@@ -37,6 +32,11 @@ declare class AudioResampler {
|
|
|
37
32
|
get inputRate(): number;
|
|
38
33
|
get outputRate(): number;
|
|
39
34
|
get channels(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Releases the underlying native resampler handle. Must be called when
|
|
37
|
+
* the resampler is no longer needed to avoid leaking the FD.
|
|
38
|
+
*/
|
|
39
|
+
close(): void;
|
|
40
40
|
/**
|
|
41
41
|
* Push audio data into the resampler and retrieve any available resampled data.
|
|
42
42
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_resampler.d.ts","sourceRoot":"","sources":["../src/audio_resampler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audio_resampler.d.ts","sourceRoot":"","sources":["../src/audio_resampler.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;GAGG;AACH,oBAAY,qBAAqB;IAC/B,KAAK,IAAsC;IAC3C,GAAG,IAAoC;IACvC,MAAM,IAAuC;IAC7C,IAAI,IAAqC;IACzC,SAAS,IAAyC;CACnD;AAED;;;;GAIG;AACH,qBAAa,cAAc;;IAMzB;;;;;;;;OAQG;gBAED,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,SAAI,EACZ,OAAO,wBAA+B;IAiCxC,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;OAGG;IACH,KAAK;IAIL;;;;;;;;;;OAUG;IACH,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE;IAmCpC;;;;;;OAMG;IACH,KAAK,IAAI,UAAU,EAAE;CA8BtB"}
|
package/dist/audio_resampler.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { AudioFrame } from "./audio_frame.js";
|
|
2
|
-
import { FfiClient, FfiHandle } from "./ffi_client.js";
|
|
3
1
|
import {
|
|
4
2
|
FlushSoxResamplerRequest,
|
|
5
3
|
NewSoxResamplerRequest,
|
|
6
4
|
PushSoxResamplerRequest,
|
|
7
5
|
SoxQualityRecipe,
|
|
8
6
|
SoxResamplerDataType
|
|
9
|
-
} from "
|
|
7
|
+
} from "@livekit/rtc-ffi-bindings";
|
|
8
|
+
import { AudioFrame } from "./audio_frame.js";
|
|
9
|
+
import { FfiClient, FfiHandle } from "./ffi_client.js";
|
|
10
10
|
var AudioResamplerQuality = ((AudioResamplerQuality2) => {
|
|
11
11
|
AudioResamplerQuality2[AudioResamplerQuality2["QUICK"] = SoxQualityRecipe.SOXR_QUALITY_QUICK] = "QUICK";
|
|
12
12
|
AudioResamplerQuality2[AudioResamplerQuality2["LOW"] = SoxQualityRecipe.SOXR_QUALITY_LOW] = "LOW";
|
|
@@ -66,6 +66,13 @@ class AudioResampler {
|
|
|
66
66
|
get channels() {
|
|
67
67
|
return this.#channels;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Releases the underlying native resampler handle. Must be called when
|
|
71
|
+
* the resampler is no longer needed to avoid leaking the FD.
|
|
72
|
+
*/
|
|
73
|
+
close() {
|
|
74
|
+
this.#ffiHandle.dispose();
|
|
75
|
+
}
|
|
69
76
|
/**
|
|
70
77
|
* Push audio data into the resampler and retrieve any available resampled data.
|
|
71
78
|
*
|
|
@@ -101,7 +108,8 @@ class AudioResampler {
|
|
|
101
108
|
new Int16Array(outputData.buffer),
|
|
102
109
|
this.#outputRate,
|
|
103
110
|
this.#channels,
|
|
104
|
-
Math.trunc(outputData.length / this.#channels / 2)
|
|
111
|
+
Math.trunc(outputData.length / this.#channels / 2),
|
|
112
|
+
data.userdata
|
|
105
113
|
)
|
|
106
114
|
];
|
|
107
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audio_resampler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport
|
|
1
|
+
{"version":3,"sources":["../src/audio_resampler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n FlushSoxResamplerResponse,\n NewSoxResamplerResponse,\n PushSoxResamplerResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport {\n FlushSoxResamplerRequest,\n NewSoxResamplerRequest,\n PushSoxResamplerRequest,\n SoxQualityRecipe,\n SoxResamplerDataType,\n} from '@livekit/rtc-ffi-bindings';\nimport { AudioFrame } from './audio_frame.js';\nimport { FfiClient, FfiHandle } from './ffi_client.js';\n\n/**\n * Resampler quality. Higher quality settings result in better audio quality but require more\n * processing power.\n */\nexport enum AudioResamplerQuality {\n QUICK = SoxQualityRecipe.SOXR_QUALITY_QUICK,\n LOW = SoxQualityRecipe.SOXR_QUALITY_LOW,\n MEDIUM = SoxQualityRecipe.SOXR_QUALITY_MEDIUM,\n HIGH = SoxQualityRecipe.SOXR_QUALITY_HIGH,\n VERY_HIGH = SoxQualityRecipe.SOXR_QUALITY_VERYHIGH,\n}\n\n/**\n * AudioResampler provides functionality to resample audio data from an input sample rate to\n * an output sample rate using the Sox resampling library. It supports multiple channels and\n * configurable resampling quality.\n */\nexport class AudioResampler {\n #inputRate: number;\n #outputRate: number;\n #channels: number;\n #ffiHandle: FfiHandle;\n\n /**\n * Initializes a new AudioResampler.\n *\n * @param inputRate - The sample rate of the input audio data (in Hz).\n * @param outputRate - The desired sample rate of the output audio data (in Hz).\n * @param channels - The number of audio channels (e.g., 1 for mono, 2 for stereo). Defaults to 1.\n * @param quality - The quality setting for the resampler. Defaults to\n * `AudioResamplerQuality.MEDIUM`.\n */\n constructor(\n inputRate: number,\n outputRate: number,\n channels = 1,\n quality = AudioResamplerQuality.MEDIUM,\n ) {\n this.#inputRate = inputRate;\n this.#outputRate = outputRate;\n this.#channels = channels;\n\n const req = new NewSoxResamplerRequest({\n inputRate,\n outputRate,\n numChannels: channels,\n qualityRecipe: quality as number as SoxQualityRecipe,\n inputDataType: SoxResamplerDataType.SOXR_DATATYPE_INT16I,\n outputDataType: SoxResamplerDataType.SOXR_DATATYPE_INT16I,\n flags: 0,\n });\n\n const res = FfiClient.instance.request<NewSoxResamplerResponse>({\n message: {\n case: 'newSoxResampler',\n value: req,\n },\n });\n\n switch (res.message.case) {\n case 'resampler':\n this.#ffiHandle = new FfiHandle(res.message.value.handle!.id!);\n break;\n case 'error':\n default:\n throw new Error(res.message.value);\n }\n }\n\n get inputRate(): number {\n return this.#inputRate;\n }\n\n get outputRate(): number {\n return this.#outputRate;\n }\n\n get channels(): number {\n return this.#channels;\n }\n\n /**\n * Releases the underlying native resampler handle. Must be called when\n * the resampler is no longer needed to avoid leaking the FD.\n */\n close() {\n this.#ffiHandle.dispose();\n }\n\n /**\n * Push audio data into the resampler and retrieve any available resampled data.\n *\n * This method accepts audio data, resamples it according to the configured input and output rates,\n * and returns any resampled data that is available after processing the input.\n *\n * @param data - The audio frame to resample\n *\n * @returns A list of {@link AudioFrame} objects containing the resampled audio data. The list may\n * be empty if no output data is available yet.\n */\n push(data: AudioFrame): AudioFrame[] {\n const req = new PushSoxResamplerRequest({\n resamplerHandle: this.#ffiHandle.handle,\n dataPtr: data.protoInfo().dataPtr,\n size: data.data.byteLength,\n });\n\n const res = FfiClient.instance.request<PushSoxResamplerResponse>({\n message: {\n case: 'pushSoxResampler',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(res.error);\n }\n\n if (!res.outputPtr) {\n return [];\n }\n\n const outputData = FfiClient.instance.copyBuffer(res.outputPtr, res.size!);\n\n return [\n new AudioFrame(\n new Int16Array(outputData.buffer),\n this.#outputRate,\n this.#channels,\n Math.trunc(outputData.length / this.#channels / 2),\n data.userdata,\n ),\n ];\n }\n\n /**\n * Flush any remaining audio data through the resampler and retrieve the resampled data.\n *\n * @remarks\n * This method should be called when no more input data will be provided to ensure that all\n * internal buffers are processed and all resampled data is output.\n */\n flush(): AudioFrame[] {\n const req = new FlushSoxResamplerRequest({\n resamplerHandle: this.#ffiHandle.handle,\n });\n\n const res = FfiClient.instance.request<FlushSoxResamplerResponse>({\n message: {\n case: 'flushSoxResampler',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(res.error);\n }\n\n if (!res.outputPtr) {\n return [];\n }\n\n const outputData = FfiClient.instance.copyBuffer(res.outputPtr, res.size!);\n return [\n new AudioFrame(\n new Int16Array(outputData.buffer),\n this.#outputRate,\n this.#channels,\n Math.trunc(outputData.length / this.#channels / 2),\n ),\n ];\n }\n}\n"],"mappings":"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","names":["AudioResamplerQuality"]}
|
package/dist/audio_source.cjs
CHANGED
|
@@ -21,9 +21,8 @@ __export(audio_source_exports, {
|
|
|
21
21
|
AudioSource: () => AudioSource
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(audio_source_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_native = require("./napi/native.cjs");
|
|
26
|
-
var import_audio_frame_pb = require("./proto/audio_frame_pb.cjs");
|
|
27
26
|
class AudioSource {
|
|
28
27
|
constructor(sampleRate, numChannels, queueSize = 1e3) {
|
|
29
28
|
/** @internal */
|
|
@@ -39,8 +38,8 @@ class AudioSource {
|
|
|
39
38
|
this.queueSize = queueSize;
|
|
40
39
|
this.lastCapture = 0;
|
|
41
40
|
this.currentQueueSize = 0;
|
|
42
|
-
const req = new
|
|
43
|
-
type:
|
|
41
|
+
const req = new import_rtc_ffi_bindings.NewAudioSourceRequest({
|
|
42
|
+
type: import_rtc_ffi_bindings.AudioSourceType.AUDIO_SOURCE_NATIVE,
|
|
44
43
|
sampleRate,
|
|
45
44
|
numChannels,
|
|
46
45
|
queueSizeMs: queueSize
|
|
@@ -52,7 +51,7 @@ class AudioSource {
|
|
|
52
51
|
}
|
|
53
52
|
});
|
|
54
53
|
this.info = res.source.info;
|
|
55
|
-
this.ffiHandle = new
|
|
54
|
+
this.ffiHandle = new import_rtc_ffi_bindings.FfiHandle(res.source.handle.id);
|
|
56
55
|
}
|
|
57
56
|
get queuedDuration() {
|
|
58
57
|
return Math.max(
|
|
@@ -61,7 +60,7 @@ class AudioSource {
|
|
|
61
60
|
);
|
|
62
61
|
}
|
|
63
62
|
clearQueue() {
|
|
64
|
-
const req = new
|
|
63
|
+
const req = new import_rtc_ffi_bindings.ClearAudioBufferRequest({
|
|
65
64
|
sourceHandle: this.ffiHandle.handle
|
|
66
65
|
});
|
|
67
66
|
import_ffi_client.FfiClient.instance.request({
|
|
@@ -103,7 +102,7 @@ class AudioSource {
|
|
|
103
102
|
clearTimeout(this.timeout);
|
|
104
103
|
}
|
|
105
104
|
this.timeout = setTimeout(this.release, this.currentQueueSize);
|
|
106
|
-
const req = new
|
|
105
|
+
const req = new import_rtc_ffi_bindings.CaptureAudioFrameRequest({
|
|
107
106
|
sourceHandle: this.ffiHandle.handle,
|
|
108
107
|
buffer: frame.protoInfo()
|
|
109
108
|
});
|
|
@@ -118,6 +117,11 @@ class AudioSource {
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
async close() {
|
|
120
|
+
if (this.timeout) {
|
|
121
|
+
clearTimeout(this.timeout);
|
|
122
|
+
this.timeout = void 0;
|
|
123
|
+
}
|
|
124
|
+
this.release();
|
|
121
125
|
this.ffiHandle.dispose();
|
|
122
126
|
this.closed = true;
|
|
123
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n FfiHandle,\n NewAudioSourceRequest,\n} from '@livekit/rtc-ffi-bindings';\nimport type { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\n\nexport class AudioSource {\n /** @internal */\n info: AudioSourceInfo;\n /** @internal */\n ffiHandle: FfiHandle;\n /** @internal */\n lastCapture: number;\n /** @internal */\n currentQueueSize: number;\n /** @internal */\n release = () => {};\n promise = this.newPromise();\n /** @internal */\n timeout?: ReturnType<typeof setTimeout> = undefined;\n /** @internal */\n closed = false;\n\n sampleRate: number;\n numChannels: number;\n queueSize: number;\n\n constructor(sampleRate: number, numChannels: number, queueSize = 1000) {\n this.sampleRate = sampleRate;\n this.numChannels = numChannels;\n this.queueSize = queueSize;\n\n this.lastCapture = 0;\n this.currentQueueSize = 0;\n\n const req = new NewAudioSourceRequest({\n type: AudioSourceType.AUDIO_SOURCE_NATIVE,\n sampleRate: sampleRate,\n numChannels: numChannels,\n queueSizeMs: queueSize,\n });\n\n const res = FfiClient.instance.request<NewAudioSourceResponse>({\n message: {\n case: 'newAudioSource',\n value: req,\n },\n });\n\n this.info = res.source!.info!;\n this.ffiHandle = new FfiHandle(res.source!.handle!.id!);\n }\n\n get queuedDuration(): number {\n return Math.max(\n this.currentQueueSize - Number(process.hrtime.bigint() / BigInt(1000000)) + this.lastCapture,\n 0,\n );\n }\n\n clearQueue() {\n const req = new ClearAudioBufferRequest({\n sourceHandle: this.ffiHandle.handle,\n });\n\n FfiClient.instance.request<ClearAudioBufferResponse>({\n message: {\n case: 'clearAudioBuffer',\n value: req,\n },\n });\n\n this.currentQueueSize = 0;\n this.release();\n }\n\n /** @internal */\n async newPromise() {\n return new Promise<void>((resolve) => {\n this.release = resolve;\n });\n }\n\n async waitForPlayout() {\n return this.promise.then(() => {\n this.lastCapture = 0;\n this.currentQueueSize = 0;\n this.promise = this.newPromise();\n this.timeout = undefined;\n });\n }\n\n async captureFrame(frame: AudioFrame) {\n if (this.closed) {\n throw new Error('AudioSource is closed');\n }\n\n if (frame.samplesPerChannel === 0) {\n return;\n }\n\n const now = Number(process.hrtime.bigint() / BigInt(1000000));\n const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;\n const frameDurationMs = (frame.samplesPerChannel / frame.sampleRate) * 1000;\n this.currentQueueSize = Math.max(this.currentQueueSize - elapsed, 0) + frameDurationMs;\n\n this.lastCapture = now;\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n this.timeout = setTimeout(this.release, this.currentQueueSize);\n\n const req = new CaptureAudioFrameRequest({\n sourceHandle: this.ffiHandle.handle,\n buffer: frame.protoInfo(),\n });\n\n const res = FfiClient.instance.request<CaptureAudioFrameResponse>({\n message: { case: 'captureAudioFrame', value: req },\n });\n\n const cb = await FfiClient.instance.waitFor<CaptureAudioFrameCallback>((ev) => {\n return ev.message.case == 'captureAudioFrame' && ev.message.value.asyncId == res.asyncId;\n });\n\n if (cb.error) {\n throw new Error(cb.error);\n }\n }\n\n async close() {\n // Clear any pending playout timeout so its callback doesn't fire after\n // the handle is disposed, which would reference freed native state.\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n // Resolve any pending waitForPlayout() promise so callers don't hang.\n this.release();\n this.ffiHandle.dispose();\n this.closed = true;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/audio_source.d.cts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
+
import { AudioSourceInfo, FfiHandle } from '@livekit/rtc-ffi-bindings';
|
|
1
2
|
import { AudioFrame } from './audio_frame.cjs';
|
|
2
|
-
import { FfiHandle } from './napi/native.d.cjs';
|
|
3
|
-
import { AudioSourceInfo } from './proto/audio_frame_pb.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
3
|
|
|
10
4
|
declare class AudioSource {
|
|
11
5
|
/** @internal */
|
package/dist/audio_source.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
+
import { AudioSourceInfo, FfiHandle } from '@livekit/rtc-ffi-bindings';
|
|
1
2
|
import { AudioFrame } from './audio_frame.js';
|
|
2
|
-
import { FfiHandle } from './napi/native.d.js';
|
|
3
|
-
import { AudioSourceInfo } from './proto/audio_frame_pb.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
3
|
|
|
10
4
|
declare class AudioSource {
|
|
11
5
|
/** @internal */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_source.d.ts","sourceRoot":"","sources":["../src/audio_source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"audio_source.d.ts","sourceRoot":"","sources":["../src/audio_source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIL,SAAS,EAEV,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,qBAAa,WAAW;IACtB,gBAAgB;IAChB,IAAI,EAAE,eAAe,CAAC;IACtB,gBAAgB;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB;IAChB,OAAO,aAAY;IACnB,OAAO,gBAAqB;IAC5B,gBAAgB;IAChB,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAa;IACpD,gBAAgB;IAChB,MAAM,UAAS;IAEf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;gBAEN,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,SAAO;IA0BrE,IAAI,cAAc,IAAI,MAAM,CAK3B;IAED,UAAU;IAgBV,gBAAgB;IACV,UAAU;IAMV,cAAc;IASd,YAAY,CAAC,KAAK,EAAE,UAAU;IAwC9B,KAAK;CAYZ"}
|
package/dist/audio_source.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { FfiClient } from "./ffi_client.js";
|
|
2
|
-
import { FfiHandle } from "./napi/native.js";
|
|
3
1
|
import {
|
|
4
2
|
AudioSourceType,
|
|
5
3
|
CaptureAudioFrameRequest,
|
|
6
4
|
ClearAudioBufferRequest,
|
|
5
|
+
FfiHandle,
|
|
7
6
|
NewAudioSourceRequest
|
|
8
|
-
} from "
|
|
7
|
+
} from "@livekit/rtc-ffi-bindings";
|
|
8
|
+
import { FfiClient } from "./ffi_client.js";
|
|
9
9
|
class AudioSource {
|
|
10
10
|
constructor(sampleRate, numChannels, queueSize = 1e3) {
|
|
11
11
|
/** @internal */
|
|
@@ -100,6 +100,11 @@ class AudioSource {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
async close() {
|
|
103
|
+
if (this.timeout) {
|
|
104
|
+
clearTimeout(this.timeout);
|
|
105
|
+
this.timeout = void 0;
|
|
106
|
+
}
|
|
107
|
+
this.release();
|
|
103
108
|
this.ffiHandle.dispose();
|
|
104
109
|
this.closed = true;
|
|
105
110
|
}
|
package/dist/audio_source.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n FfiHandle,\n NewAudioSourceRequest,\n} from '@livekit/rtc-ffi-bindings';\nimport type { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\n\nexport class AudioSource {\n /** @internal */\n info: AudioSourceInfo;\n /** @internal */\n ffiHandle: FfiHandle;\n /** @internal */\n lastCapture: number;\n /** @internal */\n currentQueueSize: number;\n /** @internal */\n release = () => {};\n promise = this.newPromise();\n /** @internal */\n timeout?: ReturnType<typeof setTimeout> = undefined;\n /** @internal */\n closed = false;\n\n sampleRate: number;\n numChannels: number;\n queueSize: number;\n\n constructor(sampleRate: number, numChannels: number, queueSize = 1000) {\n this.sampleRate = sampleRate;\n this.numChannels = numChannels;\n this.queueSize = queueSize;\n\n this.lastCapture = 0;\n this.currentQueueSize = 0;\n\n const req = new NewAudioSourceRequest({\n type: AudioSourceType.AUDIO_SOURCE_NATIVE,\n sampleRate: sampleRate,\n numChannels: numChannels,\n queueSizeMs: queueSize,\n });\n\n const res = FfiClient.instance.request<NewAudioSourceResponse>({\n message: {\n case: 'newAudioSource',\n value: req,\n },\n });\n\n this.info = res.source!.info!;\n this.ffiHandle = new FfiHandle(res.source!.handle!.id!);\n }\n\n get queuedDuration(): number {\n return Math.max(\n this.currentQueueSize - Number(process.hrtime.bigint() / BigInt(1000000)) + this.lastCapture,\n 0,\n );\n }\n\n clearQueue() {\n const req = new ClearAudioBufferRequest({\n sourceHandle: this.ffiHandle.handle,\n });\n\n FfiClient.instance.request<ClearAudioBufferResponse>({\n message: {\n case: 'clearAudioBuffer',\n value: req,\n },\n });\n\n this.currentQueueSize = 0;\n this.release();\n }\n\n /** @internal */\n async newPromise() {\n return new Promise<void>((resolve) => {\n this.release = resolve;\n });\n }\n\n async waitForPlayout() {\n return this.promise.then(() => {\n this.lastCapture = 0;\n this.currentQueueSize = 0;\n this.promise = this.newPromise();\n this.timeout = undefined;\n });\n }\n\n async captureFrame(frame: AudioFrame) {\n if (this.closed) {\n throw new Error('AudioSource is closed');\n }\n\n if (frame.samplesPerChannel === 0) {\n return;\n }\n\n const now = Number(process.hrtime.bigint() / BigInt(1000000));\n const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;\n const frameDurationMs = (frame.samplesPerChannel / frame.sampleRate) * 1000;\n this.currentQueueSize = Math.max(this.currentQueueSize - elapsed, 0) + frameDurationMs;\n\n this.lastCapture = now;\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n this.timeout = setTimeout(this.release, this.currentQueueSize);\n\n const req = new CaptureAudioFrameRequest({\n sourceHandle: this.ffiHandle.handle,\n buffer: frame.protoInfo(),\n });\n\n const res = FfiClient.instance.request<CaptureAudioFrameResponse>({\n message: { case: 'captureAudioFrame', value: req },\n });\n\n const cb = await FfiClient.instance.waitFor<CaptureAudioFrameCallback>((ev) => {\n return ev.message.case == 'captureAudioFrame' && ev.message.value.asyncId == res.asyncId;\n });\n\n if (cb.error) {\n throw new Error(cb.error);\n }\n }\n\n async close() {\n // Clear any pending playout timeout so its callback doesn't fire after\n // the handle is disposed, which would reference freed native state.\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n // Resolve any pending waitForPlayout() promise so callers don't hang.\n this.release();\n this.ffiHandle.dispose();\n this.closed = true;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/audio_stream.cjs
CHANGED
|
@@ -21,14 +21,16 @@ __export(audio_stream_exports, {
|
|
|
21
21
|
AudioStream: () => AudioStream
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(audio_stream_exports);
|
|
24
|
+
var import_rtc_ffi_bindings = require("@livekit/rtc-ffi-bindings");
|
|
24
25
|
var import_audio_frame = require("./audio_frame.cjs");
|
|
25
26
|
var import_ffi_client = require("./ffi_client.cjs");
|
|
26
27
|
var import_frame_processor = require("./frame_processor.cjs");
|
|
27
28
|
var import_log = require("./log.cjs");
|
|
28
|
-
var import_audio_frame_pb = require("./proto/audio_frame_pb.cjs");
|
|
29
29
|
class AudioStreamSource {
|
|
30
30
|
constructor(track, sampleRateOrOptions, numChannels) {
|
|
31
|
+
this.disposed = false;
|
|
31
32
|
this.onEvent = (ev) => {
|
|
33
|
+
var _a;
|
|
32
34
|
if (!this.controller) {
|
|
33
35
|
throw new Error("Stream controller not initialized");
|
|
34
36
|
}
|
|
@@ -51,13 +53,18 @@ class AudioStreamSource {
|
|
|
51
53
|
case "eos":
|
|
52
54
|
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
53
55
|
this.controller.close();
|
|
56
|
+
if (!this.disposed) {
|
|
57
|
+
this.disposed = true;
|
|
58
|
+
this.ffiHandle.dispose();
|
|
59
|
+
(_a = this.frameProcessor) == null ? void 0 : _a.close();
|
|
60
|
+
}
|
|
54
61
|
break;
|
|
55
62
|
}
|
|
56
63
|
};
|
|
57
64
|
if (sampleRateOrOptions !== void 0 && typeof sampleRateOrOptions !== "number") {
|
|
58
65
|
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48e3;
|
|
59
66
|
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
|
|
60
|
-
if (
|
|
67
|
+
if ((0, import_frame_processor.isFrameProcessor)(sampleRateOrOptions.noiseCancellation)) {
|
|
61
68
|
this.frameProcessor = sampleRateOrOptions.noiseCancellation;
|
|
62
69
|
} else {
|
|
63
70
|
this.legacyNcOptions = sampleRateOrOptions.noiseCancellation;
|
|
@@ -67,8 +74,8 @@ class AudioStreamSource {
|
|
|
67
74
|
this.sampleRate = sampleRateOrOptions ?? 48e3;
|
|
68
75
|
this.numChannels = numChannels ?? 1;
|
|
69
76
|
}
|
|
70
|
-
const req = new
|
|
71
|
-
type:
|
|
77
|
+
const req = new import_rtc_ffi_bindings.NewAudioStreamRequest({
|
|
78
|
+
type: import_rtc_ffi_bindings.AudioStreamType.AUDIO_STREAM_NATIVE,
|
|
72
79
|
trackHandle: track.ffi_handle.handle,
|
|
73
80
|
sampleRate: this.sampleRate,
|
|
74
81
|
numChannels: this.numChannels,
|
|
@@ -91,8 +98,13 @@ class AudioStreamSource {
|
|
|
91
98
|
this.controller = controller;
|
|
92
99
|
}
|
|
93
100
|
cancel() {
|
|
101
|
+
var _a;
|
|
94
102
|
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
95
|
-
this.
|
|
103
|
+
if (!this.disposed) {
|
|
104
|
+
this.disposed = true;
|
|
105
|
+
this.ffiHandle.dispose();
|
|
106
|
+
(_a = this.frameProcessor) == null ? void 0 : _a.close();
|
|
107
|
+
}
|
|
96
108
|
}
|
|
97
109
|
}
|
|
98
110
|
class AudioStream extends ReadableStream {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audio_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/audio_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { NewAudioStreamResponse } from '@livekit/rtc-ffi-bindings';\nimport { AudioStreamType, NewAudioStreamRequest } from '@livekit/rtc-ffi-bindings';\nimport type { UnderlyingSource } from 'node:stream/web';\nimport { AudioFrame } from './audio_frame.js';\nimport type { FfiEvent } from './ffi_client.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport { type FrameProcessor, isFrameProcessor } from './frame_processor.js';\nimport { log } from './log.js';\nimport type { Track } from './track.js';\n\nexport interface AudioStreamOptions {\n noiseCancellation?: NoiseCancellationOptions | FrameProcessor<AudioFrame>;\n sampleRate?: number;\n numChannels?: number;\n frameSizeMs?: number;\n}\n\nexport interface NoiseCancellationOptions {\n moduleId: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n options: Record<string, any>;\n}\n\nclass AudioStreamSource implements UnderlyingSource<AudioFrame> {\n private controller?: ReadableStreamDefaultController<AudioFrame>;\n private ffiHandle: FfiHandle;\n private disposed = false;\n private sampleRate: number;\n private numChannels: number;\n private legacyNcOptions?: NoiseCancellationOptions;\n private frameProcessor?: FrameProcessor<AudioFrame>;\n private frameSizeMs?: number;\n\n constructor(\n track: Track,\n sampleRateOrOptions?: number | AudioStreamOptions,\n numChannels?: number,\n ) {\n if (sampleRateOrOptions !== undefined && typeof sampleRateOrOptions !== 'number') {\n this.sampleRate = sampleRateOrOptions.sampleRate ?? 48000;\n this.numChannels = sampleRateOrOptions.numChannels ?? 1;\n if (isFrameProcessor(sampleRateOrOptions.noiseCancellation)) {\n this.frameProcessor = sampleRateOrOptions.noiseCancellation;\n } else {\n this.legacyNcOptions = sampleRateOrOptions.noiseCancellation;\n }\n this.frameSizeMs = sampleRateOrOptions.frameSizeMs;\n } else {\n this.sampleRate = (sampleRateOrOptions as number) ?? 48000;\n this.numChannels = numChannels ?? 1;\n }\n\n const req = new NewAudioStreamRequest({\n type: AudioStreamType.AUDIO_STREAM_NATIVE,\n trackHandle: track.ffi_handle.handle,\n sampleRate: this.sampleRate,\n numChannels: this.numChannels,\n frameSizeMs: this.frameSizeMs,\n ...(this.legacyNcOptions\n ? {\n audioFilterModuleId: this.legacyNcOptions.moduleId,\n audioFilterOptions: JSON.stringify(this.legacyNcOptions.options),\n }\n : {}),\n });\n\n const res = FfiClient.instance.request<NewAudioStreamResponse>({\n message: {\n case: 'newAudioStream',\n value: req,\n },\n });\n\n this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);\n\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 != 'audioStreamEvent' ||\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 let frame = AudioFrame.fromOwnedInfo(streamEvent.value.frame!);\n if (this.frameProcessor && this.frameProcessor.isEnabled()) {\n try {\n frame = this.frameProcessor.process(frame);\n } catch (err: unknown) {\n log.warn(`Frame processing failed, passing through original frame: ${err}`);\n }\n }\n this.controller.enqueue(frame);\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 this.frameProcessor?.close();\n }\n break;\n }\n };\n\n start(controller: ReadableStreamDefaultController<AudioFrame>) {\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 // Also close the frame processor on cancel for symmetry with the EOS path,\n // so resources are released regardless of how the stream ends.\n this.frameProcessor?.close();\n }\n }\n}\n\nexport class AudioStream extends ReadableStream<AudioFrame> {\n constructor(track: Track);\n constructor(track: Track, sampleRate: number);\n constructor(track: Track, sampleRate: number, numChannels: number);\n constructor(track: Track, options: AudioStreamOptions);\n constructor(\n track: Track,\n sampleRateOrOptions?: number | AudioStreamOptions,\n numChannels?: number,\n ) {\n super(new AudioStreamSource(track, sampleRateOrOptions, numChannels));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,8BAAuD;AAEvD,yBAA2B;AAE3B,wBAAqD;AACrD,6BAAsD;AACtD,iBAAoB;AAgBpB,MAAM,kBAA0D;AAAA,EAU9D,YACE,OACA,qBACA,aACA;AAXF,SAAQ,WAAW;AAoDnB,SAAQ,UAAU,CAAC,OAAiB;AAjFtC;AAkFI,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,cAAI,QAAQ,8BAAW,cAAc,YAAY,MAAM,KAAM;AAC7D,cAAI,KAAK,kBAAkB,KAAK,eAAe,UAAU,GAAG;AAC1D,gBAAI;AACF,sBAAQ,KAAK,eAAe,QAAQ,KAAK;AAAA,YAC3C,SAAS,KAAc;AACrB,6BAAI,KAAK,4DAA4D,GAAG,EAAE;AAAA,YAC5E;AAAA,UACF;AACA,eAAK,WAAW,QAAQ,KAAK;AAC7B;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;AACvB,uBAAK,mBAAL,mBAAqB;AAAA,UACvB;AACA;AAAA,MACJ;AAAA,IACF;AA/EE,QAAI,wBAAwB,UAAa,OAAO,wBAAwB,UAAU;AAChF,WAAK,aAAa,oBAAoB,cAAc;AACpD,WAAK,cAAc,oBAAoB,eAAe;AACtD,cAAI,yCAAiB,oBAAoB,iBAAiB,GAAG;AAC3D,aAAK,iBAAiB,oBAAoB;AAAA,MAC5C,OAAO;AACL,aAAK,kBAAkB,oBAAoB;AAAA,MAC7C;AACA,WAAK,cAAc,oBAAoB;AAAA,IACzC,OAAO;AACL,WAAK,aAAc,uBAAkC;AACrD,WAAK,cAAc,eAAe;AAAA,IACpC;AAEA,UAAM,MAAM,IAAI,8CAAsB;AAAA,MACpC,MAAM,wCAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,MAC9B,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK;AAAA,MAClB,GAAI,KAAK,kBACL;AAAA,QACE,qBAAqB,KAAK,gBAAgB;AAAA,QAC1C,oBAAoB,KAAK,UAAU,KAAK,gBAAgB,OAAO;AAAA,MACjE,IACA,CAAC;AAAA,IACP,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;AAEtD,gCAAU,SAAS,GAAG,iCAAe,UAAU,KAAK,OAAO;AAAA,EAC7D;AAAA,EA2CA,MAAM,YAAyD;AAC7D,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AA9HX;AA+HI,gCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,WAAW;AAChB,WAAK,UAAU,QAAQ;AAGvB,iBAAK,mBAAL,mBAAqB;AAAA,IACvB;AAAA,EACF;AACF;AAEO,MAAM,oBAAoB,eAA2B;AAAA,EAK1D,YACE,OACA,qBACA,aACA;AACA,UAAM,IAAI,kBAAkB,OAAO,qBAAqB,WAAW,CAAC;AAAA,EACtE;AACF;","names":[]}
|
package/dist/audio_stream.d.cts
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
import { AudioFrame } from './audio_frame.cjs';
|
|
2
2
|
import { FrameProcessor } from './frame_processor.cjs';
|
|
3
3
|
import { Track } from './track.cjs';
|
|
4
|
-
import '
|
|
5
|
-
import '@bufbuild/protobuf';
|
|
6
|
-
import './proto/track_pb.cjs';
|
|
7
|
-
import './proto/stats_pb.cjs';
|
|
8
|
-
import './proto/e2ee_pb.cjs';
|
|
9
|
-
import './proto/handle_pb.cjs';
|
|
4
|
+
import '@livekit/rtc-ffi-bindings';
|
|
10
5
|
import './video_frame.cjs';
|
|
11
|
-
import './proto/video_frame_pb.cjs';
|
|
12
6
|
import './audio_source.cjs';
|
|
13
|
-
import './napi/native.d.cjs';
|
|
14
7
|
import './ffi_client.cjs';
|
|
15
8
|
import '@livekit/typed-emitter';
|
|
16
|
-
import './proto/ffi_pb.cjs';
|
|
17
|
-
import './proto/room_pb.cjs';
|
|
18
|
-
import './proto/participant_pb.cjs';
|
|
19
|
-
import './proto/data_stream_pb.cjs';
|
|
20
|
-
import './proto/rpc_pb.cjs';
|
|
21
|
-
import './proto/track_publication_pb.cjs';
|
|
22
9
|
import './video_source.cjs';
|
|
23
10
|
|
|
24
11
|
interface AudioStreamOptions {
|
package/dist/audio_stream.d.ts
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
import { AudioFrame } from './audio_frame.js';
|
|
2
2
|
import { FrameProcessor } from './frame_processor.js';
|
|
3
3
|
import { Track } from './track.js';
|
|
4
|
-
import '
|
|
5
|
-
import '@bufbuild/protobuf';
|
|
6
|
-
import './proto/track_pb.js';
|
|
7
|
-
import './proto/stats_pb.js';
|
|
8
|
-
import './proto/e2ee_pb.js';
|
|
9
|
-
import './proto/handle_pb.js';
|
|
4
|
+
import '@livekit/rtc-ffi-bindings';
|
|
10
5
|
import './video_frame.js';
|
|
11
|
-
import './proto/video_frame_pb.js';
|
|
12
6
|
import './audio_source.js';
|
|
13
|
-
import './napi/native.d.js';
|
|
14
7
|
import './ffi_client.js';
|
|
15
8
|
import '@livekit/typed-emitter';
|
|
16
|
-
import './proto/ffi_pb.js';
|
|
17
|
-
import './proto/room_pb.js';
|
|
18
|
-
import './proto/participant_pb.js';
|
|
19
|
-
import './proto/data_stream_pb.js';
|
|
20
|
-
import './proto/rpc_pb.js';
|
|
21
|
-
import './proto/track_publication_pb.js';
|
|
22
9
|
import './video_source.js';
|
|
23
10
|
|
|
24
11
|
interface AudioStreamOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_stream.d.ts","sourceRoot":"","sources":["../src/audio_stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audio_stream.d.ts","sourceRoot":"","sources":["../src/audio_stream.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,KAAK,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAE7E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAkHD,qBAAa,WAAY,SAAQ,cAAc,CAAC,UAAU,CAAC;gBAC7C,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM;gBAChC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;gBACrD,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB;CAQtD"}
|