@camstack/addon-pipeline 1.1.27 → 1.1.28
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 +7 -6
- package/dist/audio-analyzer/index.mjs +7 -6
- package/dist/audio-codec-ffmpeg/index.js +1 -1
- package/dist/audio-codec-ffmpeg/index.mjs +1 -1
- package/dist/decoder-ffmpeg/index.js +43 -11
- package/dist/decoder-ffmpeg/index.mjs +43 -11
- package/dist/decoder-nodeav/index.js +13 -8
- package/dist/decoder-nodeav/index.mjs +13 -8
- package/dist/detection-pipeline/index.js +157 -219
- package/dist/detection-pipeline/index.mjs +157 -219
- package/dist/{dist-DAIlCdAx.js → dist-Biq62zt4.js} +372 -35
- package/dist/{dist-CgEP_0OL.mjs → dist-C6_wgXqF.mjs} +373 -24
- package/dist/{frame-handle-plane-Dq20KtKL.mjs → frame-handle-plane-BIoY6nRV.mjs} +13 -5
- package/dist/{frame-handle-plane-DtTRX_0n.js → frame-handle-plane-D6BzyEgy.js} +13 -5
- package/dist/{frame-ring-sink-BbzrqTWj.js → frame-ring-sink-9J0wCdLF.js} +128 -23
- package/dist/{frame-ring-sink-B_NvPTJZ.mjs → frame-ring-sink-Cs9vby6v.mjs} +117 -24
- package/dist/motion-wasm/index.js +55 -3
- package/dist/motion-wasm/index.mjs +55 -3
- package/dist/node-topology-platform-BkR_k6WT.mjs +15 -0
- package/dist/node-topology-platform-CFZ7F4xW.js +20 -0
- package/dist/pipeline-runner/index.js +50 -16
- package/dist/pipeline-runner/index.mjs +50 -16
- package/dist/recorder/index.js +36 -40
- package/dist/recorder/index.mjs +36 -40
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5tQlh9h4.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DoReAb4y.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.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-BJK0-svt.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DtoYGpTp.mjs} +1 -1
- package/dist/stream-broker/{hostInit-DyLqyJaS.mjs → hostInit-C0SuwQhL.mjs} +3 -3
- package/dist/stream-broker/index.js +61 -25
- package/dist/stream-broker/index.mjs +61 -25
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BDLNwJ_F.js → MaskShapeCanvas-DI4BY7W2-Br2yi8ah.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CoLjNiUN.js → MotionZonesSettings-NcxxQN8r-BSCcPdPf.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DJE3OU-q.js → PrivacyMaskSettings-APgPLF7p-ztxvxSjS.js} +1 -1
- package/embed-dist/assets/index-C5UpuPr8.css +2 -0
- package/embed-dist/assets/{index-C-pL8ETk.js → index-CeZV1B-2.js} +10 -10
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/inference_pool.py +93 -102
- package/python/test_inference_pool_device_selection.py +43 -38
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CkOPfV8r.mjs +0 -26
- package/embed-dist/assets/index-DrJ0ee3f.css +0 -2
|
@@ -3,7 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-DxM2DSns.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
7
|
+
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
7
8
|
let node_fs = require("node:fs");
|
|
8
9
|
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
10
|
let node_path = require("node:path");
|
|
@@ -672,6 +673,7 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
672
673
|
description: "Auto-detected best audio backend on this host. Click the refresh icon to re-run the probe.",
|
|
673
674
|
readonlyField: true,
|
|
674
675
|
default: "",
|
|
676
|
+
perNode: true,
|
|
675
677
|
actions: [{
|
|
676
678
|
action: "reprobe-audio-engine",
|
|
677
679
|
icon: "refresh-cw",
|
|
@@ -720,8 +722,7 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
720
722
|
* semantics detection-pipeline relies on.
|
|
721
723
|
*/
|
|
722
724
|
async getGlobalSettings(overlay, _cap, nodeId) {
|
|
723
|
-
const
|
|
724
|
-
const stored = ctx?.settings ? await ctx.settings.readAddonStore() ?? {} : {};
|
|
725
|
+
const stored = await this.resolveGlobalStore(nodeId);
|
|
725
726
|
const merged = overlay ? {
|
|
726
727
|
...stored,
|
|
727
728
|
...overlay
|
|
@@ -777,8 +778,8 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
777
778
|
const localNode = this.ctxIfReady?.kernel?.localNodeId;
|
|
778
779
|
if (!nodeId || nodeId === localNode) return process.platform;
|
|
779
780
|
try {
|
|
780
|
-
const
|
|
781
|
-
if (
|
|
781
|
+
const { platform } = require_node_topology_platform.pickNodePlatformArch(await this.ctxIfReady?.api?.nodes?.topology.query(), nodeId);
|
|
782
|
+
if (platform) return platform;
|
|
782
783
|
} catch {}
|
|
783
784
|
return process.platform;
|
|
784
785
|
}
|
|
@@ -789,7 +790,7 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
789
790
|
*/
|
|
790
791
|
async reprobeAudioEngine() {
|
|
791
792
|
const backend = process.platform === "darwin" ? "apple-soundanalysis" : "yamnet-onnx";
|
|
792
|
-
await this.
|
|
793
|
+
await this.updateGlobalSettings({ probedBestAudioBackend: backend });
|
|
793
794
|
this.ctx.logger.info("reprobeAudioEngine: wrote probedBestAudioBackend", { meta: { backend } });
|
|
794
795
|
return { backend };
|
|
795
796
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { B as errMsg, D as mapAudioLabelToMacro, V as BaseAddon,
|
|
2
|
+
import { B as errMsg, D as mapAudioLabelToMacro, V as BaseAddon, _ as audioAnalyzerCapability, g as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, q as hydrateSchema, u as HF_BASE_URL } from "../dist-C6_wgXqF.mjs";
|
|
3
|
+
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
3
4
|
import { r as downloadFile } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
4
5
|
import * as fs from "node:fs";
|
|
5
6
|
import * as path$1 from "node:path";
|
|
@@ -667,6 +668,7 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
667
668
|
description: "Auto-detected best audio backend on this host. Click the refresh icon to re-run the probe.",
|
|
668
669
|
readonlyField: true,
|
|
669
670
|
default: "",
|
|
671
|
+
perNode: true,
|
|
670
672
|
actions: [{
|
|
671
673
|
action: "reprobe-audio-engine",
|
|
672
674
|
icon: "refresh-cw",
|
|
@@ -715,8 +717,7 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
715
717
|
* semantics detection-pipeline relies on.
|
|
716
718
|
*/
|
|
717
719
|
async getGlobalSettings(overlay, _cap, nodeId) {
|
|
718
|
-
const
|
|
719
|
-
const stored = ctx?.settings ? await ctx.settings.readAddonStore() ?? {} : {};
|
|
720
|
+
const stored = await this.resolveGlobalStore(nodeId);
|
|
720
721
|
const merged = overlay ? {
|
|
721
722
|
...stored,
|
|
722
723
|
...overlay
|
|
@@ -772,8 +773,8 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
772
773
|
const localNode = this.ctxIfReady?.kernel?.localNodeId;
|
|
773
774
|
if (!nodeId || nodeId === localNode) return process.platform;
|
|
774
775
|
try {
|
|
775
|
-
const
|
|
776
|
-
if (
|
|
776
|
+
const { platform } = pickNodePlatformArch(await this.ctxIfReady?.api?.nodes?.topology.query(), nodeId);
|
|
777
|
+
if (platform) return platform;
|
|
777
778
|
} catch {}
|
|
778
779
|
return process.platform;
|
|
779
780
|
}
|
|
@@ -784,7 +785,7 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
784
785
|
*/
|
|
785
786
|
async reprobeAudioEngine() {
|
|
786
787
|
const backend = process.platform === "darwin" ? "apple-soundanalysis" : "yamnet-onnx";
|
|
787
|
-
await this.
|
|
788
|
+
await this.updateGlobalSettings({ probedBestAudioBackend: backend });
|
|
788
789
|
this.ctx.logger.info("reprobeAudioEngine: wrote probedBestAudioBackend", { meta: { backend } });
|
|
789
790
|
return { backend };
|
|
790
791
|
}
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
6
6
|
let node_crypto = require("node:crypto");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
//#region src/audio-codec-ffmpeg/ffmpeg-audio-process.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as errMsg, V as BaseAddon, v as audioCodecCapability } from "../dist-
|
|
1
|
+
import { B as errMsg, V as BaseAddon, v as audioCodecCapability } from "../dist-C6_wgXqF.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region src/audio-codec-ffmpeg/ffmpeg-audio-process.ts
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_frame_ring_sink = require("../frame-ring-sink-
|
|
5
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
6
|
+
const require_frame_ring_sink = require("../frame-ring-sink-9J0wCdLF.js");
|
|
7
7
|
const require_frame_dropper = require("../frame-dropper-7RTo_YyG.js");
|
|
8
8
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
@@ -896,6 +896,18 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
|
|
|
896
896
|
//#endregion
|
|
897
897
|
//#region src/decoder-ffmpeg/addon/index.ts
|
|
898
898
|
var FRAME_BUFFER_CAPACITY = 32;
|
|
899
|
+
var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = {
|
|
900
|
+
...require_dist.DEFAULT_DECODER_HWACCEL_CONFIG,
|
|
901
|
+
backend: require_frame_ring_sink.DEFAULT_DECODER_BACKEND
|
|
902
|
+
};
|
|
903
|
+
/** The two selectable decoder backends, in default-first order. */
|
|
904
|
+
var DECODER_BACKEND_OPTIONS = [{
|
|
905
|
+
value: "ffmpeg",
|
|
906
|
+
label: "ffmpeg (subprocess — crash-isolated, default)"
|
|
907
|
+
}, {
|
|
908
|
+
value: "nodeav",
|
|
909
|
+
label: "node-av (in-process FFmpeg bindings)"
|
|
910
|
+
}];
|
|
899
911
|
var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
900
912
|
sessions = /* @__PURE__ */ new Map();
|
|
901
913
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
@@ -926,10 +938,24 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
926
938
|
*/
|
|
927
939
|
unsupportedGpuScaleFilters = /* @__PURE__ */ new Set();
|
|
928
940
|
constructor() {
|
|
929
|
-
super(
|
|
941
|
+
super(DEFAULT_DECODER_FFMPEG_ADDON_CONFIG);
|
|
930
942
|
}
|
|
931
943
|
globalSettingsSchema() {
|
|
932
944
|
return this.schema({ sections: [{
|
|
945
|
+
id: "backend",
|
|
946
|
+
title: "Decoder backend",
|
|
947
|
+
tab: "decoder",
|
|
948
|
+
description: "Which decoder addon provides the decoder capability on this node — per-node selection, stored centrally on the hub. Selection is decided once at addon init (registration-time, race-free), so a change applies at the next restart of the decoder addons on the node.",
|
|
949
|
+
fields: [this.field({
|
|
950
|
+
type: "select",
|
|
951
|
+
key: "backend",
|
|
952
|
+
label: "Backend",
|
|
953
|
+
options: [...DECODER_BACKEND_OPTIONS],
|
|
954
|
+
default: require_frame_ring_sink.DEFAULT_DECODER_BACKEND,
|
|
955
|
+
immediate: true,
|
|
956
|
+
perNode: true
|
|
957
|
+
})]
|
|
958
|
+
}, {
|
|
933
959
|
id: "hwaccel",
|
|
934
960
|
title: "Hardware acceleration",
|
|
935
961
|
tab: "decoder",
|
|
@@ -948,6 +974,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
948
974
|
description: "Auto-detected best decoder backend on this host. Click the refresh icon to re-run the probe.",
|
|
949
975
|
readonlyField: true,
|
|
950
976
|
default: "",
|
|
977
|
+
perNode: true,
|
|
951
978
|
actions: [{
|
|
952
979
|
action: "reprobe-hwaccel",
|
|
953
980
|
icon: "refresh-cw",
|
|
@@ -957,7 +984,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
957
984
|
}] });
|
|
958
985
|
}
|
|
959
986
|
async onInitialize() {
|
|
960
|
-
const backend = await require_frame_ring_sink.resolveDecoderBackend(this.ctx.
|
|
987
|
+
const backend = await require_frame_ring_sink.resolveDecoderBackend(this.ctx.api, this.resolveLocalNodeId(), this.ctx.logger);
|
|
961
988
|
if (backend !== "ffmpeg") {
|
|
962
989
|
this.ctx.logger.info("ffmpeg decoder: this node selects a different decoder backend — standing down (no decoder provider registered)", { meta: { selectedBackend: backend } });
|
|
963
990
|
return [];
|
|
@@ -1025,8 +1052,9 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1025
1052
|
if (pref === "none") return [];
|
|
1026
1053
|
if (pref !== "auto") return [pref];
|
|
1027
1054
|
try {
|
|
1028
|
-
const
|
|
1029
|
-
const
|
|
1055
|
+
const pin = require_dist.nodePin(this.resolveLocalNodeId());
|
|
1056
|
+
const res = await this.ctx.api.platformProbe.resolveHwAccel.query({ prefer: null }, pin);
|
|
1057
|
+
const supported = await this.ctx.api.platformProbe.getHardwareDecodeAccels.query(void 0, pin);
|
|
1030
1058
|
return rankDecodeHwAccels(res.preferred, supported.methods);
|
|
1031
1059
|
} catch (err) {
|
|
1032
1060
|
this.ctx.logger.warn("ffmpeg: decode-hwaccel probe failed — software decode", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -1037,13 +1065,13 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1037
1065
|
const resolver = this.ctx.kernel.hwaccel;
|
|
1038
1066
|
if (!resolver) {
|
|
1039
1067
|
this.ctx.logger.warn("reprobeHwaccel: no kernel hwaccel resolver — returning none");
|
|
1040
|
-
await this.
|
|
1068
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
1041
1069
|
return { backend: "none" };
|
|
1042
1070
|
}
|
|
1043
1071
|
try {
|
|
1044
1072
|
const res = await resolver.resolve();
|
|
1045
1073
|
const backend = res.preferred[0] ?? "none";
|
|
1046
|
-
await this.
|
|
1074
|
+
await this.updateGlobalSettings({ probedBestHwaccel: backend });
|
|
1047
1075
|
this.ctx.logger.info("reprobeHwaccel: wrote probedBestHwaccel", { meta: {
|
|
1048
1076
|
backend,
|
|
1049
1077
|
rationale: res.rationale,
|
|
@@ -1052,7 +1080,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1052
1080
|
return { backend };
|
|
1053
1081
|
} catch (err) {
|
|
1054
1082
|
this.ctx.logger.warn("reprobeHwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
1055
|
-
await this.
|
|
1083
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
1056
1084
|
return { backend: "none" };
|
|
1057
1085
|
}
|
|
1058
1086
|
}
|
|
@@ -1119,7 +1147,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1119
1147
|
};
|
|
1120
1148
|
}
|
|
1121
1149
|
wireCallbackSink(sessionId, session) {
|
|
1122
|
-
const ringBuffer = new
|
|
1150
|
+
const ringBuffer = new require_frame_ring_sink.NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1123
1151
|
this.frameBuffers.set(sessionId, ringBuffer);
|
|
1124
1152
|
return session.onFrame((frame) => {
|
|
1125
1153
|
const { format } = frame;
|
|
@@ -1137,7 +1165,7 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1137
1165
|
});
|
|
1138
1166
|
}
|
|
1139
1167
|
wireShmSink(sessionId, session) {
|
|
1140
|
-
const handleRing = new
|
|
1168
|
+
const handleRing = new require_frame_ring_sink.NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1141
1169
|
this.handleBuffers.set(sessionId, handleRing);
|
|
1142
1170
|
return session.onFrameHandle((frame) => {
|
|
1143
1171
|
handleRing.push(frame.handle);
|
|
@@ -1149,6 +1177,8 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1149
1177
|
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${sessionId}`);
|
|
1150
1178
|
const unsub = this.unsubscribers.get(sessionId);
|
|
1151
1179
|
if (unsub) unsub();
|
|
1180
|
+
this.frameBuffers.get(sessionId)?.cancel();
|
|
1181
|
+
this.handleBuffers.get(sessionId)?.cancel();
|
|
1152
1182
|
await session.destroy();
|
|
1153
1183
|
this.sessions.delete(sessionId);
|
|
1154
1184
|
this.frameBuffers.delete(sessionId);
|
|
@@ -1186,12 +1216,14 @@ var DecoderFfmpegAddon = class extends require_dist.BaseAddon {
|
|
|
1186
1216
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1187
1217
|
const ringBuffer = this.frameBuffers.get(input.sessionId);
|
|
1188
1218
|
if (!ringBuffer) return [];
|
|
1219
|
+
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
1189
1220
|
return ringBuffer.drain(input.maxCount);
|
|
1190
1221
|
}
|
|
1191
1222
|
async pullHandles(input) {
|
|
1192
1223
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1193
1224
|
const handleRing = this.handleBuffers.get(input.sessionId);
|
|
1194
1225
|
if (!handleRing) return [];
|
|
1226
|
+
if (handleRing.size === 0 && input.waitMs) await handleRing.waitForItem(input.waitMs);
|
|
1195
1227
|
return handleRing.drain(input.maxCount);
|
|
1196
1228
|
}
|
|
1197
1229
|
async updateConfig(input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as errMsg, V as BaseAddon, d as HWACCEL_OPTIONS,
|
|
2
|
-
import {
|
|
1
|
+
import { B as errMsg, V as BaseAddon, d as HWACCEL_OPTIONS, j as nodePin, s as DEFAULT_DECODER_HWACCEL_CONFIG, x as decoderCapability } from "../dist-C6_wgXqF.mjs";
|
|
2
|
+
import { a as resolveDecoderBackend, i as NotifyingRingBuffer, n as RING_BUDGET_MB, o as DEFAULT_DECODER_BACKEND, t as DecoderFrameRingSink } from "../frame-ring-sink-Cs9vby6v.mjs";
|
|
3
3
|
import { r as logBannerArgs, t as FrameDropper } from "../frame-dropper-CwkBTPGV.mjs";
|
|
4
4
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -892,6 +892,18 @@ function probeGpuScaleFilters(ffmpegPath, logger) {
|
|
|
892
892
|
//#endregion
|
|
893
893
|
//#region src/decoder-ffmpeg/addon/index.ts
|
|
894
894
|
var FRAME_BUFFER_CAPACITY = 32;
|
|
895
|
+
var DEFAULT_DECODER_FFMPEG_ADDON_CONFIG = {
|
|
896
|
+
...DEFAULT_DECODER_HWACCEL_CONFIG,
|
|
897
|
+
backend: DEFAULT_DECODER_BACKEND
|
|
898
|
+
};
|
|
899
|
+
/** The two selectable decoder backends, in default-first order. */
|
|
900
|
+
var DECODER_BACKEND_OPTIONS = [{
|
|
901
|
+
value: "ffmpeg",
|
|
902
|
+
label: "ffmpeg (subprocess — crash-isolated, default)"
|
|
903
|
+
}, {
|
|
904
|
+
value: "nodeav",
|
|
905
|
+
label: "node-av (in-process FFmpeg bindings)"
|
|
906
|
+
}];
|
|
895
907
|
var DecoderFfmpegAddon = class extends BaseAddon {
|
|
896
908
|
sessions = /* @__PURE__ */ new Map();
|
|
897
909
|
frameBuffers = /* @__PURE__ */ new Map();
|
|
@@ -922,10 +934,24 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
922
934
|
*/
|
|
923
935
|
unsupportedGpuScaleFilters = /* @__PURE__ */ new Set();
|
|
924
936
|
constructor() {
|
|
925
|
-
super(
|
|
937
|
+
super(DEFAULT_DECODER_FFMPEG_ADDON_CONFIG);
|
|
926
938
|
}
|
|
927
939
|
globalSettingsSchema() {
|
|
928
940
|
return this.schema({ sections: [{
|
|
941
|
+
id: "backend",
|
|
942
|
+
title: "Decoder backend",
|
|
943
|
+
tab: "decoder",
|
|
944
|
+
description: "Which decoder addon provides the decoder capability on this node — per-node selection, stored centrally on the hub. Selection is decided once at addon init (registration-time, race-free), so a change applies at the next restart of the decoder addons on the node.",
|
|
945
|
+
fields: [this.field({
|
|
946
|
+
type: "select",
|
|
947
|
+
key: "backend",
|
|
948
|
+
label: "Backend",
|
|
949
|
+
options: [...DECODER_BACKEND_OPTIONS],
|
|
950
|
+
default: DEFAULT_DECODER_BACKEND,
|
|
951
|
+
immediate: true,
|
|
952
|
+
perNode: true
|
|
953
|
+
})]
|
|
954
|
+
}, {
|
|
929
955
|
id: "hwaccel",
|
|
930
956
|
title: "Hardware acceleration",
|
|
931
957
|
tab: "decoder",
|
|
@@ -944,6 +970,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
944
970
|
description: "Auto-detected best decoder backend on this host. Click the refresh icon to re-run the probe.",
|
|
945
971
|
readonlyField: true,
|
|
946
972
|
default: "",
|
|
973
|
+
perNode: true,
|
|
947
974
|
actions: [{
|
|
948
975
|
action: "reprobe-hwaccel",
|
|
949
976
|
icon: "refresh-cw",
|
|
@@ -953,7 +980,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
953
980
|
}] });
|
|
954
981
|
}
|
|
955
982
|
async onInitialize() {
|
|
956
|
-
const backend = await resolveDecoderBackend(this.ctx.
|
|
983
|
+
const backend = await resolveDecoderBackend(this.ctx.api, this.resolveLocalNodeId(), this.ctx.logger);
|
|
957
984
|
if (backend !== "ffmpeg") {
|
|
958
985
|
this.ctx.logger.info("ffmpeg decoder: this node selects a different decoder backend — standing down (no decoder provider registered)", { meta: { selectedBackend: backend } });
|
|
959
986
|
return [];
|
|
@@ -1021,8 +1048,9 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1021
1048
|
if (pref === "none") return [];
|
|
1022
1049
|
if (pref !== "auto") return [pref];
|
|
1023
1050
|
try {
|
|
1024
|
-
const
|
|
1025
|
-
const
|
|
1051
|
+
const pin = nodePin(this.resolveLocalNodeId());
|
|
1052
|
+
const res = await this.ctx.api.platformProbe.resolveHwAccel.query({ prefer: null }, pin);
|
|
1053
|
+
const supported = await this.ctx.api.platformProbe.getHardwareDecodeAccels.query(void 0, pin);
|
|
1026
1054
|
return rankDecodeHwAccels(res.preferred, supported.methods);
|
|
1027
1055
|
} catch (err) {
|
|
1028
1056
|
this.ctx.logger.warn("ffmpeg: decode-hwaccel probe failed — software decode", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -1033,13 +1061,13 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1033
1061
|
const resolver = this.ctx.kernel.hwaccel;
|
|
1034
1062
|
if (!resolver) {
|
|
1035
1063
|
this.ctx.logger.warn("reprobeHwaccel: no kernel hwaccel resolver — returning none");
|
|
1036
|
-
await this.
|
|
1064
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
1037
1065
|
return { backend: "none" };
|
|
1038
1066
|
}
|
|
1039
1067
|
try {
|
|
1040
1068
|
const res = await resolver.resolve();
|
|
1041
1069
|
const backend = res.preferred[0] ?? "none";
|
|
1042
|
-
await this.
|
|
1070
|
+
await this.updateGlobalSettings({ probedBestHwaccel: backend });
|
|
1043
1071
|
this.ctx.logger.info("reprobeHwaccel: wrote probedBestHwaccel", { meta: {
|
|
1044
1072
|
backend,
|
|
1045
1073
|
rationale: res.rationale,
|
|
@@ -1048,7 +1076,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1048
1076
|
return { backend };
|
|
1049
1077
|
} catch (err) {
|
|
1050
1078
|
this.ctx.logger.warn("reprobeHwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
1051
|
-
await this.
|
|
1079
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
1052
1080
|
return { backend: "none" };
|
|
1053
1081
|
}
|
|
1054
1082
|
}
|
|
@@ -1115,7 +1143,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1115
1143
|
};
|
|
1116
1144
|
}
|
|
1117
1145
|
wireCallbackSink(sessionId, session) {
|
|
1118
|
-
const ringBuffer = new
|
|
1146
|
+
const ringBuffer = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1119
1147
|
this.frameBuffers.set(sessionId, ringBuffer);
|
|
1120
1148
|
return session.onFrame((frame) => {
|
|
1121
1149
|
const { format } = frame;
|
|
@@ -1133,7 +1161,7 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1133
1161
|
});
|
|
1134
1162
|
}
|
|
1135
1163
|
wireShmSink(sessionId, session) {
|
|
1136
|
-
const handleRing = new
|
|
1164
|
+
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1137
1165
|
this.handleBuffers.set(sessionId, handleRing);
|
|
1138
1166
|
return session.onFrameHandle((frame) => {
|
|
1139
1167
|
handleRing.push(frame.handle);
|
|
@@ -1145,6 +1173,8 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1145
1173
|
if (!session) throw new Error(`decoder-ffmpeg: unknown sessionId ${sessionId}`);
|
|
1146
1174
|
const unsub = this.unsubscribers.get(sessionId);
|
|
1147
1175
|
if (unsub) unsub();
|
|
1176
|
+
this.frameBuffers.get(sessionId)?.cancel();
|
|
1177
|
+
this.handleBuffers.get(sessionId)?.cancel();
|
|
1148
1178
|
await session.destroy();
|
|
1149
1179
|
this.sessions.delete(sessionId);
|
|
1150
1180
|
this.frameBuffers.delete(sessionId);
|
|
@@ -1182,12 +1212,14 @@ var DecoderFfmpegAddon = class extends BaseAddon {
|
|
|
1182
1212
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1183
1213
|
const ringBuffer = this.frameBuffers.get(input.sessionId);
|
|
1184
1214
|
if (!ringBuffer) return [];
|
|
1215
|
+
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
1185
1216
|
return ringBuffer.drain(input.maxCount);
|
|
1186
1217
|
}
|
|
1187
1218
|
async pullHandles(input) {
|
|
1188
1219
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-ffmpeg: unknown sessionId ${input.sessionId}`);
|
|
1189
1220
|
const handleRing = this.handleBuffers.get(input.sessionId);
|
|
1190
1221
|
if (!handleRing) return [];
|
|
1222
|
+
if (handleRing.size === 0 && input.waitMs) await handleRing.waitForItem(input.waitMs);
|
|
1191
1223
|
return handleRing.drain(input.maxCount);
|
|
1192
1224
|
}
|
|
1193
1225
|
async updateConfig(input) {
|
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_frame_ring_sink = require("../frame-ring-sink-
|
|
5
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
6
|
+
const require_frame_ring_sink = require("../frame-ring-sink-9J0wCdLF.js");
|
|
7
7
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
//#region src/decoder-nodeav/scaler-geometry.ts
|
|
@@ -927,6 +927,7 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
927
927
|
description: "Auto-detected best decoder backend on this host. Click the refresh icon to re-run the probe.",
|
|
928
928
|
readonlyField: true,
|
|
929
929
|
default: "",
|
|
930
|
+
perNode: true,
|
|
930
931
|
actions: [{
|
|
931
932
|
action: "reprobe-hwaccel",
|
|
932
933
|
icon: "refresh-cw",
|
|
@@ -936,7 +937,7 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
936
937
|
}] });
|
|
937
938
|
}
|
|
938
939
|
async onInitialize() {
|
|
939
|
-
const backend = await require_frame_ring_sink.resolveDecoderBackend(this.ctx.
|
|
940
|
+
const backend = await require_frame_ring_sink.resolveDecoderBackend(this.ctx.api, this.resolveLocalNodeId(), this.ctx.logger);
|
|
940
941
|
if (backend !== "nodeav") {
|
|
941
942
|
this.ctx.logger.info("node-av decoder: this node selects a different decoder backend — standing down (no decoder provider registered)", { meta: { selectedBackend: backend } });
|
|
942
943
|
return [];
|
|
@@ -970,13 +971,13 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
970
971
|
const resolver = this.ctx.kernel.hwaccel;
|
|
971
972
|
if (!resolver) {
|
|
972
973
|
this.ctx.logger.warn("reprobeHwaccel: no kernel hwaccel resolver — returning none");
|
|
973
|
-
await this.
|
|
974
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
974
975
|
return { backend: "none" };
|
|
975
976
|
}
|
|
976
977
|
try {
|
|
977
978
|
const res = await resolver.resolve();
|
|
978
979
|
const backend = res.preferred[0] ?? "none";
|
|
979
|
-
await this.
|
|
980
|
+
await this.updateGlobalSettings({ probedBestHwaccel: backend });
|
|
980
981
|
this.ctx.logger.info("reprobeHwaccel: wrote probedBestHwaccel", { meta: {
|
|
981
982
|
backend,
|
|
982
983
|
rationale: res.rationale,
|
|
@@ -985,7 +986,7 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
985
986
|
return { backend };
|
|
986
987
|
} catch (err) {
|
|
987
988
|
this.ctx.logger.warn("reprobeHwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
988
|
-
await this.
|
|
989
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
989
990
|
return { backend: "none" };
|
|
990
991
|
}
|
|
991
992
|
}
|
|
@@ -1052,7 +1053,7 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
1052
1053
|
* ring buffer drained by `pullFrames`.
|
|
1053
1054
|
*/
|
|
1054
1055
|
wireCallbackSink(sessionId, session) {
|
|
1055
|
-
const ringBuffer = new
|
|
1056
|
+
const ringBuffer = new require_frame_ring_sink.NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1056
1057
|
this.frameBuffers.set(sessionId, ringBuffer);
|
|
1057
1058
|
return session.onFrame((frame) => {
|
|
1058
1059
|
const { format } = frame;
|
|
@@ -1075,7 +1076,7 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
1075
1076
|
* — the broker opens the named segment and reads the pixels zero-copy.
|
|
1076
1077
|
*/
|
|
1077
1078
|
wireShmSink(sessionId, session) {
|
|
1078
|
-
const handleRing = new
|
|
1079
|
+
const handleRing = new require_frame_ring_sink.NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1079
1080
|
this.handleBuffers.set(sessionId, handleRing);
|
|
1080
1081
|
return session.onFrameHandle((frame) => {
|
|
1081
1082
|
handleRing.push(frame.handle);
|
|
@@ -1087,6 +1088,8 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
1087
1088
|
if (!session) throw new Error(`decoder-nodeav: unknown sessionId ${sessionId}`);
|
|
1088
1089
|
const unsub = this.unsubscribers.get(sessionId);
|
|
1089
1090
|
if (unsub) unsub();
|
|
1091
|
+
this.frameBuffers.get(sessionId)?.cancel();
|
|
1092
|
+
this.handleBuffers.get(sessionId)?.cancel();
|
|
1090
1093
|
await session.destroy();
|
|
1091
1094
|
this.sessions.delete(sessionId);
|
|
1092
1095
|
this.frameBuffers.delete(sessionId);
|
|
@@ -1123,12 +1126,14 @@ var DecoderNodeAvAddon = class extends require_dist.BaseAddon {
|
|
|
1123
1126
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-nodeav: unknown sessionId ${input.sessionId}`);
|
|
1124
1127
|
const ringBuffer = this.frameBuffers.get(input.sessionId);
|
|
1125
1128
|
if (!ringBuffer) return [];
|
|
1129
|
+
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
1126
1130
|
return ringBuffer.drain(input.maxCount);
|
|
1127
1131
|
}
|
|
1128
1132
|
async pullHandles(input) {
|
|
1129
1133
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-nodeav: unknown sessionId ${input.sessionId}`);
|
|
1130
1134
|
const handleRing = this.handleBuffers.get(input.sessionId);
|
|
1131
1135
|
if (!handleRing) return [];
|
|
1136
|
+
if (handleRing.size === 0 && input.waitMs) await handleRing.waitForItem(input.waitMs);
|
|
1132
1137
|
return handleRing.drain(input.maxCount);
|
|
1133
1138
|
}
|
|
1134
1139
|
async updateConfig(input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as errMsg, V as BaseAddon, d as HWACCEL_OPTIONS,
|
|
2
|
-
import {
|
|
1
|
+
import { B as errMsg, V as BaseAddon, d as HWACCEL_OPTIONS, s as DEFAULT_DECODER_HWACCEL_CONFIG, x as decoderCapability } from "../dist-C6_wgXqF.mjs";
|
|
2
|
+
import { a as resolveDecoderBackend, i as NotifyingRingBuffer, n as RING_BUDGET_MB, r as makeSegmentName, t as DecoderFrameRingSink } from "../frame-ring-sink-Cs9vby6v.mjs";
|
|
3
3
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
//#region src/decoder-nodeav/scaler-geometry.ts
|
|
@@ -923,6 +923,7 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
923
923
|
description: "Auto-detected best decoder backend on this host. Click the refresh icon to re-run the probe.",
|
|
924
924
|
readonlyField: true,
|
|
925
925
|
default: "",
|
|
926
|
+
perNode: true,
|
|
926
927
|
actions: [{
|
|
927
928
|
action: "reprobe-hwaccel",
|
|
928
929
|
icon: "refresh-cw",
|
|
@@ -932,7 +933,7 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
932
933
|
}] });
|
|
933
934
|
}
|
|
934
935
|
async onInitialize() {
|
|
935
|
-
const backend = await resolveDecoderBackend(this.ctx.
|
|
936
|
+
const backend = await resolveDecoderBackend(this.ctx.api, this.resolveLocalNodeId(), this.ctx.logger);
|
|
936
937
|
if (backend !== "nodeav") {
|
|
937
938
|
this.ctx.logger.info("node-av decoder: this node selects a different decoder backend — standing down (no decoder provider registered)", { meta: { selectedBackend: backend } });
|
|
938
939
|
return [];
|
|
@@ -966,13 +967,13 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
966
967
|
const resolver = this.ctx.kernel.hwaccel;
|
|
967
968
|
if (!resolver) {
|
|
968
969
|
this.ctx.logger.warn("reprobeHwaccel: no kernel hwaccel resolver — returning none");
|
|
969
|
-
await this.
|
|
970
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
970
971
|
return { backend: "none" };
|
|
971
972
|
}
|
|
972
973
|
try {
|
|
973
974
|
const res = await resolver.resolve();
|
|
974
975
|
const backend = res.preferred[0] ?? "none";
|
|
975
|
-
await this.
|
|
976
|
+
await this.updateGlobalSettings({ probedBestHwaccel: backend });
|
|
976
977
|
this.ctx.logger.info("reprobeHwaccel: wrote probedBestHwaccel", { meta: {
|
|
977
978
|
backend,
|
|
978
979
|
rationale: res.rationale,
|
|
@@ -981,7 +982,7 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
981
982
|
return { backend };
|
|
982
983
|
} catch (err) {
|
|
983
984
|
this.ctx.logger.warn("reprobeHwaccel failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
984
|
-
await this.
|
|
985
|
+
await this.updateGlobalSettings({ probedBestHwaccel: "none" });
|
|
985
986
|
return { backend: "none" };
|
|
986
987
|
}
|
|
987
988
|
}
|
|
@@ -1048,7 +1049,7 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
1048
1049
|
* ring buffer drained by `pullFrames`.
|
|
1049
1050
|
*/
|
|
1050
1051
|
wireCallbackSink(sessionId, session) {
|
|
1051
|
-
const ringBuffer = new
|
|
1052
|
+
const ringBuffer = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1052
1053
|
this.frameBuffers.set(sessionId, ringBuffer);
|
|
1053
1054
|
return session.onFrame((frame) => {
|
|
1054
1055
|
const { format } = frame;
|
|
@@ -1071,7 +1072,7 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
1071
1072
|
* — the broker opens the named segment and reads the pixels zero-copy.
|
|
1072
1073
|
*/
|
|
1073
1074
|
wireShmSink(sessionId, session) {
|
|
1074
|
-
const handleRing = new
|
|
1075
|
+
const handleRing = new NotifyingRingBuffer(FRAME_BUFFER_CAPACITY);
|
|
1075
1076
|
this.handleBuffers.set(sessionId, handleRing);
|
|
1076
1077
|
return session.onFrameHandle((frame) => {
|
|
1077
1078
|
handleRing.push(frame.handle);
|
|
@@ -1083,6 +1084,8 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
1083
1084
|
if (!session) throw new Error(`decoder-nodeav: unknown sessionId ${sessionId}`);
|
|
1084
1085
|
const unsub = this.unsubscribers.get(sessionId);
|
|
1085
1086
|
if (unsub) unsub();
|
|
1087
|
+
this.frameBuffers.get(sessionId)?.cancel();
|
|
1088
|
+
this.handleBuffers.get(sessionId)?.cancel();
|
|
1086
1089
|
await session.destroy();
|
|
1087
1090
|
this.sessions.delete(sessionId);
|
|
1088
1091
|
this.frameBuffers.delete(sessionId);
|
|
@@ -1119,12 +1122,14 @@ var DecoderNodeAvAddon = class extends BaseAddon {
|
|
|
1119
1122
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-nodeav: unknown sessionId ${input.sessionId}`);
|
|
1120
1123
|
const ringBuffer = this.frameBuffers.get(input.sessionId);
|
|
1121
1124
|
if (!ringBuffer) return [];
|
|
1125
|
+
if (ringBuffer.size === 0 && input.waitMs) await ringBuffer.waitForItem(input.waitMs);
|
|
1122
1126
|
return ringBuffer.drain(input.maxCount);
|
|
1123
1127
|
}
|
|
1124
1128
|
async pullHandles(input) {
|
|
1125
1129
|
if (!this.sessions.has(input.sessionId)) throw new Error(`decoder-nodeav: unknown sessionId ${input.sessionId}`);
|
|
1126
1130
|
const handleRing = this.handleBuffers.get(input.sessionId);
|
|
1127
1131
|
if (!handleRing) return [];
|
|
1132
|
+
if (handleRing.size === 0 && input.waitMs) await handleRing.waitForItem(input.waitMs);
|
|
1128
1133
|
return handleRing.drain(input.maxCount);
|
|
1129
1134
|
}
|
|
1130
1135
|
async updateConfig(input) {
|