@livekit/rtc-node 0.13.10 → 0.13.12
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 +48 -0
- package/dist/audio_filter.cjs.map +1 -0
- package/dist/audio_filter.d.cts +5 -0
- package/dist/audio_filter.d.ts +5 -0
- package/dist/audio_filter.d.ts.map +1 -0
- package/dist/audio_filter.js +24 -0
- package/dist/audio_filter.js.map +1 -0
- package/dist/audio_source.cjs +5 -1
- package/dist/audio_source.cjs.map +1 -1
- package/dist/audio_source.d.ts.map +1 -1
- package/dist/audio_source.js +5 -1
- package/dist/audio_source.js.map +1 -1
- package/dist/audio_stream.cjs +30 -41
- package/dist/audio_stream.cjs.map +1 -1
- package/dist/audio_stream.d.cts +18 -23
- package/dist/audio_stream.d.ts +18 -23
- package/dist/audio_stream.d.ts.map +1 -1
- package/dist/audio_stream.js +30 -41
- package/dist/audio_stream.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/proto/audio_frame_pb.cjs +312 -2
- package/dist/proto/audio_frame_pb.cjs.map +1 -1
- package/dist/proto/audio_frame_pb.d.cts +278 -1
- package/dist/proto/audio_frame_pb.d.ts +278 -1
- package/dist/proto/audio_frame_pb.d.ts.map +1 -1
- package/dist/proto/audio_frame_pb.js +301 -2
- package/dist/proto/audio_frame_pb.js.map +1 -1
- package/dist/proto/ffi_pb.cjs +12 -2
- package/dist/proto/ffi_pb.cjs.map +1 -1
- package/dist/proto/ffi_pb.d.cts +65 -1
- package/dist/proto/ffi_pb.d.ts +65 -1
- package/dist/proto/ffi_pb.d.ts.map +1 -1
- package/dist/proto/ffi_pb.js +13 -3
- package/dist/proto/ffi_pb.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_stream.cjs +20 -48
- package/dist/video_stream.cjs.map +1 -1
- package/dist/video_stream.d.cts +4 -19
- package/dist/video_stream.d.ts +4 -19
- package/dist/video_stream.d.ts.map +1 -1
- package/dist/video_stream.js +20 -48
- package/dist/video_stream.js.map +1 -1
- package/package.json +6 -6
- package/src/audio_filter.ts +27 -0
- package/src/audio_source.ts +7 -3
- package/src/audio_stream.ts +61 -49
- package/src/index.ts +2 -0
- package/src/proto/audio_frame_pb.ts +533 -0
- package/src/proto/ffi_pb.ts +75 -1
- package/src/version.ts +1 -1
- package/src/video_stream.ts +24 -60
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var audio_filter_exports = {};
|
|
20
|
+
__export(audio_filter_exports, {
|
|
21
|
+
AudioFilter: () => AudioFilter
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(audio_filter_exports);
|
|
24
|
+
var import_ffi_client = require("./ffi_client.cjs");
|
|
25
|
+
var import_audio_frame_pb = require("./proto/audio_frame_pb.cjs");
|
|
26
|
+
class AudioFilter {
|
|
27
|
+
constructor(moduleId, path, dependencies = []) {
|
|
28
|
+
const req = new import_audio_frame_pb.LoadAudioFilterPluginRequest({
|
|
29
|
+
moduleId,
|
|
30
|
+
pluginPath: path,
|
|
31
|
+
dependencies
|
|
32
|
+
});
|
|
33
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
34
|
+
message: {
|
|
35
|
+
case: "loadAudioFilterPlugin",
|
|
36
|
+
value: req
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
if (res.error) {
|
|
40
|
+
throw new Error(`Failed to initialize audio filter: ${res.error}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
45
|
+
0 && (module.exports = {
|
|
46
|
+
AudioFilter
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=audio_filter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/audio_filter.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { FfiClient } from './ffi_client.js';\nimport type { LoadAudioFilterPluginResponse } from './proto/audio_frame_pb.js';\nimport { LoadAudioFilterPluginRequest } from './proto/audio_frame_pb.js';\n\nexport class AudioFilter {\n constructor(moduleId: string, path: string, dependencies: string[] = []) {\n const req = new LoadAudioFilterPluginRequest({\n moduleId,\n pluginPath: path,\n dependencies,\n });\n\n const res = FfiClient.instance.request<LoadAudioFilterPluginResponse>({\n message: {\n case: 'loadAudioFilterPlugin',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(`Failed to initialize audio filter: ${res.error}`);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,wBAA0B;AAE1B,4BAA6C;AAEtC,MAAM,YAAY;AAAA,EACvB,YAAY,UAAkB,MAAc,eAAyB,CAAC,GAAG;AACvE,UAAM,MAAM,IAAI,mDAA6B;AAAA,MAC3C;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AAED,UAAM,MAAM,4BAAU,SAAS,QAAuC;AAAA,MACpE,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,QAAI,IAAI,OAAO;AACb,YAAM,IAAI,MAAM,sCAAsC,IAAI,KAAK,EAAE;AAAA,IACnE;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_filter.d.ts","sourceRoot":"","sources":["../src/audio_filter.ts"],"names":[],"mappings":"AAOA,qBAAa,WAAW;gBACV,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO;CAkBxE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FfiClient } from "./ffi_client.js";
|
|
2
|
+
import { LoadAudioFilterPluginRequest } from "./proto/audio_frame_pb.js";
|
|
3
|
+
class AudioFilter {
|
|
4
|
+
constructor(moduleId, path, dependencies = []) {
|
|
5
|
+
const req = new LoadAudioFilterPluginRequest({
|
|
6
|
+
moduleId,
|
|
7
|
+
pluginPath: path,
|
|
8
|
+
dependencies
|
|
9
|
+
});
|
|
10
|
+
const res = FfiClient.instance.request({
|
|
11
|
+
message: {
|
|
12
|
+
case: "loadAudioFilterPlugin",
|
|
13
|
+
value: req
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
if (res.error) {
|
|
17
|
+
throw new Error(`Failed to initialize audio filter: ${res.error}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
AudioFilter
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=audio_filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/audio_filter.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { FfiClient } from './ffi_client.js';\nimport type { LoadAudioFilterPluginResponse } from './proto/audio_frame_pb.js';\nimport { LoadAudioFilterPluginRequest } from './proto/audio_frame_pb.js';\n\nexport class AudioFilter {\n constructor(moduleId: string, path: string, dependencies: string[] = []) {\n const req = new LoadAudioFilterPluginRequest({\n moduleId,\n pluginPath: path,\n dependencies,\n });\n\n const res = FfiClient.instance.request<LoadAudioFilterPluginResponse>({\n message: {\n case: 'loadAudioFilterPlugin',\n value: req,\n },\n });\n\n if (res.error) {\n throw new Error(`Failed to initialize audio filter: ${res.error}`);\n }\n }\n}\n"],"mappings":"AAGA,SAAS,iBAAiB;AAE1B,SAAS,oCAAoC;AAEtC,MAAM,YAAY;AAAA,EACvB,YAAY,UAAkB,MAAc,eAAyB,CAAC,GAAG;AACvE,UAAM,MAAM,IAAI,6BAA6B;AAAA,MAC3C;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AAED,UAAM,MAAM,UAAU,SAAS,QAAuC;AAAA,MACpE,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,QAAI,IAAI,OAAO;AACb,YAAM,IAAI,MAAM,sCAAsC,IAAI,KAAK,EAAE;AAAA,IACnE;AAAA,EACF;AACF;","names":[]}
|
package/dist/audio_source.cjs
CHANGED
|
@@ -83,12 +83,16 @@ class AudioSource {
|
|
|
83
83
|
this.lastCapture = 0;
|
|
84
84
|
this.currentQueueSize = 0;
|
|
85
85
|
this.promise = this.newPromise();
|
|
86
|
+
this.timeout = void 0;
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
async captureFrame(frame) {
|
|
89
90
|
if (this.closed) {
|
|
90
91
|
throw new Error("AudioSource is closed");
|
|
91
92
|
}
|
|
93
|
+
if (frame.samplesPerChannel === 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
92
96
|
const now = Number(process.hrtime.bigint() / BigInt(1e6));
|
|
93
97
|
const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;
|
|
94
98
|
const frameDurationMs = frame.samplesPerChannel / frame.sampleRate * 1e3;
|
|
@@ -97,7 +101,7 @@ class AudioSource {
|
|
|
97
101
|
if (this.timeout) {
|
|
98
102
|
clearTimeout(this.timeout);
|
|
99
103
|
}
|
|
100
|
-
this.timeout = setTimeout(this.release, this.currentQueueSize
|
|
104
|
+
this.timeout = setTimeout(this.release, this.currentQueueSize);
|
|
101
105
|
const req = new import_audio_frame_pb.CaptureAudioFrameRequest({
|
|
102
106
|
sourceHandle: this.ffiHandle.handle,
|
|
103
107
|
buffer: frame.protoInfo()
|
|
@@ -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 { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\nimport { FfiHandle } from './napi/native.js';\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from './proto/audio_frame_pb.js';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n NewAudioSourceRequest,\n} from './proto/audio_frame_pb.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.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 });\n }\n\n async captureFrame(frame: AudioFrame) {\n if (this.closed) {\n throw new Error('AudioSource is closed');\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 += frameDurationMs - elapsed;\n\n this.lastCapture = now;\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\nimport { FfiHandle } from './napi/native.js';\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from './proto/audio_frame_pb.js';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n NewAudioSourceRequest,\n} from './proto/audio_frame_pb.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.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 += frameDurationMs - elapsed;\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 this.ffiHandle.dispose();\n this.closed = true;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_source.d.ts","sourceRoot":"","sources":["../src/audio_source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,2BAA2B,CAAC;AAQnC,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;IAeV,gBAAgB;IACV,UAAU;IAMV,cAAc;
|
|
1
|
+
{"version":3,"file":"audio_source.d.ts","sourceRoot":"","sources":["../src/audio_source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,2BAA2B,CAAC;AAQnC,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;IAeV,gBAAgB;IACV,UAAU;IAMV,cAAc;IASd,YAAY,CAAC,KAAK,EAAE,UAAU;IAwC9B,KAAK;CAIZ"}
|
package/dist/audio_source.js
CHANGED
|
@@ -65,12 +65,16 @@ class AudioSource {
|
|
|
65
65
|
this.lastCapture = 0;
|
|
66
66
|
this.currentQueueSize = 0;
|
|
67
67
|
this.promise = this.newPromise();
|
|
68
|
+
this.timeout = void 0;
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
71
|
async captureFrame(frame) {
|
|
71
72
|
if (this.closed) {
|
|
72
73
|
throw new Error("AudioSource is closed");
|
|
73
74
|
}
|
|
75
|
+
if (frame.samplesPerChannel === 0) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
74
78
|
const now = Number(process.hrtime.bigint() / BigInt(1e6));
|
|
75
79
|
const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;
|
|
76
80
|
const frameDurationMs = frame.samplesPerChannel / frame.sampleRate * 1e3;
|
|
@@ -79,7 +83,7 @@ class AudioSource {
|
|
|
79
83
|
if (this.timeout) {
|
|
80
84
|
clearTimeout(this.timeout);
|
|
81
85
|
}
|
|
82
|
-
this.timeout = setTimeout(this.release, this.currentQueueSize
|
|
86
|
+
this.timeout = setTimeout(this.release, this.currentQueueSize);
|
|
83
87
|
const req = new CaptureAudioFrameRequest({
|
|
84
88
|
sourceHandle: this.ffiHandle.handle,
|
|
85
89
|
buffer: frame.protoInfo()
|
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 { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\nimport { FfiHandle } from './napi/native.js';\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from './proto/audio_frame_pb.js';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n NewAudioSourceRequest,\n} from './proto/audio_frame_pb.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.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 });\n }\n\n async captureFrame(frame: AudioFrame) {\n if (this.closed) {\n throw new Error('AudioSource is closed');\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 += frameDurationMs - elapsed;\n\n this.lastCapture = now;\n\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../src/audio_source.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from './audio_frame.js';\nimport { FfiClient } from './ffi_client.js';\nimport { FfiHandle } from './napi/native.js';\nimport type {\n AudioSourceInfo,\n CaptureAudioFrameCallback,\n CaptureAudioFrameResponse,\n ClearAudioBufferResponse,\n NewAudioSourceResponse,\n} from './proto/audio_frame_pb.js';\nimport {\n AudioSourceType,\n CaptureAudioFrameRequest,\n ClearAudioBufferRequest,\n NewAudioSourceRequest,\n} from './proto/audio_frame_pb.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.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 += frameDurationMs - elapsed;\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 this.ffiHandle.dispose();\n this.closed = true;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/audio_stream.cjs
CHANGED
|
@@ -21,19 +21,16 @@ __export(audio_stream_exports, {
|
|
|
21
21
|
AudioStream: () => AudioStream
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(audio_stream_exports);
|
|
24
|
-
var
|
|
24
|
+
var import_web = require("node:stream/web");
|
|
25
25
|
var import_audio_frame = require("./audio_frame.cjs");
|
|
26
26
|
var import_ffi_client = require("./ffi_client.cjs");
|
|
27
27
|
var import_audio_frame_pb = require("./proto/audio_frame_pb.cjs");
|
|
28
|
-
class
|
|
29
|
-
constructor(track,
|
|
30
|
-
/** @internal */
|
|
31
|
-
this.eventQueue = [];
|
|
32
|
-
/** @internal */
|
|
33
|
-
this.queueResolve = null;
|
|
34
|
-
/** @internal */
|
|
35
|
-
this.mutex = new import_mutex.Mutex();
|
|
28
|
+
class AudioStreamSource {
|
|
29
|
+
constructor(track, sampleRateOrOptions, numChannels) {
|
|
36
30
|
this.onEvent = (ev) => {
|
|
31
|
+
if (!this.controller) {
|
|
32
|
+
throw new Error("Stream controller not initialized");
|
|
33
|
+
}
|
|
37
34
|
if (ev.message.case != "audioStreamEvent" || ev.message.value.streamHandle != this.ffiHandle.handle) {
|
|
38
35
|
return;
|
|
39
36
|
}
|
|
@@ -41,26 +38,31 @@ class AudioStream {
|
|
|
41
38
|
switch (streamEvent.case) {
|
|
42
39
|
case "frameReceived":
|
|
43
40
|
const frame = import_audio_frame.AudioFrame.fromOwnedInfo(streamEvent.value.frame);
|
|
44
|
-
|
|
45
|
-
this.queueResolve({ done: false, value: frame });
|
|
46
|
-
this.queueResolve = null;
|
|
47
|
-
} else {
|
|
48
|
-
this.eventQueue.push(frame);
|
|
49
|
-
}
|
|
41
|
+
this.controller.enqueue(frame);
|
|
50
42
|
break;
|
|
51
43
|
case "eos":
|
|
52
44
|
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
45
|
+
this.controller.close();
|
|
53
46
|
break;
|
|
54
47
|
}
|
|
55
48
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
if (sampleRateOrOptions !== void 0 && typeof sampleRateOrOptions !== "number") {
|
|
50
|
+
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48e3;
|
|
51
|
+
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
|
|
52
|
+
this.ncOptions = sampleRateOrOptions.noiseCancellation;
|
|
53
|
+
} else {
|
|
54
|
+
this.sampleRate = sampleRateOrOptions ?? 48e3;
|
|
55
|
+
this.numChannels = numChannels ?? 1;
|
|
56
|
+
}
|
|
59
57
|
const req = new import_audio_frame_pb.NewAudioStreamRequest({
|
|
60
58
|
type: import_audio_frame_pb.AudioStreamType.AUDIO_STREAM_NATIVE,
|
|
61
59
|
trackHandle: track.ffi_handle.handle,
|
|
62
|
-
sampleRate,
|
|
63
|
-
numChannels
|
|
60
|
+
sampleRate: this.sampleRate,
|
|
61
|
+
numChannels: this.numChannels,
|
|
62
|
+
...this.ncOptions ? {
|
|
63
|
+
audioFilterModuleId: this.ncOptions.moduleId,
|
|
64
|
+
audioFilterOptions: JSON.stringify(this.ncOptions.options)
|
|
65
|
+
} : {}
|
|
64
66
|
});
|
|
65
67
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
66
68
|
message: {
|
|
@@ -68,33 +70,20 @@ class AudioStream {
|
|
|
68
70
|
value: req
|
|
69
71
|
}
|
|
70
72
|
});
|
|
71
|
-
this.info = res.stream.info;
|
|
72
73
|
this.ffiHandle = new import_ffi_client.FfiHandle(res.stream.handle.id);
|
|
73
74
|
import_ffi_client.FfiClient.instance.on(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (this.eventQueue.length > 0) {
|
|
78
|
-
unlock();
|
|
79
|
-
const value = this.eventQueue.shift();
|
|
80
|
-
if (value) {
|
|
81
|
-
return { done: false, value };
|
|
82
|
-
} else {
|
|
83
|
-
return { done: true, value: void 0 };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const promise = new Promise(
|
|
87
|
-
(resolve) => this.queueResolve = resolve
|
|
88
|
-
);
|
|
89
|
-
unlock();
|
|
90
|
-
return promise;
|
|
76
|
+
start(controller) {
|
|
77
|
+
this.controller = controller;
|
|
91
78
|
}
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
cancel() {
|
|
80
|
+
import_ffi_client.FfiClient.instance.off(import_ffi_client.FfiClientEvent.FfiEvent, this.onEvent);
|
|
94
81
|
this.ffiHandle.dispose();
|
|
95
82
|
}
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
}
|
|
84
|
+
class AudioStream extends import_web.ReadableStream {
|
|
85
|
+
constructor(track, sampleRateOrOptions, numChannels) {
|
|
86
|
+
super(new AudioStreamSource(track, sampleRateOrOptions, numChannels));
|
|
98
87
|
}
|
|
99
88
|
}
|
|
100
89
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -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 {
|
|
1
|
+
{"version":3,"sources":["../src/audio_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { ReadableStream, 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 { NewAudioStreamResponse } from './proto/audio_frame_pb.js';\nimport { AudioStreamType, NewAudioStreamRequest } from './proto/audio_frame_pb.js';\nimport type { Track } from './track.js';\n\nexport interface AudioStreamOptions {\n noiseCancellation?: NoiseCancellationOptions;\n sampleRate?: number;\n numChannels?: number;\n}\n\nexport interface NoiseCancellationOptions {\n moduleId: string;\n options: Record<string, any>;\n}\n\nclass AudioStreamSource implements UnderlyingSource<AudioFrame> {\n private controller?: ReadableStreamDefaultController<AudioFrame>;\n private ffiHandle: FfiHandle;\n private sampleRate: number;\n private numChannels: number;\n private ncOptions?: NoiseCancellationOptions;\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 this.ncOptions = sampleRateOrOptions.noiseCancellation;\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 ...(this.ncOptions\n ? {\n audioFilterModuleId: this.ncOptions.moduleId,\n audioFilterOptions: JSON.stringify(this.ncOptions.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 const frame = AudioFrame.fromOwnedInfo(streamEvent.value.frame!);\n this.controller.enqueue(frame);\n break;\n case 'eos':\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n this.controller.close();\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 this.ffiHandle.dispose();\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;AAGA,iBAAsD;AACtD,yBAA2B;AAE3B,wBAAqD;AAErD,4BAAuD;AAcvD,MAAM,kBAA0D;AAAA,EAO9D,YACE,OACA,qBACA,aACA;AAmCF,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,QAAQ,8BAAW,cAAc,YAAY,MAAM,KAAM;AAC/D,eAAK,WAAW,QAAQ,KAAK;AAC7B;AAAA,QACF,KAAK;AACH,sCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,eAAK,WAAW,MAAM;AACtB;AAAA,MACJ;AAAA,IACF;AAzDE,QAAI,wBAAwB,UAAa,OAAO,wBAAwB,UAAU;AAChF,WAAK,aAAa,oBAAoB,cAAc;AACpD,WAAK,cAAc,oBAAoB,eAAe;AACtD,WAAK,YAAY,oBAAoB;AAAA,IACvC,OAAO;AACL,WAAK,aAAc,uBAAkC;AACrD,WAAK,cAAc,eAAe;AAAA,IACpC;AAEA,UAAM,MAAM,IAAI,4CAAsB;AAAA,MACpC,MAAM,sCAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,MAC9B,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,GAAI,KAAK,YACL;AAAA,QACE,qBAAqB,KAAK,UAAU;AAAA,QACpC,oBAAoB,KAAK,UAAU,KAAK,UAAU,OAAO;AAAA,MAC3D,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,EA2BA,MAAM,YAAyD;AAC7D,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AACP,gCAAU,SAAS,IAAI,iCAAe,UAAU,KAAK,OAAO;AAC5D,SAAK,UAAU,QAAQ;AAAA,EACzB;AACF;AAEO,MAAM,oBAAoB,0BAA2B;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,37 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
2
|
import { AudioFrame } from './audio_frame.cjs';
|
|
3
|
-
import { FfiHandle } from './napi/native.d.cjs';
|
|
4
|
-
import { AudioStreamInfo } from './proto/audio_frame_pb.cjs';
|
|
5
3
|
import { Track } from './track.cjs';
|
|
4
|
+
import './proto/audio_frame_pb.cjs';
|
|
6
5
|
import '@bufbuild/protobuf';
|
|
7
6
|
import './proto/track_pb.cjs';
|
|
8
7
|
import './proto/stats_pb.cjs';
|
|
9
8
|
import './proto/e2ee_pb.cjs';
|
|
10
9
|
import './proto/handle_pb.cjs';
|
|
11
10
|
import './audio_source.cjs';
|
|
11
|
+
import './napi/native.d.cjs';
|
|
12
12
|
import './video_source.cjs';
|
|
13
13
|
import './proto/video_frame_pb.cjs';
|
|
14
14
|
import './video_frame.cjs';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
track: Track;
|
|
28
|
-
sampleRate: number;
|
|
29
|
-
|
|
30
|
-
constructor(track: Track, sampleRate?: number, numChannels?: number);
|
|
31
|
-
private onEvent;
|
|
32
|
-
next(): Promise<IteratorResult<AudioFrame>>;
|
|
33
|
-
close(): void;
|
|
34
|
-
[Symbol.asyncIterator](): AudioStream;
|
|
16
|
+
interface AudioStreamOptions {
|
|
17
|
+
noiseCancellation?: NoiseCancellationOptions;
|
|
18
|
+
sampleRate?: number;
|
|
19
|
+
numChannels?: number;
|
|
20
|
+
}
|
|
21
|
+
interface NoiseCancellationOptions {
|
|
22
|
+
moduleId: string;
|
|
23
|
+
options: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
declare class AudioStream extends ReadableStream<AudioFrame> {
|
|
26
|
+
constructor(track: Track);
|
|
27
|
+
constructor(track: Track, sampleRate: number);
|
|
28
|
+
constructor(track: Track, sampleRate: number, numChannels: number);
|
|
29
|
+
constructor(track: Track, options: AudioStreamOptions);
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
export { AudioStream };
|
|
32
|
+
export { AudioStream, type AudioStreamOptions, type NoiseCancellationOptions };
|
package/dist/audio_stream.d.ts
CHANGED
|
@@ -1,37 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
2
|
import { AudioFrame } from './audio_frame.js';
|
|
3
|
-
import { FfiHandle } from './napi/native.d.js';
|
|
4
|
-
import { AudioStreamInfo } from './proto/audio_frame_pb.js';
|
|
5
3
|
import { Track } from './track.js';
|
|
4
|
+
import './proto/audio_frame_pb.js';
|
|
6
5
|
import '@bufbuild/protobuf';
|
|
7
6
|
import './proto/track_pb.js';
|
|
8
7
|
import './proto/stats_pb.js';
|
|
9
8
|
import './proto/e2ee_pb.js';
|
|
10
9
|
import './proto/handle_pb.js';
|
|
11
10
|
import './audio_source.js';
|
|
11
|
+
import './napi/native.d.js';
|
|
12
12
|
import './video_source.js';
|
|
13
13
|
import './proto/video_frame_pb.js';
|
|
14
14
|
import './video_frame.js';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
track: Track;
|
|
28
|
-
sampleRate: number;
|
|
29
|
-
|
|
30
|
-
constructor(track: Track, sampleRate?: number, numChannels?: number);
|
|
31
|
-
private onEvent;
|
|
32
|
-
next(): Promise<IteratorResult<AudioFrame>>;
|
|
33
|
-
close(): void;
|
|
34
|
-
[Symbol.asyncIterator](): AudioStream;
|
|
16
|
+
interface AudioStreamOptions {
|
|
17
|
+
noiseCancellation?: NoiseCancellationOptions;
|
|
18
|
+
sampleRate?: number;
|
|
19
|
+
numChannels?: number;
|
|
20
|
+
}
|
|
21
|
+
interface NoiseCancellationOptions {
|
|
22
|
+
moduleId: string;
|
|
23
|
+
options: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
declare class AudioStream extends ReadableStream<AudioFrame> {
|
|
26
|
+
constructor(track: Track);
|
|
27
|
+
constructor(track: Track, sampleRate: number);
|
|
28
|
+
constructor(track: Track, sampleRate: number, numChannels: number);
|
|
29
|
+
constructor(track: Track, options: AudioStreamOptions);
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
export { AudioStream };
|
|
32
|
+
export { AudioStream, type AudioStreamOptions, type NoiseCancellationOptions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio_stream.d.ts","sourceRoot":"","sources":["../src/audio_stream.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"audio_stream.d.ts","sourceRoot":"","sources":["../src/audio_stream.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAyB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAK9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAmFD,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"}
|
package/dist/audio_stream.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReadableStream } from "node:stream/web";
|
|
2
2
|
import { AudioFrame } from "./audio_frame.js";
|
|
3
3
|
import { FfiClient, FfiClientEvent, FfiHandle } from "./ffi_client.js";
|
|
4
4
|
import { AudioStreamType, NewAudioStreamRequest } from "./proto/audio_frame_pb.js";
|
|
5
|
-
class
|
|
6
|
-
constructor(track,
|
|
7
|
-
/** @internal */
|
|
8
|
-
this.eventQueue = [];
|
|
9
|
-
/** @internal */
|
|
10
|
-
this.queueResolve = null;
|
|
11
|
-
/** @internal */
|
|
12
|
-
this.mutex = new Mutex();
|
|
5
|
+
class AudioStreamSource {
|
|
6
|
+
constructor(track, sampleRateOrOptions, numChannels) {
|
|
13
7
|
this.onEvent = (ev) => {
|
|
8
|
+
if (!this.controller) {
|
|
9
|
+
throw new Error("Stream controller not initialized");
|
|
10
|
+
}
|
|
14
11
|
if (ev.message.case != "audioStreamEvent" || ev.message.value.streamHandle != this.ffiHandle.handle) {
|
|
15
12
|
return;
|
|
16
13
|
}
|
|
@@ -18,26 +15,31 @@ class AudioStream {
|
|
|
18
15
|
switch (streamEvent.case) {
|
|
19
16
|
case "frameReceived":
|
|
20
17
|
const frame = AudioFrame.fromOwnedInfo(streamEvent.value.frame);
|
|
21
|
-
|
|
22
|
-
this.queueResolve({ done: false, value: frame });
|
|
23
|
-
this.queueResolve = null;
|
|
24
|
-
} else {
|
|
25
|
-
this.eventQueue.push(frame);
|
|
26
|
-
}
|
|
18
|
+
this.controller.enqueue(frame);
|
|
27
19
|
break;
|
|
28
20
|
case "eos":
|
|
29
21
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
22
|
+
this.controller.close();
|
|
30
23
|
break;
|
|
31
24
|
}
|
|
32
25
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
if (sampleRateOrOptions !== void 0 && typeof sampleRateOrOptions !== "number") {
|
|
27
|
+
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48e3;
|
|
28
|
+
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
|
|
29
|
+
this.ncOptions = sampleRateOrOptions.noiseCancellation;
|
|
30
|
+
} else {
|
|
31
|
+
this.sampleRate = sampleRateOrOptions ?? 48e3;
|
|
32
|
+
this.numChannels = numChannels ?? 1;
|
|
33
|
+
}
|
|
36
34
|
const req = new NewAudioStreamRequest({
|
|
37
35
|
type: AudioStreamType.AUDIO_STREAM_NATIVE,
|
|
38
36
|
trackHandle: track.ffi_handle.handle,
|
|
39
|
-
sampleRate,
|
|
40
|
-
numChannels
|
|
37
|
+
sampleRate: this.sampleRate,
|
|
38
|
+
numChannels: this.numChannels,
|
|
39
|
+
...this.ncOptions ? {
|
|
40
|
+
audioFilterModuleId: this.ncOptions.moduleId,
|
|
41
|
+
audioFilterOptions: JSON.stringify(this.ncOptions.options)
|
|
42
|
+
} : {}
|
|
41
43
|
});
|
|
42
44
|
const res = FfiClient.instance.request({
|
|
43
45
|
message: {
|
|
@@ -45,33 +47,20 @@ class AudioStream {
|
|
|
45
47
|
value: req
|
|
46
48
|
}
|
|
47
49
|
});
|
|
48
|
-
this.info = res.stream.info;
|
|
49
50
|
this.ffiHandle = new FfiHandle(res.stream.handle.id);
|
|
50
51
|
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (this.eventQueue.length > 0) {
|
|
55
|
-
unlock();
|
|
56
|
-
const value = this.eventQueue.shift();
|
|
57
|
-
if (value) {
|
|
58
|
-
return { done: false, value };
|
|
59
|
-
} else {
|
|
60
|
-
return { done: true, value: void 0 };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const promise = new Promise(
|
|
64
|
-
(resolve) => this.queueResolve = resolve
|
|
65
|
-
);
|
|
66
|
-
unlock();
|
|
67
|
-
return promise;
|
|
53
|
+
start(controller) {
|
|
54
|
+
this.controller = controller;
|
|
68
55
|
}
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
cancel() {
|
|
57
|
+
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
71
58
|
this.ffiHandle.dispose();
|
|
72
59
|
}
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
}
|
|
61
|
+
class AudioStream extends ReadableStream {
|
|
62
|
+
constructor(track, sampleRateOrOptions, numChannels) {
|
|
63
|
+
super(new AudioStreamSource(track, sampleRateOrOptions, numChannels));
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
66
|
export {
|