@camstack/addon-pipeline 1.1.42 → 1.1.44
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 +5 -4
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/chunk-D6vf50IK.js +28 -0
- package/dist/detection-pipeline/index.js +535 -115
- package/dist/detection-pipeline/index.mjs +529 -110
- package/dist/{dist-GFd8M6KO.mjs → dist-CgBGV988.mjs} +214 -186
- package/dist/{dist-NvwN60Fq.js → dist-CySHUkXY.js} +219 -185
- package/dist/{model-download-service-C-IHWnXx-DxM2DSns.js → model-download-service-C-IHWnXx-BLXzxpRB.js} +3 -29
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +122 -287
- package/dist/pipeline-runner/index.mjs +118 -285
- package/dist/recorder/index.js +78 -31
- package/dist/recorder/index.mjs +75 -29
- package/dist/{frame-handle-plane-E_AsR77T.mjs → remote-source-plane-B4hRwchu.mjs} +219 -2
- package/dist/{frame-handle-plane-B3Fxww8H.js → remote-source-plane-D9m0z1N9.js} +236 -1
- package/dist/session-decode/decode-worker-child.js +118 -12
- package/dist/session-decode/decode-worker-child.mjs +115 -12
- package/dist/stream-broker/_stub.js +550 -523
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CeielP5P.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-G6wtUb3G.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Dlagq9X_.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-BUpF966l.mjs +26 -0
- package/dist/stream-broker/{hostInit-DmjqskOR.mjs → hostInit-BSzP3BIj.mjs} +2 -2
- package/dist/stream-broker/index.js +840 -196
- package/dist/stream-broker/index.mjs +831 -188
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CEsPjwzT.js → MaskShapeCanvas-DI4BY7W2-D2ML5TPi.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-D43fIRgB.js → MotionZonesSettings-NcxxQN8r-TTWGGaqv.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-C3wJxG8V.js → PrivacyMaskSettings-APgPLF7p-CJqHU2nU.js} +1 -1
- package/embed-dist/assets/index-C1oaL9s7.js +81 -0
- package/embed-dist/assets/{index-CSFtK41z.css → index-CA4kv43T.css} +1 -1
- package/embed-dist/index.html +2 -2
- package/package.json +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DqJvWBKS.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-dGka8yZY.mjs +0 -26
- package/embed-dist/assets/index-CX-rILhw.js +0 -81
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
2
3
|
const require_worker_protocol = require("../worker-protocol-BCfO8gUF.js");
|
|
4
|
+
let node_url = require("node:url");
|
|
5
|
+
let node_fs = require("node:fs");
|
|
6
|
+
node_fs = require_chunk.__toESM(node_fs);
|
|
7
|
+
let node_path = require("node:path");
|
|
8
|
+
node_path = require_chunk.__toESM(node_path);
|
|
3
9
|
//#region src/session-decode/frame-slot.ts
|
|
4
10
|
function toReply(held) {
|
|
5
11
|
return {
|
|
@@ -104,6 +110,30 @@ var FrameSlot = class {
|
|
|
104
110
|
};
|
|
105
111
|
//#endregion
|
|
106
112
|
//#region src/session-decode/decode-worker-child.ts
|
|
113
|
+
/**
|
|
114
|
+
* Decode-worker CHILD entry point — Epic C P1, Task 2.
|
|
115
|
+
*
|
|
116
|
+
* This file is the whole program run by a per-session `child_process.fork`
|
|
117
|
+
* that the pipeline-runner's session-decode coordinator (Task 3) spawns and
|
|
118
|
+
* tears down on session end, so the OS reclaims the VAAPI/GPU decode pool by
|
|
119
|
+
* process exit (Task 0 live-validated that reclaim-by-exit works — node-av
|
|
120
|
+
* does not release the hw_frames_ctx pool on `dispose()` alone). It decodes
|
|
121
|
+
* with node-av in-process and hands decoded frames to its parent as a
|
|
122
|
+
* request/response PULL over its own dedicated fork IPC channel. Frames
|
|
123
|
+
* NEVER touch `/dev/shm` — no shm sink is ever constructed here.
|
|
124
|
+
*
|
|
125
|
+
* ‼ SANCTIONED RAW IPC — READ BEFORE "FIXING" THIS FILE ‼
|
|
126
|
+
* CLAUDE.md rule #8 forbids `process.send`/`process.on('message')` for
|
|
127
|
+
* BUSINESS LOGIC — that rule governs cross-ADDON traffic, which must travel
|
|
128
|
+
* over the UDS cap/event channel (`LocalChildRegistry`/`LocalChildClient`)
|
|
129
|
+
* instead of raw IPC. This file is NOT a second addon: it is a private
|
|
130
|
+
* worker directly forked by the pipeline-runner addon, exactly like the
|
|
131
|
+
* Python inference pool's own subprocess. The Epic C P1 plan's Global
|
|
132
|
+
* Constraints explicitly carve this out — "a directly-forked worker child
|
|
133
|
+
* talks to its parent over its own IPC channel (this is NOT a cap call and
|
|
134
|
+
* NOT `$process`)". Raw `process.on('message')` / `process.send` are the
|
|
135
|
+
* correct, sanctioned mechanism for this specific file only.
|
|
136
|
+
*/
|
|
107
137
|
/** Re-dial backoff on a transient dial error/EOF (mirrors the decoder's PULL_REDIAL_MS). */
|
|
108
138
|
var REDIAL_MS = 3e3;
|
|
109
139
|
/** How often the worker emits its `framesDecoded/framesSkipped/deliveredFps` line. */
|
|
@@ -129,12 +159,66 @@ function pixelFilterName(format) {
|
|
|
129
159
|
}
|
|
130
160
|
var _nav = null;
|
|
131
161
|
var _consts = null;
|
|
162
|
+
/**
|
|
163
|
+
* Candidate file URLs for a node-av entry (`.` or `./constants`) in the
|
|
164
|
+
* module locations an AGENT lays node-av out in.
|
|
165
|
+
*
|
|
166
|
+
* WHY: this worker runs as a plain `child_process.fork` (see `fork-decode-
|
|
167
|
+
* worker.ts`) which does NOT inherit the addon-runner's module resolution
|
|
168
|
+
* (NODE_PATH / the framework ESM hook). On the HUB the fork inherits that hook,
|
|
169
|
+
* so the bare `import('node-av')` resolves. On an AGENT it does not, so the
|
|
170
|
+
* bare import throws `Cannot find package 'node-av'` and we resolve node-av
|
|
171
|
+
* explicitly from the agent's known layout:
|
|
172
|
+
* • DOCKER agent — the framework overlay `CAMSTACK_FRAMEWORK_DIR`
|
|
173
|
+
* (`/data/framework/node_modules/node-av`).
|
|
174
|
+
* • ELECTRON agent — the app bundle beside `CAMSTACK_BUNDLED_ADDONS_DIR`
|
|
175
|
+
* (`<resources>/addons`): `<resources>/node_modules/node-av` (+ the
|
|
176
|
+
* `app.asar.unpacked` mirror).
|
|
177
|
+
* Both env vars point at real dirs only on the respective platform; a missing
|
|
178
|
+
* dir is skipped, and `[]` (dev / hub with the bare import already working)
|
|
179
|
+
* keeps the bare specifier the only path.
|
|
180
|
+
*/
|
|
181
|
+
function fallbackNodeAvUrls(subpath) {
|
|
182
|
+
const rel = subpath === "." ? "dist/index.js" : "dist/constants/index.js";
|
|
183
|
+
const dirs = [];
|
|
184
|
+
const frameworkDir = process.env["CAMSTACK_FRAMEWORK_DIR"];
|
|
185
|
+
if (frameworkDir !== void 0 && frameworkDir.length > 0) dirs.push(node_path.join(frameworkDir, "node_modules", "node-av"));
|
|
186
|
+
const bundledAddons = process.env["CAMSTACK_BUNDLED_ADDONS_DIR"];
|
|
187
|
+
if (bundledAddons !== void 0 && bundledAddons.length > 0) {
|
|
188
|
+
const resourcesRoot = node_path.dirname(bundledAddons);
|
|
189
|
+
dirs.push(node_path.join(resourcesRoot, "node_modules", "node-av"));
|
|
190
|
+
dirs.push(node_path.join(resourcesRoot, "app.asar.unpacked", "node_modules", "node-av"));
|
|
191
|
+
}
|
|
192
|
+
const urls = [];
|
|
193
|
+
for (const dir of dirs) {
|
|
194
|
+
const entry = node_path.join(dir, rel);
|
|
195
|
+
if (node_fs.existsSync(entry)) urls.push((0, node_url.pathToFileURL)(entry).href);
|
|
196
|
+
}
|
|
197
|
+
return urls;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Import a node-av entry, falling back to the agent's explicit node-av path
|
|
201
|
+
* when the bare specifier can't be resolved (see {@link fallbackNodeAvUrls}).
|
|
202
|
+
* Each candidate is tried in turn so a copy whose native binary fails to load
|
|
203
|
+
* doesn't mask a working one. A dynamic `import(url)` is typed `any`, so the
|
|
204
|
+
* caller's declared module type applies without a cast.
|
|
205
|
+
*/
|
|
206
|
+
async function importNodeAv(bareSpecifier, subpath) {
|
|
207
|
+
try {
|
|
208
|
+
return await import(bareSpecifier);
|
|
209
|
+
} catch (bareErr) {
|
|
210
|
+
for (const url of fallbackNodeAvUrls(subpath)) try {
|
|
211
|
+
return await import(url);
|
|
212
|
+
} catch {}
|
|
213
|
+
throw bareErr;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
132
216
|
async function getNodeAv() {
|
|
133
|
-
if (!_nav) _nav = await
|
|
217
|
+
if (!_nav) _nav = await importNodeAv("node-av", ".");
|
|
134
218
|
return _nav;
|
|
135
219
|
}
|
|
136
220
|
async function getConstants() {
|
|
137
|
-
if (!_consts) _consts = await
|
|
221
|
+
if (!_consts) _consts = await importNodeAv("node-av/constants", "./constants");
|
|
138
222
|
return _consts;
|
|
139
223
|
}
|
|
140
224
|
function errMessage(err) {
|
|
@@ -318,6 +402,16 @@ var DecodeWorkerChild = class {
|
|
|
318
402
|
abortController = null;
|
|
319
403
|
started = false;
|
|
320
404
|
stopped = false;
|
|
405
|
+
/**
|
|
406
|
+
* The numeric device id this worker decodes for — learned from the `start`
|
|
407
|
+
* message's {@link VideoFrameSource}. The child's stderr is INHERITED by the
|
|
408
|
+
* pipeline-runner (silent:false fork), so these lines land in the runner's
|
|
409
|
+
* raw stderr WITHOUT passing through a scoped logger; prefixing every line
|
|
410
|
+
* with `[dev:<id>]` (see {@link emitStderr}) is what lets the per-device log
|
|
411
|
+
* UI attribute the forked worker's output to its camera. `null` only for the
|
|
412
|
+
* brief pre-`start` window (e.g. a malformed first message).
|
|
413
|
+
*/
|
|
414
|
+
deviceId = null;
|
|
321
415
|
sessionFormat = "rgb";
|
|
322
416
|
minIntervalMs = 0;
|
|
323
417
|
/**
|
|
@@ -390,16 +484,17 @@ var DecodeWorkerChild = class {
|
|
|
390
484
|
const deliveredDelta = this.framesDelivered - this.lastDeliveredSnapshot;
|
|
391
485
|
const deliveredFps = Math.round(deliveredDelta / windowMs * 1e3 * 10) / 10;
|
|
392
486
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
393
|
-
|
|
487
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}}${final ? " (final)" : ""}\n`);
|
|
394
488
|
this.lastMetricsAt = now;
|
|
395
489
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
396
490
|
}
|
|
397
491
|
handleStart(source, opts) {
|
|
398
492
|
if (this.started) {
|
|
399
|
-
|
|
493
|
+
this.emitStderr("decode-worker-child: duplicate start ignored\n");
|
|
400
494
|
return;
|
|
401
495
|
}
|
|
402
496
|
this.started = true;
|
|
497
|
+
this.deviceId = source.deviceId;
|
|
403
498
|
this.sessionFormat = opts.format ?? "rgb";
|
|
404
499
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
405
500
|
this.lastMetricsAt = Date.now();
|
|
@@ -456,7 +551,7 @@ var DecodeWorkerChild = class {
|
|
|
456
551
|
await this.dialAndDecode(nav, C, url);
|
|
457
552
|
} catch (err) {
|
|
458
553
|
if (this.stopped) break;
|
|
459
|
-
|
|
554
|
+
this.emitStderr(`decode-worker-child: dial ended — re-dial scheduled: ${errMessage(err)}\n`);
|
|
460
555
|
} finally {
|
|
461
556
|
this.closeInput();
|
|
462
557
|
}
|
|
@@ -549,7 +644,7 @@ var DecodeWorkerChild = class {
|
|
|
549
644
|
return;
|
|
550
645
|
}
|
|
551
646
|
this.hwFilterDecision = "hardware";
|
|
552
|
-
|
|
647
|
+
this.emitStderr(`decode-worker-child: GPU crop+scale path active (${this.gpuScaleFilter})\n`);
|
|
553
648
|
}
|
|
554
649
|
this.publishFrame(frame);
|
|
555
650
|
}
|
|
@@ -587,7 +682,7 @@ var DecodeWorkerChild = class {
|
|
|
587
682
|
for (const out of outputs) out.free();
|
|
588
683
|
return ok;
|
|
589
684
|
} catch (err) {
|
|
590
|
-
|
|
685
|
+
this.emitStderr(`decode-worker-child: GPU filter probe failed — using software decode: ${errMessage(err)}\n`);
|
|
591
686
|
return false;
|
|
592
687
|
} finally {
|
|
593
688
|
filter?.close();
|
|
@@ -764,7 +859,7 @@ var DecodeWorkerChild = class {
|
|
|
764
859
|
if (deviceType === null) continue;
|
|
765
860
|
const hw = nav.HardwareContext.create(deviceType);
|
|
766
861
|
if (!hw) {
|
|
767
|
-
|
|
862
|
+
this.emitStderr(`decode-worker-child: hwaccel candidate '${backend}' failed — trying next\n`);
|
|
768
863
|
continue;
|
|
769
864
|
}
|
|
770
865
|
this.hwContext = hw;
|
|
@@ -772,7 +867,7 @@ var DecodeWorkerChild = class {
|
|
|
772
867
|
this.hwFilterDecision = this.gpuScaleFilter !== null ? "undecided" : "software";
|
|
773
868
|
return;
|
|
774
869
|
}
|
|
775
|
-
|
|
870
|
+
this.emitStderr("decode-worker-child: no hwaccel backend available — using software decode\n");
|
|
776
871
|
}
|
|
777
872
|
/** Interruptible re-dial backoff — `teardown` clears the pending timer. */
|
|
778
873
|
sleep(ms) {
|
|
@@ -803,8 +898,19 @@ var DecodeWorkerChild = class {
|
|
|
803
898
|
send(reply) {
|
|
804
899
|
process.send?.(reply);
|
|
805
900
|
}
|
|
901
|
+
/**
|
|
902
|
+
* Write one diagnostic line to the (inherited) stderr, prefixed with the
|
|
903
|
+
* device tag so the per-device log UI can attribute the forked worker's
|
|
904
|
+
* output. Public so the module-level process handlers — which only hold the
|
|
905
|
+
* `worker` instance — emit device-tagged lines too. Callers pass the line
|
|
906
|
+
* INCLUDING its trailing newline (unchanged from the prior direct writes).
|
|
907
|
+
*/
|
|
908
|
+
emitStderr(line) {
|
|
909
|
+
const tag = this.deviceId === null ? "[dev:?]" : `[dev:${this.deviceId}]`;
|
|
910
|
+
process.stderr.write(`${tag} ${line}`);
|
|
911
|
+
}
|
|
806
912
|
sendError(message, frameId) {
|
|
807
|
-
|
|
913
|
+
this.emitStderr(`${message}\n`);
|
|
808
914
|
this.send(frameId === void 0 ? {
|
|
809
915
|
kind: "error",
|
|
810
916
|
message
|
|
@@ -824,12 +930,12 @@ process.on("disconnect", () => {
|
|
|
824
930
|
process.exit(0);
|
|
825
931
|
});
|
|
826
932
|
process.on("uncaughtException", (err) => {
|
|
827
|
-
|
|
933
|
+
worker.emitStderr(`decode-worker-child: uncaught exception — ${errMessage(err)}\n`);
|
|
828
934
|
worker.teardown();
|
|
829
935
|
process.exit(1);
|
|
830
936
|
});
|
|
831
937
|
process.on("unhandledRejection", (reason) => {
|
|
832
|
-
|
|
938
|
+
worker.emitStderr(`decode-worker-child: unhandled rejection — ${errMessage(reason)}\n`);
|
|
833
939
|
});
|
|
834
940
|
//#endregion
|
|
835
941
|
exports.DecodeWorkerChild = DecodeWorkerChild;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { n as isWorkerRequest } from "../worker-protocol-pk7qdYXt.mjs";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path$1 from "node:path";
|
|
2
5
|
//#region src/session-decode/frame-slot.ts
|
|
3
6
|
function toReply(held) {
|
|
4
7
|
return {
|
|
@@ -103,6 +106,30 @@ var FrameSlot = class {
|
|
|
103
106
|
};
|
|
104
107
|
//#endregion
|
|
105
108
|
//#region src/session-decode/decode-worker-child.ts
|
|
109
|
+
/**
|
|
110
|
+
* Decode-worker CHILD entry point — Epic C P1, Task 2.
|
|
111
|
+
*
|
|
112
|
+
* This file is the whole program run by a per-session `child_process.fork`
|
|
113
|
+
* that the pipeline-runner's session-decode coordinator (Task 3) spawns and
|
|
114
|
+
* tears down on session end, so the OS reclaims the VAAPI/GPU decode pool by
|
|
115
|
+
* process exit (Task 0 live-validated that reclaim-by-exit works — node-av
|
|
116
|
+
* does not release the hw_frames_ctx pool on `dispose()` alone). It decodes
|
|
117
|
+
* with node-av in-process and hands decoded frames to its parent as a
|
|
118
|
+
* request/response PULL over its own dedicated fork IPC channel. Frames
|
|
119
|
+
* NEVER touch `/dev/shm` — no shm sink is ever constructed here.
|
|
120
|
+
*
|
|
121
|
+
* ‼ SANCTIONED RAW IPC — READ BEFORE "FIXING" THIS FILE ‼
|
|
122
|
+
* CLAUDE.md rule #8 forbids `process.send`/`process.on('message')` for
|
|
123
|
+
* BUSINESS LOGIC — that rule governs cross-ADDON traffic, which must travel
|
|
124
|
+
* over the UDS cap/event channel (`LocalChildRegistry`/`LocalChildClient`)
|
|
125
|
+
* instead of raw IPC. This file is NOT a second addon: it is a private
|
|
126
|
+
* worker directly forked by the pipeline-runner addon, exactly like the
|
|
127
|
+
* Python inference pool's own subprocess. The Epic C P1 plan's Global
|
|
128
|
+
* Constraints explicitly carve this out — "a directly-forked worker child
|
|
129
|
+
* talks to its parent over its own IPC channel (this is NOT a cap call and
|
|
130
|
+
* NOT `$process`)". Raw `process.on('message')` / `process.send` are the
|
|
131
|
+
* correct, sanctioned mechanism for this specific file only.
|
|
132
|
+
*/
|
|
106
133
|
/** Re-dial backoff on a transient dial error/EOF (mirrors the decoder's PULL_REDIAL_MS). */
|
|
107
134
|
var REDIAL_MS = 3e3;
|
|
108
135
|
/** How often the worker emits its `framesDecoded/framesSkipped/deliveredFps` line. */
|
|
@@ -128,12 +155,66 @@ function pixelFilterName(format) {
|
|
|
128
155
|
}
|
|
129
156
|
var _nav = null;
|
|
130
157
|
var _consts = null;
|
|
158
|
+
/**
|
|
159
|
+
* Candidate file URLs for a node-av entry (`.` or `./constants`) in the
|
|
160
|
+
* module locations an AGENT lays node-av out in.
|
|
161
|
+
*
|
|
162
|
+
* WHY: this worker runs as a plain `child_process.fork` (see `fork-decode-
|
|
163
|
+
* worker.ts`) which does NOT inherit the addon-runner's module resolution
|
|
164
|
+
* (NODE_PATH / the framework ESM hook). On the HUB the fork inherits that hook,
|
|
165
|
+
* so the bare `import('node-av')` resolves. On an AGENT it does not, so the
|
|
166
|
+
* bare import throws `Cannot find package 'node-av'` and we resolve node-av
|
|
167
|
+
* explicitly from the agent's known layout:
|
|
168
|
+
* • DOCKER agent — the framework overlay `CAMSTACK_FRAMEWORK_DIR`
|
|
169
|
+
* (`/data/framework/node_modules/node-av`).
|
|
170
|
+
* • ELECTRON agent — the app bundle beside `CAMSTACK_BUNDLED_ADDONS_DIR`
|
|
171
|
+
* (`<resources>/addons`): `<resources>/node_modules/node-av` (+ the
|
|
172
|
+
* `app.asar.unpacked` mirror).
|
|
173
|
+
* Both env vars point at real dirs only on the respective platform; a missing
|
|
174
|
+
* dir is skipped, and `[]` (dev / hub with the bare import already working)
|
|
175
|
+
* keeps the bare specifier the only path.
|
|
176
|
+
*/
|
|
177
|
+
function fallbackNodeAvUrls(subpath) {
|
|
178
|
+
const rel = subpath === "." ? "dist/index.js" : "dist/constants/index.js";
|
|
179
|
+
const dirs = [];
|
|
180
|
+
const frameworkDir = process.env["CAMSTACK_FRAMEWORK_DIR"];
|
|
181
|
+
if (frameworkDir !== void 0 && frameworkDir.length > 0) dirs.push(path$1.join(frameworkDir, "node_modules", "node-av"));
|
|
182
|
+
const bundledAddons = process.env["CAMSTACK_BUNDLED_ADDONS_DIR"];
|
|
183
|
+
if (bundledAddons !== void 0 && bundledAddons.length > 0) {
|
|
184
|
+
const resourcesRoot = path$1.dirname(bundledAddons);
|
|
185
|
+
dirs.push(path$1.join(resourcesRoot, "node_modules", "node-av"));
|
|
186
|
+
dirs.push(path$1.join(resourcesRoot, "app.asar.unpacked", "node_modules", "node-av"));
|
|
187
|
+
}
|
|
188
|
+
const urls = [];
|
|
189
|
+
for (const dir of dirs) {
|
|
190
|
+
const entry = path$1.join(dir, rel);
|
|
191
|
+
if (fs.existsSync(entry)) urls.push(pathToFileURL(entry).href);
|
|
192
|
+
}
|
|
193
|
+
return urls;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Import a node-av entry, falling back to the agent's explicit node-av path
|
|
197
|
+
* when the bare specifier can't be resolved (see {@link fallbackNodeAvUrls}).
|
|
198
|
+
* Each candidate is tried in turn so a copy whose native binary fails to load
|
|
199
|
+
* doesn't mask a working one. A dynamic `import(url)` is typed `any`, so the
|
|
200
|
+
* caller's declared module type applies without a cast.
|
|
201
|
+
*/
|
|
202
|
+
async function importNodeAv(bareSpecifier, subpath) {
|
|
203
|
+
try {
|
|
204
|
+
return await import(bareSpecifier);
|
|
205
|
+
} catch (bareErr) {
|
|
206
|
+
for (const url of fallbackNodeAvUrls(subpath)) try {
|
|
207
|
+
return await import(url);
|
|
208
|
+
} catch {}
|
|
209
|
+
throw bareErr;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
131
212
|
async function getNodeAv() {
|
|
132
|
-
if (!_nav) _nav = await
|
|
213
|
+
if (!_nav) _nav = await importNodeAv("node-av", ".");
|
|
133
214
|
return _nav;
|
|
134
215
|
}
|
|
135
216
|
async function getConstants() {
|
|
136
|
-
if (!_consts) _consts = await
|
|
217
|
+
if (!_consts) _consts = await importNodeAv("node-av/constants", "./constants");
|
|
137
218
|
return _consts;
|
|
138
219
|
}
|
|
139
220
|
function errMessage(err) {
|
|
@@ -317,6 +398,16 @@ var DecodeWorkerChild = class {
|
|
|
317
398
|
abortController = null;
|
|
318
399
|
started = false;
|
|
319
400
|
stopped = false;
|
|
401
|
+
/**
|
|
402
|
+
* The numeric device id this worker decodes for — learned from the `start`
|
|
403
|
+
* message's {@link VideoFrameSource}. The child's stderr is INHERITED by the
|
|
404
|
+
* pipeline-runner (silent:false fork), so these lines land in the runner's
|
|
405
|
+
* raw stderr WITHOUT passing through a scoped logger; prefixing every line
|
|
406
|
+
* with `[dev:<id>]` (see {@link emitStderr}) is what lets the per-device log
|
|
407
|
+
* UI attribute the forked worker's output to its camera. `null` only for the
|
|
408
|
+
* brief pre-`start` window (e.g. a malformed first message).
|
|
409
|
+
*/
|
|
410
|
+
deviceId = null;
|
|
320
411
|
sessionFormat = "rgb";
|
|
321
412
|
minIntervalMs = 0;
|
|
322
413
|
/**
|
|
@@ -389,16 +480,17 @@ var DecodeWorkerChild = class {
|
|
|
389
480
|
const deliveredDelta = this.framesDelivered - this.lastDeliveredSnapshot;
|
|
390
481
|
const deliveredFps = Math.round(deliveredDelta / windowMs * 1e3 * 10) / 10;
|
|
391
482
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
392
|
-
|
|
483
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}}${final ? " (final)" : ""}\n`);
|
|
393
484
|
this.lastMetricsAt = now;
|
|
394
485
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
395
486
|
}
|
|
396
487
|
handleStart(source, opts) {
|
|
397
488
|
if (this.started) {
|
|
398
|
-
|
|
489
|
+
this.emitStderr("decode-worker-child: duplicate start ignored\n");
|
|
399
490
|
return;
|
|
400
491
|
}
|
|
401
492
|
this.started = true;
|
|
493
|
+
this.deviceId = source.deviceId;
|
|
402
494
|
this.sessionFormat = opts.format ?? "rgb";
|
|
403
495
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
404
496
|
this.lastMetricsAt = Date.now();
|
|
@@ -455,7 +547,7 @@ var DecodeWorkerChild = class {
|
|
|
455
547
|
await this.dialAndDecode(nav, C, url);
|
|
456
548
|
} catch (err) {
|
|
457
549
|
if (this.stopped) break;
|
|
458
|
-
|
|
550
|
+
this.emitStderr(`decode-worker-child: dial ended — re-dial scheduled: ${errMessage(err)}\n`);
|
|
459
551
|
} finally {
|
|
460
552
|
this.closeInput();
|
|
461
553
|
}
|
|
@@ -548,7 +640,7 @@ var DecodeWorkerChild = class {
|
|
|
548
640
|
return;
|
|
549
641
|
}
|
|
550
642
|
this.hwFilterDecision = "hardware";
|
|
551
|
-
|
|
643
|
+
this.emitStderr(`decode-worker-child: GPU crop+scale path active (${this.gpuScaleFilter})\n`);
|
|
552
644
|
}
|
|
553
645
|
this.publishFrame(frame);
|
|
554
646
|
}
|
|
@@ -586,7 +678,7 @@ var DecodeWorkerChild = class {
|
|
|
586
678
|
for (const out of outputs) out.free();
|
|
587
679
|
return ok;
|
|
588
680
|
} catch (err) {
|
|
589
|
-
|
|
681
|
+
this.emitStderr(`decode-worker-child: GPU filter probe failed — using software decode: ${errMessage(err)}\n`);
|
|
590
682
|
return false;
|
|
591
683
|
} finally {
|
|
592
684
|
filter?.close();
|
|
@@ -763,7 +855,7 @@ var DecodeWorkerChild = class {
|
|
|
763
855
|
if (deviceType === null) continue;
|
|
764
856
|
const hw = nav.HardwareContext.create(deviceType);
|
|
765
857
|
if (!hw) {
|
|
766
|
-
|
|
858
|
+
this.emitStderr(`decode-worker-child: hwaccel candidate '${backend}' failed — trying next\n`);
|
|
767
859
|
continue;
|
|
768
860
|
}
|
|
769
861
|
this.hwContext = hw;
|
|
@@ -771,7 +863,7 @@ var DecodeWorkerChild = class {
|
|
|
771
863
|
this.hwFilterDecision = this.gpuScaleFilter !== null ? "undecided" : "software";
|
|
772
864
|
return;
|
|
773
865
|
}
|
|
774
|
-
|
|
866
|
+
this.emitStderr("decode-worker-child: no hwaccel backend available — using software decode\n");
|
|
775
867
|
}
|
|
776
868
|
/** Interruptible re-dial backoff — `teardown` clears the pending timer. */
|
|
777
869
|
sleep(ms) {
|
|
@@ -802,8 +894,19 @@ var DecodeWorkerChild = class {
|
|
|
802
894
|
send(reply) {
|
|
803
895
|
process.send?.(reply);
|
|
804
896
|
}
|
|
897
|
+
/**
|
|
898
|
+
* Write one diagnostic line to the (inherited) stderr, prefixed with the
|
|
899
|
+
* device tag so the per-device log UI can attribute the forked worker's
|
|
900
|
+
* output. Public so the module-level process handlers — which only hold the
|
|
901
|
+
* `worker` instance — emit device-tagged lines too. Callers pass the line
|
|
902
|
+
* INCLUDING its trailing newline (unchanged from the prior direct writes).
|
|
903
|
+
*/
|
|
904
|
+
emitStderr(line) {
|
|
905
|
+
const tag = this.deviceId === null ? "[dev:?]" : `[dev:${this.deviceId}]`;
|
|
906
|
+
process.stderr.write(`${tag} ${line}`);
|
|
907
|
+
}
|
|
805
908
|
sendError(message, frameId) {
|
|
806
|
-
|
|
909
|
+
this.emitStderr(`${message}\n`);
|
|
807
910
|
this.send(frameId === void 0 ? {
|
|
808
911
|
kind: "error",
|
|
809
912
|
message
|
|
@@ -823,12 +926,12 @@ process.on("disconnect", () => {
|
|
|
823
926
|
process.exit(0);
|
|
824
927
|
});
|
|
825
928
|
process.on("uncaughtException", (err) => {
|
|
826
|
-
|
|
929
|
+
worker.emitStderr(`decode-worker-child: uncaught exception — ${errMessage(err)}\n`);
|
|
827
930
|
worker.teardown();
|
|
828
931
|
process.exit(1);
|
|
829
932
|
});
|
|
830
933
|
process.on("unhandledRejection", (reason) => {
|
|
831
|
-
|
|
934
|
+
worker.emitStderr(`decode-worker-child: unhandled rejection — ${errMessage(reason)}\n`);
|
|
832
935
|
});
|
|
833
936
|
//#endregion
|
|
834
937
|
export { DecodeWorkerChild };
|