@camstack/addon-pipeline 0.1.20 → 0.2.1
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-analyzer/index.js +736 -719
- package/dist/audio-analyzer/index.mjs +726 -679
- package/dist/audio-codec-nodeav/index.js +304 -461
- package/dist/audio-codec-nodeav/index.mjs +300 -462
- package/dist/chunk-BdkLduGY.mjs +5 -0
- package/dist/chunk-D6vf50IK.js +28 -0
- package/dist/codec-runtime-BOk-13PN.js +202 -0
- package/dist/codec-runtime-BsqlEjPi.mjs +197 -0
- package/dist/constants-B_b0a-6h.mjs +3119 -0
- package/dist/{index-CMcx_k6Y.js → constants-D65v6yp6.js} +3107 -2935
- package/dist/decoder-nodeav/index.js +1374 -1444
- package/dist/decoder-nodeav/index.mjs +1369 -1425
- package/dist/detection-pipeline/index.js +6462 -5613
- package/dist/detection-pipeline/index.mjs +6451 -5574
- package/dist/dist-7ewQjTle.js +22454 -0
- package/dist/dist-C5jnNl0n.mjs +22089 -0
- package/dist/motion-wasm/index.js +469 -467
- package/dist/motion-wasm/index.mjs +464 -446
- package/dist/pipeline-runner/index.js +2029 -1827
- package/dist/pipeline-runner/index.mjs +2025 -1811
- package/dist/recorder/index.js +2045 -2157
- package/dist/recorder/index.mjs +2042 -2156
- package/dist/stream-broker/_stub.js +1806 -1352
- package/dist/stream-broker/_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D4-DHanK.mjs +156 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Tf-HACFd.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C9WX5HNw.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs +26 -0
- package/dist/stream-broker/dist-CYZr2fwk.mjs +2726 -0
- package/dist/stream-broker/hostInit-Di6vceAU.mjs +129 -0
- package/dist/stream-broker/index.js +17778 -15470
- package/dist/stream-broker/index.mjs +17769 -15465
- package/dist/stream-broker/remoteEntry.js +134 -2973
- package/dist/stream-broker/remoteEntry.ssr.js +33 -0
- package/dist/stream-broker/virtualExposes-dYNvIwoR.mjs +27 -0
- package/dist/stream-broker/virtual_mf-exposes-ssr___mfe_internal__addon_stream_broker_widgets__remoteEntry_js-Cmqfp4i_.mjs +10 -0
- package/embed-dist/assets/index-B8VlSD0-.js +150 -0
- package/embed-dist/assets/index-ZhDdp1Nd.css +2 -0
- package/embed-dist/index.html +13 -0
- package/package.json +25 -7
- package/wasm/assembly/index.ts +41 -16
- package/dist/audio-analyzer/index.js.map +0 -1
- package/dist/audio-analyzer/index.mjs.map +0 -1
- package/dist/audio-codec-nodeav/index.js.map +0 -1
- package/dist/audio-codec-nodeav/index.mjs.map +0 -1
- package/dist/decoder-nodeav/index.js.map +0 -1
- package/dist/decoder-nodeav/index.mjs.map +0 -1
- package/dist/detection-pipeline/index.js.map +0 -1
- package/dist/detection-pipeline/index.mjs.map +0 -1
- package/dist/index-5aYef068.mjs +0 -17514
- package/dist/index-5aYef068.mjs.map +0 -1
- package/dist/index-B36NMAdu.js +0 -17513
- package/dist/index-B36NMAdu.js.map +0 -1
- package/dist/index-CMcx_k6Y.js.map +0 -1
- package/dist/index-CYb7cFrv.mjs +0 -5790
- package/dist/index-CYb7cFrv.mjs.map +0 -1
- package/dist/motion-wasm/index.js.map +0 -1
- package/dist/motion-wasm/index.mjs.map +0 -1
- package/dist/pipeline-runner/index.js.map +0 -1
- package/dist/pipeline-runner/index.mjs.map +0 -1
- package/dist/recorder/index.js.map +0 -1
- package/dist/recorder/index.mjs.map +0 -1
- package/dist/stream-broker/@mf-types/compiled-types/stream-broker/widgets/FfmpegParamsField.d.ts +0 -41
- package/dist/stream-broker/@mf-types/compiled-types/stream-broker/widgets/GeometryBuilder.d.ts +0 -54
- package/dist/stream-broker/@mf-types/compiled-types/stream-broker/widgets/StreamBrokerPanel.d.ts +0 -21
- package/dist/stream-broker/@mf-types/compiled-types/stream-broker/widgets/format-ua.d.ts +0 -13
- package/dist/stream-broker/@mf-types/compiled-types/stream-broker/widgets/index.d.ts +0 -15
- package/dist/stream-broker/@mf-types/widgets.d.ts +0 -2
- package/dist/stream-broker/@mf-types.d.ts +0 -3
- package/dist/stream-broker/@mf-types.zip +0 -0
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.mjs-lantnv8e.mjs +0 -12
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-DJ3UNg7O.mjs +0 -30
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-CYXy_bhS.mjs +0 -21
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.mjs-U1EUeEPs.mjs +0 -104
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_trpc_mf_1_client__loadShare__.mjs-DeouEaSs.mjs +0 -85
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.mjs-DHUwjbb9.mjs +0 -62
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs-CaDEYBIU.mjs +0 -89
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-D6EROtlA.mjs +0 -29
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.mjs-x6pP3Ghk.mjs +0 -36
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs-DYEKzzY-.mjs +0 -45
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-CcnN6sbA.mjs +0 -6
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom_mf_1_client__loadShare__.mjs-DICOtMTl.mjs +0 -34
- package/dist/stream-broker/_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CL9DR49k.mjs +0 -156
- package/dist/stream-broker/client-BvTmMOQu.mjs +0 -9836
- package/dist/stream-broker/getErrorShape-BPSzUA7W-TlK8ipWe.mjs +0 -211
- package/dist/stream-broker/hostInit-ChmiMPS0.mjs +0 -168
- package/dist/stream-broker/index-BxsFuFmE.mjs +0 -2603
- package/dist/stream-broker/index-C-248uOU.mjs +0 -725
- package/dist/stream-broker/index-C05B6jqp.mjs +0 -185
- package/dist/stream-broker/index-CWkKuNLr.mjs +0 -232
- package/dist/stream-broker/index-DOJoSShD.mjs +0 -67784
- package/dist/stream-broker/index-DtOI1aTU.mjs +0 -18504
- package/dist/stream-broker/index-oMq6ilgR.mjs +0 -1641
- package/dist/stream-broker/index-vIWZQBIL.mjs +0 -435
- package/dist/stream-broker/index-xncRG7-x.mjs +0 -2713
- package/dist/stream-broker/index.js.map +0 -1
- package/dist/stream-broker/index.mjs.map +0 -1
- package/dist/stream-broker/jsx-runtime-BRT_HL0A.mjs +0 -55
- package/dist/stream-broker/schemas-B7L0qZtq.mjs +0 -3599
- package/dist/stream-broker/virtualExposes-pCd777Rp.mjs +0 -42
|
@@ -1,467 +1,305 @@
|
|
|
1
|
+
import { C as audioCodecCapability, i as BaseAddon, j as errMsg } from "../dist-C5jnNl0n.mjs";
|
|
2
|
+
import { t as DecodeRuntime } from "../codec-runtime-BsqlEjPi.mjs";
|
|
1
3
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
ctx;
|
|
41
|
-
swr;
|
|
42
|
-
packet;
|
|
43
|
-
inFrame;
|
|
44
|
-
outFrame;
|
|
45
|
-
cfg;
|
|
46
|
-
inLayout;
|
|
47
|
-
outLayout;
|
|
48
|
-
inSampleFmt;
|
|
49
|
-
outSampleFmt;
|
|
50
|
-
outBytesPerSample;
|
|
51
|
-
nextPts = 0;
|
|
52
|
-
closed = false;
|
|
53
|
-
static create(cfg) {
|
|
54
|
-
const codecId = resolveCodecId(cfg.codec);
|
|
55
|
-
if (codecId === null) {
|
|
56
|
-
throw new Error(`audio-codec: unknown codec '${cfg.codec}' for decode runtime`);
|
|
57
|
-
}
|
|
58
|
-
const codec = Codec.findDecoder(codecId);
|
|
59
|
-
if (!codec) {
|
|
60
|
-
throw new Error(`audio-codec: decoder not registered for codec '${cfg.codec}'`);
|
|
61
|
-
}
|
|
62
|
-
const ctx = new CodecContext();
|
|
63
|
-
ctx.allocContext3(codec);
|
|
64
|
-
ctx.sampleRate = cfg.sourceSampleRate;
|
|
65
|
-
ctx.channels = cfg.sourceChannels;
|
|
66
|
-
const inLayout = buildChannelLayout(cfg.sourceChannels);
|
|
67
|
-
ctx.channelLayout = inLayout;
|
|
68
|
-
if (cfg.extraData && cfg.extraData.byteLength > 0) {
|
|
69
|
-
ctx.extraData = Buffer.from(cfg.extraData);
|
|
70
|
-
}
|
|
71
|
-
const ret = ctx.open2Sync(codec, null);
|
|
72
|
-
if (ret < 0) {
|
|
73
|
-
ctx.freeContext();
|
|
74
|
-
throw new Error(`audio-codec: open2 failed for '${cfg.codec}' (ret=${ret})`);
|
|
75
|
-
}
|
|
76
|
-
const inSampleFmt = ctx.sampleFormat ?? AV_SAMPLE_FMT_FLT;
|
|
77
|
-
const outSampleFmt = pcmFormatToAv(cfg.targetFormat);
|
|
78
|
-
const outLayout = buildChannelLayout(cfg.targetChannels);
|
|
79
|
-
const swr = new SoftwareResampleContext();
|
|
80
|
-
const allocRet = swr.allocSetOpts2(
|
|
81
|
-
outLayout,
|
|
82
|
-
outSampleFmt,
|
|
83
|
-
cfg.targetSampleRate,
|
|
84
|
-
inLayout,
|
|
85
|
-
inSampleFmt,
|
|
86
|
-
cfg.sourceSampleRate
|
|
87
|
-
);
|
|
88
|
-
if (allocRet < 0) {
|
|
89
|
-
ctx.freeContext();
|
|
90
|
-
throw new Error(`audio-codec: swr allocSetOpts2 failed (ret=${allocRet})`);
|
|
91
|
-
}
|
|
92
|
-
const initRet = swr.init();
|
|
93
|
-
if (initRet < 0) {
|
|
94
|
-
ctx.freeContext();
|
|
95
|
-
throw new Error(`audio-codec: swr init failed (ret=${initRet})`);
|
|
96
|
-
}
|
|
97
|
-
const packet = new Packet();
|
|
98
|
-
packet.alloc();
|
|
99
|
-
const inFrame = new Frame();
|
|
100
|
-
inFrame.alloc();
|
|
101
|
-
const outFrame = new Frame();
|
|
102
|
-
outFrame.alloc();
|
|
103
|
-
return new DecodeRuntime(ctx, swr, packet, inFrame, outFrame, cfg, inLayout, outLayout, inSampleFmt, outSampleFmt);
|
|
104
|
-
}
|
|
105
|
-
constructor(ctx, swr, packet, inFrame, outFrame, cfg, inLayout, outLayout, inSampleFmt, outSampleFmt) {
|
|
106
|
-
this.ctx = ctx;
|
|
107
|
-
this.swr = swr;
|
|
108
|
-
this.packet = packet;
|
|
109
|
-
this.inFrame = inFrame;
|
|
110
|
-
this.outFrame = outFrame;
|
|
111
|
-
this.cfg = cfg;
|
|
112
|
-
this.inLayout = inLayout;
|
|
113
|
-
this.outLayout = outLayout;
|
|
114
|
-
this.inSampleFmt = inSampleFmt;
|
|
115
|
-
this.outSampleFmt = outSampleFmt;
|
|
116
|
-
this.outBytesPerSample = bytesPerSample(cfg.targetFormat);
|
|
117
|
-
}
|
|
118
|
-
decode(buf, pts) {
|
|
119
|
-
if (this.closed) return [];
|
|
120
|
-
this.packet.data = Buffer.from(buf);
|
|
121
|
-
if (pts !== void 0) {
|
|
122
|
-
this.packet.pts = BigInt(Math.round(pts));
|
|
123
|
-
this.packet.dts = BigInt(Math.round(pts));
|
|
124
|
-
}
|
|
125
|
-
const sendRet = this.ctx.sendPacketSync(this.packet);
|
|
126
|
-
if (sendRet < 0 && sendRet !== AVERROR_EAGAIN) {
|
|
127
|
-
throw new Error(`audio-codec: sendPacket failed (ret=${sendRet})`);
|
|
128
|
-
}
|
|
129
|
-
const out = [];
|
|
130
|
-
while (true) {
|
|
131
|
-
const recvRet = this.ctx.receiveFrameSync(this.inFrame);
|
|
132
|
-
if (recvRet === AVERROR_EAGAIN || recvRet === AVERROR_EOF) break;
|
|
133
|
-
if (recvRet < 0) {
|
|
134
|
-
throw new Error(`audio-codec: receiveFrame failed (ret=${recvRet})`);
|
|
135
|
-
}
|
|
136
|
-
const pcm = this.resampleFrame(this.inFrame);
|
|
137
|
-
if (pcm) out.push(pcm);
|
|
138
|
-
this.inFrame.unref();
|
|
139
|
-
}
|
|
140
|
-
return out;
|
|
141
|
-
}
|
|
142
|
-
resampleFrame(inFrame) {
|
|
143
|
-
const inSamples = inFrame.nbSamples;
|
|
144
|
-
const outSamples = Math.ceil(inSamples * this.cfg.targetSampleRate / this.cfg.sourceSampleRate) + 32;
|
|
145
|
-
this.outFrame.unref();
|
|
146
|
-
this.outFrame.format = this.outSampleFmt;
|
|
147
|
-
this.outFrame.sampleRate = this.cfg.targetSampleRate;
|
|
148
|
-
this.outFrame.channelLayout = this.outLayout;
|
|
149
|
-
this.outFrame.nbSamples = outSamples;
|
|
150
|
-
const allocRet = this.outFrame.getBuffer(0);
|
|
151
|
-
if (allocRet < 0) {
|
|
152
|
-
throw new Error(`audio-codec: outFrame.getBuffer failed (ret=${allocRet})`);
|
|
153
|
-
}
|
|
154
|
-
const convRet = this.swr.convertFrame(this.outFrame, inFrame);
|
|
155
|
-
if (convRet < 0) {
|
|
156
|
-
throw new Error(`audio-codec: swr.convertFrame failed (ret=${convRet})`);
|
|
157
|
-
}
|
|
158
|
-
const producedSamples = this.outFrame.nbSamples;
|
|
159
|
-
if (producedSamples <= 0) return null;
|
|
160
|
-
const planes = this.outFrame.extendedData;
|
|
161
|
-
if (!planes || planes.length === 0 || !planes[0]) return null;
|
|
162
|
-
const bytes = producedSamples * this.cfg.targetChannels * this.outBytesPerSample;
|
|
163
|
-
const src = planes[0];
|
|
164
|
-
const out = new Uint8Array(new ArrayBuffer(bytes));
|
|
165
|
-
out.set(src.subarray(0, bytes));
|
|
166
|
-
const ptsMs = this.nextPts;
|
|
167
|
-
this.nextPts = ptsMs + Math.round(producedSamples * 1e3 / this.cfg.targetSampleRate);
|
|
168
|
-
return {
|
|
169
|
-
data: out,
|
|
170
|
-
sampleRate: this.cfg.targetSampleRate,
|
|
171
|
-
channels: this.cfg.targetChannels,
|
|
172
|
-
format: this.cfg.targetFormat,
|
|
173
|
-
pts: ptsMs
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
close() {
|
|
177
|
-
if (this.closed) return;
|
|
178
|
-
this.closed = true;
|
|
179
|
-
try {
|
|
180
|
-
this.outFrame.free();
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
try {
|
|
184
|
-
this.inFrame.free();
|
|
185
|
-
} catch {
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
this.packet.free();
|
|
189
|
-
} catch {
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
this.swr.free();
|
|
193
|
-
} catch {
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
this.ctx.freeContext();
|
|
197
|
-
} catch {
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
/** Surfaced for assertion-style tests to inspect computed layouts. */
|
|
201
|
-
describe() {
|
|
202
|
-
return {
|
|
203
|
-
inLayoutMask: this.inLayout.mask,
|
|
204
|
-
outLayoutMask: this.outLayout.mask,
|
|
205
|
-
inSampleFormat: this.inSampleFmt,
|
|
206
|
-
outSampleFormat: this.outSampleFmt
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const CODEC_CATALOG = [
|
|
211
|
-
{ codec: "pcm_mulaw", canDecode: true, canEncode: true, label: "PCM µ-law (G.711)" },
|
|
212
|
-
{ codec: "pcm_alaw", canDecode: true, canEncode: true, label: "PCM A-law (G.711)" },
|
|
213
|
-
{ codec: "aac", canDecode: true, canEncode: true, label: "AAC" },
|
|
214
|
-
{ codec: "aac_latm", canDecode: true, canEncode: false, label: "AAC LATM" },
|
|
215
|
-
{ codec: "mpeg4-generic", canDecode: true, canEncode: false, label: "AAC (MPEG4-GENERIC)" },
|
|
216
|
-
{ codec: "opus", canDecode: true, canEncode: true, label: "Opus" }
|
|
4
|
+
//#region src/audio-codec-nodeav/addon/index.ts
|
|
5
|
+
var CODEC_CATALOG = [
|
|
6
|
+
{
|
|
7
|
+
codec: "pcm_mulaw",
|
|
8
|
+
canDecode: true,
|
|
9
|
+
canEncode: true,
|
|
10
|
+
label: "PCM µ-law (G.711)"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
codec: "pcm_alaw",
|
|
14
|
+
canDecode: true,
|
|
15
|
+
canEncode: true,
|
|
16
|
+
label: "PCM A-law (G.711)"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
codec: "aac",
|
|
20
|
+
canDecode: true,
|
|
21
|
+
canEncode: true,
|
|
22
|
+
label: "AAC"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
codec: "aac_latm",
|
|
26
|
+
canDecode: true,
|
|
27
|
+
canEncode: false,
|
|
28
|
+
label: "AAC LATM"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
codec: "mpeg4-generic",
|
|
32
|
+
canDecode: true,
|
|
33
|
+
canEncode: false,
|
|
34
|
+
label: "AAC (MPEG4-GENERIC)"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
codec: "opus",
|
|
38
|
+
canDecode: true,
|
|
39
|
+
canEncode: true,
|
|
40
|
+
label: "Opus"
|
|
41
|
+
}
|
|
217
42
|
];
|
|
218
43
|
function resolveCodecAlias(codec) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
const DEFAULT_IDLE_MS = 3e4;
|
|
225
|
-
const REAPER_INTERVAL_MS = 5e3;
|
|
226
|
-
const DEFAULT_GLOBAL_CONFIG = {
|
|
227
|
-
defaultIdleMs: DEFAULT_IDLE_MS
|
|
228
|
-
};
|
|
229
|
-
class AudioCodecNodeAvAddon extends BaseAddon {
|
|
230
|
-
sessions = /* @__PURE__ */ new Map();
|
|
231
|
-
reaperTimer = null;
|
|
232
|
-
logger = null;
|
|
233
|
-
constructor() {
|
|
234
|
-
super(DEFAULT_GLOBAL_CONFIG);
|
|
235
|
-
}
|
|
236
|
-
async onInitialize() {
|
|
237
|
-
this.logger = this.ctx.logger;
|
|
238
|
-
this.reaperTimer = setInterval(() => this.reapIdleSessions(), REAPER_INTERVAL_MS);
|
|
239
|
-
if (typeof this.reaperTimer.unref === "function") this.reaperTimer.unref();
|
|
240
|
-
return [{ capability: audioCodecCapability, provider: this }];
|
|
241
|
-
}
|
|
242
|
-
async onShutdown() {
|
|
243
|
-
if (this.reaperTimer) {
|
|
244
|
-
clearInterval(this.reaperTimer);
|
|
245
|
-
this.reaperTimer = null;
|
|
246
|
-
}
|
|
247
|
-
for (const s of [...this.sessions.values()]) {
|
|
248
|
-
this.disposeSession(s);
|
|
249
|
-
}
|
|
250
|
-
this.sessions.clear();
|
|
251
|
-
}
|
|
252
|
-
// ── Discovery ─────────────────────────────────────────────────────────────
|
|
253
|
-
async listSupportedCodecs() {
|
|
254
|
-
return CODEC_CATALOG.map((e) => ({
|
|
255
|
-
codec: e.codec,
|
|
256
|
-
canDecode: e.canDecode,
|
|
257
|
-
canEncode: e.canEncode,
|
|
258
|
-
...e.label ? { label: e.label } : {}
|
|
259
|
-
}));
|
|
260
|
-
}
|
|
261
|
-
async canHandle(input) {
|
|
262
|
-
const resolved = resolveCodecAlias(input.codec);
|
|
263
|
-
const entry = CODEC_CATALOG.find((e) => e.codec === resolved);
|
|
264
|
-
if (!entry) return false;
|
|
265
|
-
return input.kind === "decode" ? entry.canDecode : entry.canEncode;
|
|
266
|
-
}
|
|
267
|
-
// ── Session lifecycle ─────────────────────────────────────────────────────
|
|
268
|
-
async createDecodeSession(input) {
|
|
269
|
-
const codec = resolveCodecAlias(input.codec);
|
|
270
|
-
const entry = CODEC_CATALOG.find((e) => e.codec === codec);
|
|
271
|
-
if (!entry || !entry.canDecode) {
|
|
272
|
-
throw new Error(`audio-codec: decode unsupported for codec '${input.codec}'`);
|
|
273
|
-
}
|
|
274
|
-
const sessionId = `dec-${randomUUID()}`;
|
|
275
|
-
const state = {
|
|
276
|
-
sessionId,
|
|
277
|
-
kind: "decode",
|
|
278
|
-
config: { ...input, codec },
|
|
279
|
-
...input.tag ? { tag: input.tag } : {},
|
|
280
|
-
createdAtMs: Date.now(),
|
|
281
|
-
lastActivityMs: Date.now(),
|
|
282
|
-
framesIn: 0,
|
|
283
|
-
framesOut: 0,
|
|
284
|
-
pcmQueue: [],
|
|
285
|
-
runtime: null
|
|
286
|
-
};
|
|
287
|
-
this.sessions.set(sessionId, state);
|
|
288
|
-
this.logger?.info("audio-codec: decode session created", {
|
|
289
|
-
tags: { sessionId },
|
|
290
|
-
meta: { codec, target: `${input.targetSampleRate}Hz×${input.targetChannels}` }
|
|
291
|
-
});
|
|
292
|
-
return { sessionId, nodeId: this.ctx.kernel.localNodeId ?? "local" };
|
|
293
|
-
}
|
|
294
|
-
async createEncodeSession(input) {
|
|
295
|
-
const codec = resolveCodecAlias(input.codec);
|
|
296
|
-
const entry = CODEC_CATALOG.find((e) => e.codec === codec);
|
|
297
|
-
if (!entry || !entry.canEncode) {
|
|
298
|
-
throw new Error(`audio-codec: encode unsupported for codec '${input.codec}'`);
|
|
299
|
-
}
|
|
300
|
-
const sessionId = `enc-${randomUUID()}`;
|
|
301
|
-
const state = {
|
|
302
|
-
sessionId,
|
|
303
|
-
kind: "encode",
|
|
304
|
-
config: { ...input, codec },
|
|
305
|
-
...input.tag ? { tag: input.tag } : {},
|
|
306
|
-
createdAtMs: Date.now(),
|
|
307
|
-
lastActivityMs: Date.now(),
|
|
308
|
-
framesIn: 0,
|
|
309
|
-
framesOut: 0,
|
|
310
|
-
encodedQueue: []
|
|
311
|
-
};
|
|
312
|
-
this.sessions.set(sessionId, state);
|
|
313
|
-
this.logger?.info("audio-codec: encode session created", {
|
|
314
|
-
tags: { sessionId },
|
|
315
|
-
meta: { codec, target: `${input.targetSampleRate}Hz×${input.targetChannels}` }
|
|
316
|
-
});
|
|
317
|
-
return { sessionId, nodeId: this.ctx.kernel.localNodeId ?? "local" };
|
|
318
|
-
}
|
|
319
|
-
async closeSession(input) {
|
|
320
|
-
const s = this.sessions.get(input.sessionId);
|
|
321
|
-
if (!s) return;
|
|
322
|
-
this.disposeSession(s);
|
|
323
|
-
this.sessions.delete(input.sessionId);
|
|
324
|
-
}
|
|
325
|
-
// ── Decode data plane ─────────────────────────────────────────────────────
|
|
326
|
-
async pushEncodedFrame(input) {
|
|
327
|
-
const s = this.sessions.get(input.sessionId);
|
|
328
|
-
if (!s || s.kind !== "decode") {
|
|
329
|
-
throw new Error(`audio-codec: decode session '${input.sessionId}' not found`);
|
|
330
|
-
}
|
|
331
|
-
s.lastActivityMs = Date.now();
|
|
332
|
-
s.framesIn++;
|
|
333
|
-
if (!s.runtime) {
|
|
334
|
-
try {
|
|
335
|
-
s.runtime = DecodeRuntime.create({
|
|
336
|
-
codec: s.config.codec,
|
|
337
|
-
sourceSampleRate: s.config.sourceSampleRate,
|
|
338
|
-
sourceChannels: s.config.sourceChannels,
|
|
339
|
-
...s.config.extraData ? { extraData: s.config.extraData } : {},
|
|
340
|
-
targetSampleRate: s.config.targetSampleRate,
|
|
341
|
-
targetChannels: s.config.targetChannels,
|
|
342
|
-
targetFormat: s.config.targetFormat ?? "f32le"
|
|
343
|
-
});
|
|
344
|
-
} catch (err) {
|
|
345
|
-
this.logger?.error("audio-codec: decode runtime init failed", {
|
|
346
|
-
tags: { sessionId: input.sessionId },
|
|
347
|
-
meta: { codec: s.config.codec, error: errMsg(err) }
|
|
348
|
-
});
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
try {
|
|
353
|
-
const pcms = s.runtime.decode(input.data, input.pts);
|
|
354
|
-
for (const pcm of pcms) {
|
|
355
|
-
s.pcmQueue.push(pcm);
|
|
356
|
-
}
|
|
357
|
-
} catch (err) {
|
|
358
|
-
this.logger?.warn("audio-codec: decode frame failed", {
|
|
359
|
-
tags: { sessionId: input.sessionId },
|
|
360
|
-
meta: { codec: s.config.codec, error: errMsg(err) }
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
async pullPcm(input) {
|
|
365
|
-
const s = this.sessions.get(input.sessionId);
|
|
366
|
-
if (!s || s.kind !== "decode") {
|
|
367
|
-
throw new Error(`audio-codec: decode session '${input.sessionId}' not found`);
|
|
368
|
-
}
|
|
369
|
-
s.lastActivityMs = Date.now();
|
|
370
|
-
const out = s.pcmQueue.splice(0, input.maxCount);
|
|
371
|
-
s.framesOut += out.length;
|
|
372
|
-
return out;
|
|
373
|
-
}
|
|
374
|
-
// ── Encode data plane ─────────────────────────────────────────────────────
|
|
375
|
-
async pushPcm(input) {
|
|
376
|
-
const s = this.sessions.get(input.sessionId);
|
|
377
|
-
if (!s || s.kind !== "encode") {
|
|
378
|
-
throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
379
|
-
}
|
|
380
|
-
s.lastActivityMs = Date.now();
|
|
381
|
-
s.framesIn++;
|
|
382
|
-
void input.data;
|
|
383
|
-
void input.pts;
|
|
384
|
-
}
|
|
385
|
-
async pullEncoded(input) {
|
|
386
|
-
const s = this.sessions.get(input.sessionId);
|
|
387
|
-
if (!s || s.kind !== "encode") {
|
|
388
|
-
throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
389
|
-
}
|
|
390
|
-
s.lastActivityMs = Date.now();
|
|
391
|
-
const out = s.encodedQueue.splice(0, input.maxCount);
|
|
392
|
-
s.framesOut += out.length;
|
|
393
|
-
return out;
|
|
394
|
-
}
|
|
395
|
-
async flushEncode(input) {
|
|
396
|
-
const s = this.sessions.get(input.sessionId);
|
|
397
|
-
if (!s || s.kind !== "encode") {
|
|
398
|
-
throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
399
|
-
}
|
|
400
|
-
s.lastActivityMs = Date.now();
|
|
401
|
-
const out = s.encodedQueue.splice(0);
|
|
402
|
-
s.framesOut += out.length;
|
|
403
|
-
return out;
|
|
404
|
-
}
|
|
405
|
-
// ── Inventory ─────────────────────────────────────────────────────────────
|
|
406
|
-
async listActiveSessions() {
|
|
407
|
-
return [...this.sessions.values()].map((s) => ({
|
|
408
|
-
sessionId: s.sessionId,
|
|
409
|
-
kind: s.kind,
|
|
410
|
-
codec: s.config.codec,
|
|
411
|
-
sourceSampleRate: s.config.sourceSampleRate,
|
|
412
|
-
sourceChannels: s.config.sourceChannels,
|
|
413
|
-
targetSampleRate: s.config.targetSampleRate,
|
|
414
|
-
targetChannels: s.config.targetChannels,
|
|
415
|
-
format: this.resolveFormat(s),
|
|
416
|
-
...s.tag ? { tag: s.tag } : {},
|
|
417
|
-
createdAtMs: s.createdAtMs,
|
|
418
|
-
lastActivityMs: s.lastActivityMs,
|
|
419
|
-
framesIn: s.framesIn,
|
|
420
|
-
framesOut: s.framesOut
|
|
421
|
-
}));
|
|
422
|
-
}
|
|
423
|
-
// ── Internals ─────────────────────────────────────────────────────────────
|
|
424
|
-
resolveFormat(s) {
|
|
425
|
-
if (s.kind === "decode") return s.config.targetFormat ?? "f32le";
|
|
426
|
-
return s.config.sourceFormat ?? "f32le";
|
|
427
|
-
}
|
|
428
|
-
reapIdleSessions() {
|
|
429
|
-
const now = Date.now();
|
|
430
|
-
for (const [id, s] of this.sessions) {
|
|
431
|
-
const limit = s.config.idleMs ?? this.config.defaultIdleMs ?? DEFAULT_IDLE_MS;
|
|
432
|
-
if (now - s.lastActivityMs > limit) {
|
|
433
|
-
this.logger?.info("audio-codec: reaping idle session", {
|
|
434
|
-
tags: { sessionId: id },
|
|
435
|
-
meta: { kind: s.kind, idleMs: now - s.lastActivityMs, limit }
|
|
436
|
-
});
|
|
437
|
-
try {
|
|
438
|
-
this.disposeSession(s);
|
|
439
|
-
} catch (err) {
|
|
440
|
-
this.logger?.warn("audio-codec: dispose failed during reap", {
|
|
441
|
-
tags: { sessionId: id },
|
|
442
|
-
meta: { error: errMsg(err) }
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
this.sessions.delete(id);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
disposeSession(s) {
|
|
450
|
-
if (s.kind === "decode" && s.runtime) {
|
|
451
|
-
try {
|
|
452
|
-
s.runtime.close();
|
|
453
|
-
} catch (err) {
|
|
454
|
-
this.logger?.warn("audio-codec: decode runtime close failed", {
|
|
455
|
-
tags: { sessionId: s.sessionId },
|
|
456
|
-
meta: { error: errMsg(err) }
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
s.runtime = null;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
44
|
+
const c = codec.toLowerCase();
|
|
45
|
+
if (c === "mpeg4-generic") return "aac";
|
|
46
|
+
if (c === "l16") return "pcm_s16be";
|
|
47
|
+
return c;
|
|
462
48
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
49
|
+
var DEFAULT_IDLE_MS = 3e4;
|
|
50
|
+
var REAPER_INTERVAL_MS = 5e3;
|
|
51
|
+
var DEFAULT_GLOBAL_CONFIG = { defaultIdleMs: DEFAULT_IDLE_MS };
|
|
52
|
+
/**
|
|
53
|
+
* Audio codec I/O box backed by node-av (libavcodec + libswresample).
|
|
54
|
+
*
|
|
55
|
+
* Each `createDecodeSession` / `createEncodeSession` allocates an
|
|
56
|
+
* independent libav codec context + resampler so consumers don't share
|
|
57
|
+
* resamplers — a 16kHz mono ASA subscriber and a 48kHz stereo WebRTC
|
|
58
|
+
* subscriber on the same source codec each get their own session.
|
|
59
|
+
*
|
|
60
|
+
* Phase 1 scaffolds the cap surface + session bookkeeping. The actual
|
|
61
|
+
* libav decode/encode wiring is filled in by follow-up commits — the
|
|
62
|
+
* stub keeps the cap registered so wiring on the broker side can land
|
|
63
|
+
* before the audio backend matures.
|
|
64
|
+
*/
|
|
65
|
+
var AudioCodecNodeAvAddon = class extends BaseAddon {
|
|
66
|
+
sessions = /* @__PURE__ */ new Map();
|
|
67
|
+
reaperTimer = null;
|
|
68
|
+
logger = null;
|
|
69
|
+
constructor() {
|
|
70
|
+
super(DEFAULT_GLOBAL_CONFIG);
|
|
71
|
+
}
|
|
72
|
+
async onInitialize() {
|
|
73
|
+
this.logger = this.ctx.logger;
|
|
74
|
+
this.reaperTimer = setInterval(() => this.reapIdleSessions(), REAPER_INTERVAL_MS);
|
|
75
|
+
if (typeof this.reaperTimer.unref === "function") this.reaperTimer.unref();
|
|
76
|
+
return [{
|
|
77
|
+
capability: audioCodecCapability,
|
|
78
|
+
provider: this
|
|
79
|
+
}];
|
|
80
|
+
}
|
|
81
|
+
async onShutdown() {
|
|
82
|
+
if (this.reaperTimer) {
|
|
83
|
+
clearInterval(this.reaperTimer);
|
|
84
|
+
this.reaperTimer = null;
|
|
85
|
+
}
|
|
86
|
+
for (const s of [...this.sessions.values()]) this.disposeSession(s);
|
|
87
|
+
this.sessions.clear();
|
|
88
|
+
}
|
|
89
|
+
async listSupportedCodecs() {
|
|
90
|
+
return CODEC_CATALOG.map((e) => ({
|
|
91
|
+
codec: e.codec,
|
|
92
|
+
canDecode: e.canDecode,
|
|
93
|
+
canEncode: e.canEncode,
|
|
94
|
+
...e.label ? { label: e.label } : {}
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
async canHandle(input) {
|
|
98
|
+
const resolved = resolveCodecAlias(input.codec);
|
|
99
|
+
const entry = CODEC_CATALOG.find((e) => e.codec === resolved);
|
|
100
|
+
if (!entry) return false;
|
|
101
|
+
return input.kind === "decode" ? entry.canDecode : entry.canEncode;
|
|
102
|
+
}
|
|
103
|
+
async createDecodeSession(input) {
|
|
104
|
+
const codec = resolveCodecAlias(input.codec);
|
|
105
|
+
const entry = CODEC_CATALOG.find((e) => e.codec === codec);
|
|
106
|
+
if (!entry || !entry.canDecode) throw new Error(`audio-codec: decode unsupported for codec '${input.codec}'`);
|
|
107
|
+
const sessionId = `dec-${randomUUID()}`;
|
|
108
|
+
const state = {
|
|
109
|
+
sessionId,
|
|
110
|
+
kind: "decode",
|
|
111
|
+
config: {
|
|
112
|
+
...input,
|
|
113
|
+
codec
|
|
114
|
+
},
|
|
115
|
+
...input.tag ? { tag: input.tag } : {},
|
|
116
|
+
createdAtMs: Date.now(),
|
|
117
|
+
lastActivityMs: Date.now(),
|
|
118
|
+
framesIn: 0,
|
|
119
|
+
framesOut: 0,
|
|
120
|
+
pcmQueue: [],
|
|
121
|
+
runtime: null
|
|
122
|
+
};
|
|
123
|
+
this.sessions.set(sessionId, state);
|
|
124
|
+
this.logger?.info("audio-codec: decode session created", {
|
|
125
|
+
tags: { sessionId },
|
|
126
|
+
meta: {
|
|
127
|
+
codec,
|
|
128
|
+
target: `${input.targetSampleRate}Hz×${input.targetChannels}`
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
sessionId,
|
|
133
|
+
nodeId: this.ctx.kernel.localNodeId ?? "local"
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async createEncodeSession(input) {
|
|
137
|
+
const codec = resolveCodecAlias(input.codec);
|
|
138
|
+
const entry = CODEC_CATALOG.find((e) => e.codec === codec);
|
|
139
|
+
if (!entry || !entry.canEncode) throw new Error(`audio-codec: encode unsupported for codec '${input.codec}'`);
|
|
140
|
+
const sessionId = `enc-${randomUUID()}`;
|
|
141
|
+
const state = {
|
|
142
|
+
sessionId,
|
|
143
|
+
kind: "encode",
|
|
144
|
+
config: {
|
|
145
|
+
...input,
|
|
146
|
+
codec
|
|
147
|
+
},
|
|
148
|
+
...input.tag ? { tag: input.tag } : {},
|
|
149
|
+
createdAtMs: Date.now(),
|
|
150
|
+
lastActivityMs: Date.now(),
|
|
151
|
+
framesIn: 0,
|
|
152
|
+
framesOut: 0,
|
|
153
|
+
encodedQueue: []
|
|
154
|
+
};
|
|
155
|
+
this.sessions.set(sessionId, state);
|
|
156
|
+
this.logger?.info("audio-codec: encode session created", {
|
|
157
|
+
tags: { sessionId },
|
|
158
|
+
meta: {
|
|
159
|
+
codec,
|
|
160
|
+
target: `${input.targetSampleRate}Hz×${input.targetChannels}`
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
sessionId,
|
|
165
|
+
nodeId: this.ctx.kernel.localNodeId ?? "local"
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async closeSession(input) {
|
|
169
|
+
const s = this.sessions.get(input.sessionId);
|
|
170
|
+
if (!s) return;
|
|
171
|
+
this.disposeSession(s);
|
|
172
|
+
this.sessions.delete(input.sessionId);
|
|
173
|
+
}
|
|
174
|
+
async pushEncodedFrame(input) {
|
|
175
|
+
const s = this.sessions.get(input.sessionId);
|
|
176
|
+
if (!s || s.kind !== "decode") throw new Error(`audio-codec: decode session '${input.sessionId}' not found`);
|
|
177
|
+
s.lastActivityMs = Date.now();
|
|
178
|
+
s.framesIn++;
|
|
179
|
+
if (!s.runtime) try {
|
|
180
|
+
s.runtime = DecodeRuntime.create({
|
|
181
|
+
codec: s.config.codec,
|
|
182
|
+
sourceSampleRate: s.config.sourceSampleRate,
|
|
183
|
+
sourceChannels: s.config.sourceChannels,
|
|
184
|
+
...s.config.extraData ? { extraData: s.config.extraData } : {},
|
|
185
|
+
targetSampleRate: s.config.targetSampleRate,
|
|
186
|
+
targetChannels: s.config.targetChannels,
|
|
187
|
+
targetFormat: s.config.targetFormat ?? "f32le"
|
|
188
|
+
});
|
|
189
|
+
} catch (err) {
|
|
190
|
+
this.logger?.error("audio-codec: decode runtime init failed", {
|
|
191
|
+
tags: { sessionId: input.sessionId },
|
|
192
|
+
meta: {
|
|
193
|
+
codec: s.config.codec,
|
|
194
|
+
error: errMsg(err)
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const pcms = s.runtime.decode(input.data, input.pts);
|
|
201
|
+
for (const pcm of pcms) s.pcmQueue.push(pcm);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
this.logger?.warn("audio-codec: decode frame failed", {
|
|
204
|
+
tags: { sessionId: input.sessionId },
|
|
205
|
+
meta: {
|
|
206
|
+
codec: s.config.codec,
|
|
207
|
+
error: errMsg(err)
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async pullPcm(input) {
|
|
213
|
+
const s = this.sessions.get(input.sessionId);
|
|
214
|
+
if (!s || s.kind !== "decode") throw new Error(`audio-codec: decode session '${input.sessionId}' not found`);
|
|
215
|
+
s.lastActivityMs = Date.now();
|
|
216
|
+
const out = s.pcmQueue.splice(0, input.maxCount);
|
|
217
|
+
s.framesOut += out.length;
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
async pushPcm(input) {
|
|
221
|
+
const s = this.sessions.get(input.sessionId);
|
|
222
|
+
if (!s || s.kind !== "encode") throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
223
|
+
s.lastActivityMs = Date.now();
|
|
224
|
+
s.framesIn++;
|
|
225
|
+
input.data;
|
|
226
|
+
input.pts;
|
|
227
|
+
}
|
|
228
|
+
async pullEncoded(input) {
|
|
229
|
+
const s = this.sessions.get(input.sessionId);
|
|
230
|
+
if (!s || s.kind !== "encode") throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
231
|
+
s.lastActivityMs = Date.now();
|
|
232
|
+
const out = s.encodedQueue.splice(0, input.maxCount);
|
|
233
|
+
s.framesOut += out.length;
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
async flushEncode(input) {
|
|
237
|
+
const s = this.sessions.get(input.sessionId);
|
|
238
|
+
if (!s || s.kind !== "encode") throw new Error(`audio-codec: encode session '${input.sessionId}' not found`);
|
|
239
|
+
s.lastActivityMs = Date.now();
|
|
240
|
+
const out = s.encodedQueue.splice(0);
|
|
241
|
+
s.framesOut += out.length;
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
async listActiveSessions() {
|
|
245
|
+
return [...this.sessions.values()].map((s) => ({
|
|
246
|
+
sessionId: s.sessionId,
|
|
247
|
+
kind: s.kind,
|
|
248
|
+
codec: s.config.codec,
|
|
249
|
+
sourceSampleRate: s.config.sourceSampleRate,
|
|
250
|
+
sourceChannels: s.config.sourceChannels,
|
|
251
|
+
targetSampleRate: s.config.targetSampleRate,
|
|
252
|
+
targetChannels: s.config.targetChannels,
|
|
253
|
+
format: this.resolveFormat(s),
|
|
254
|
+
...s.tag ? { tag: s.tag } : {},
|
|
255
|
+
createdAtMs: s.createdAtMs,
|
|
256
|
+
lastActivityMs: s.lastActivityMs,
|
|
257
|
+
framesIn: s.framesIn,
|
|
258
|
+
framesOut: s.framesOut
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
resolveFormat(s) {
|
|
262
|
+
if (s.kind === "decode") return s.config.targetFormat ?? "f32le";
|
|
263
|
+
return s.config.sourceFormat ?? "f32le";
|
|
264
|
+
}
|
|
265
|
+
reapIdleSessions() {
|
|
266
|
+
const now = Date.now();
|
|
267
|
+
for (const [id, s] of this.sessions) {
|
|
268
|
+
const limit = s.config.idleMs ?? this.config.defaultIdleMs ?? DEFAULT_IDLE_MS;
|
|
269
|
+
if (now - s.lastActivityMs > limit) {
|
|
270
|
+
this.logger?.info("audio-codec: reaping idle session", {
|
|
271
|
+
tags: { sessionId: id },
|
|
272
|
+
meta: {
|
|
273
|
+
kind: s.kind,
|
|
274
|
+
idleMs: now - s.lastActivityMs,
|
|
275
|
+
limit
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
try {
|
|
279
|
+
this.disposeSession(s);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
this.logger?.warn("audio-codec: dispose failed during reap", {
|
|
282
|
+
tags: { sessionId: id },
|
|
283
|
+
meta: { error: errMsg(err) }
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
this.sessions.delete(id);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
disposeSession(s) {
|
|
291
|
+
if (s.kind === "decode" && s.runtime) {
|
|
292
|
+
try {
|
|
293
|
+
s.runtime.close();
|
|
294
|
+
} catch (err) {
|
|
295
|
+
this.logger?.warn("audio-codec: decode runtime close failed", {
|
|
296
|
+
tags: { sessionId: s.sessionId },
|
|
297
|
+
meta: { error: errMsg(err) }
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
s.runtime = null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
466
303
|
};
|
|
467
|
-
//#
|
|
304
|
+
//#endregion
|
|
305
|
+
export { AudioCodecNodeAvAddon, AudioCodecNodeAvAddon as default };
|