@camstack/addon-pipeline 1.2.326 → 1.2.328
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/THIRD_PARTY_MODELS.md +49 -43
- package/dist/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-WnGDQAQh.js → default-detection-model-1SadEKsW.js} +120 -61
- package/dist/{default-detection-model-Bu4So71h.mjs → default-detection-model-CMeVTcPZ.mjs} +120 -61
- package/dist/detection-pipeline/index.js +49 -9
- package/dist/detection-pipeline/index.mjs +49 -9
- package/dist/{dist-BuU8Y4o1.mjs → dist-Bpub2VcJ.mjs} +584 -396
- package/dist/{dist-BTBbIGSC.js → dist-wJO7mXeO.js} +631 -395
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-UK2v2ZSw.js → node-BCrqaejt.js} +1 -1
- package/dist/{node-inZFYgkm.mjs → node-BjCSgD4M.mjs} +1 -1
- package/dist/pipeline-runner/index.js +253 -87
- package/dist/pipeline-runner/index.mjs +253 -88
- package/dist/{process-memory-DbSOL63T.mjs → process-memory-BaKQ8u2G.mjs} +1 -1
- package/dist/{process-memory-BfDlwJc4.js → process-memory-iUJuTpXs.js} +1 -1
- package/dist/recorder/index.js +9 -8
- package/dist/recorder/index.mjs +9 -8
- package/dist/{segment-demux-js-CMj020zz.mjs → segment-demux-js-C5ez9LYG.mjs} +1 -1
- package/dist/{segment-demux-js-C1GXgxZ0.js → segment-demux-js-Opx6Hv9G.js} +1 -1
- package/dist/session-decode/decode-worker-main.js +13 -21
- package/dist/session-decode/decode-worker-main.mjs +13 -21
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-qHD9Dl1z.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B94LgsV0.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BPWYf4_O.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-BgWrKrQn.mjs → hostInit-xOx7AvgW.mjs} +3 -3
- package/dist/stream-broker/index.js +6 -5
- package/dist/stream-broker/index.mjs +6 -5
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/python/inference_pool.py +38 -121
- package/python/postprocessors/scrfd.py +2 -0
- package/python/postprocessors/test_bird_edgetpu.py +124 -0
- package/python/postprocessors/testdata/bird_classifier_int8_outputs.json +1 -0
- package/python/test_inference_pool_static_batch.py +2 -4
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-NkeMtp-v.mjs +0 -26
|
@@ -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-wJO7mXeO.js");
|
|
6
6
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { An as hydrateSchema, It as motionDetectionCapability, Sn as BaseAddon, pt as evaluateZoneRules, yn as DeviceType } from "../dist-Bpub2VcJ.mjs";
|
|
2
2
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-emK7D4bc.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-wJO7mXeO.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _n as isSoftwareDecode, bn as errMsg, mn as buildFfmpegArgs, pn as Fmp4BoxSplitter } from "./dist-Bpub2VcJ.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-wJO7mXeO.js");
|
|
7
|
+
const require_node = require("../node-BCrqaejt.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-1SadEKsW.js");
|
|
9
9
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
10
10
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
11
11
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
@@ -903,10 +903,12 @@ function classifyNativeCropLeaseAge(ageMs, maxAgeMs = NATIVE_CROP_LEASE_MAX_AGE_
|
|
|
903
903
|
}
|
|
904
904
|
//#endregion
|
|
905
905
|
//#region src/session-decode/native-crop-registry.ts
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
906
|
+
/**
|
|
907
|
+
* Default max entries retained across all active decode sessions on a node
|
|
908
|
+
* (64 → 256 → 4096; see the class doc for the two windows the cap has been
|
|
909
|
+
* sized to).
|
|
910
|
+
*/
|
|
911
|
+
var DEFAULT_NATIVE_CROP_REGISTRY_CAP = 4096;
|
|
910
912
|
/** Invoke a requester's best-effort `release()`, swallowing any transport error. */
|
|
911
913
|
function safeRelease(request) {
|
|
912
914
|
try {
|
|
@@ -1173,20 +1175,6 @@ function createRetainedContentFallback(registry, ref) {
|
|
|
1173
1175
|
}
|
|
1174
1176
|
} };
|
|
1175
1177
|
}
|
|
1176
|
-
//#endregion
|
|
1177
|
-
//#region src/session-decode/parked-frame-store.ts
|
|
1178
|
-
/** Per-camera parcel bound. 24 live tracks × two kinds on one camera is a
|
|
1179
|
-
* crowd scene; past that the camera's oldest parcel leaves first. Read ONCE
|
|
1180
|
-
* from `CAMSTACK_PARKED_FRAMES_PER_DEVICE`. */
|
|
1181
|
-
var PARKED_FRAMES_PER_DEVICE_MAX = (() => {
|
|
1182
|
-
const raw = Number(process.env["CAMSTACK_PARKED_FRAMES_PER_DEVICE"]);
|
|
1183
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 48;
|
|
1184
|
-
})();
|
|
1185
|
-
/** {@link PARKED_FRAME_BUDGET_MB} in bytes. */
|
|
1186
|
-
var PARKED_FRAME_BUDGET_BYTES = (() => {
|
|
1187
|
-
const raw = Number(process.env["CAMSTACK_PARKED_FRAME_MB"]);
|
|
1188
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 64;
|
|
1189
|
-
})() * 1024 * 1024;
|
|
1190
1178
|
function keyOf$1(deviceId, trackId, kind) {
|
|
1191
1179
|
return `${deviceId}|${trackId}|${kind}`;
|
|
1192
1180
|
}
|
|
@@ -1203,8 +1191,8 @@ var ParkedFrameStore = class {
|
|
|
1203
1191
|
perDeviceKeys = /* @__PURE__ */ new Map();
|
|
1204
1192
|
totalBytes = 0;
|
|
1205
1193
|
constructor(options = {}) {
|
|
1206
|
-
this.budgetBytes = options.budgetBytes ??
|
|
1207
|
-
this.perDeviceMax = options.perDeviceMax ??
|
|
1194
|
+
this.budgetBytes = options.budgetBytes ?? 67108864;
|
|
1195
|
+
this.perDeviceMax = options.perDeviceMax ?? 48;
|
|
1208
1196
|
this.maxAgeMs = options.maxAgeMs ?? 6e5;
|
|
1209
1197
|
this.now = options.now ?? Date.now;
|
|
1210
1198
|
this.onEvict = options.onEvict;
|
|
@@ -1389,47 +1377,6 @@ var PartitionedParkedFrameStore = class {
|
|
|
1389
1377
|
this.faceSource.clear();
|
|
1390
1378
|
}
|
|
1391
1379
|
};
|
|
1392
|
-
/** {@link RETAINED_FRAME_BUDGET_MB} in bytes. */
|
|
1393
|
-
var RETAINED_FRAME_BUDGET_BYTES = (() => {
|
|
1394
|
-
const raw = Number(process.env["CAMSTACK_SESSION_RETAIN_MB"]);
|
|
1395
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 160;
|
|
1396
|
-
})() * 1024 * 1024;
|
|
1397
|
-
/**
|
|
1398
|
-
* How many of each device's MOST RECENT frames are protected from a busy
|
|
1399
|
-
* neighbour's budget eviction. Sized to cover the late two-plane / cross-process
|
|
1400
|
-
* enrichment horizon (~150-500 ms) at the REAL delivered rate: the store writes
|
|
1401
|
-
* every DELIVERED rgb frame, and a high-fps camera delivers ~25 fps (observed on
|
|
1402
|
-
* the 4K outdoor cams), so the old floor of 6 protected only ~240 ms there and
|
|
1403
|
-
* its late captures hit "frame recycled before resolve" (2026-07-22 triage —
|
|
1404
|
-
* missing thumbnails/firstFrames concentrated on exactly those cameras).
|
|
1405
|
-
* 16 frames ≈ 0.64 s @25 fps (1.6 s @10 fps). Only ACTIVE cameras hold frames,
|
|
1406
|
-
* so the worst-case protected footprint stays well inside the byte budget for
|
|
1407
|
-
* realistic concurrency (4 active cams ≈ 44 MB of 160); the hard ceiling always
|
|
1408
|
-
* overrides the floor regardless. Read ONCE from `CAMSTACK_SESSION_RETAIN_FLOOR`;
|
|
1409
|
-
* `0` disables the floor (pure global FIFO — the pre-2026-07-19 behaviour).
|
|
1410
|
-
*/
|
|
1411
|
-
var RETAINED_FRAME_PER_DEVICE_FLOOR = (() => {
|
|
1412
|
-
const raw = Number(process.env["CAMSTACK_SESSION_RETAIN_FLOOR"]);
|
|
1413
|
-
return Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 16;
|
|
1414
|
-
})();
|
|
1415
|
-
/**
|
|
1416
|
-
* TIME-based per-device protection (2026-07-23 cold-window fix): a frame
|
|
1417
|
-
* YOUNGER than this survives budget eviction regardless of how many frames
|
|
1418
|
-
* its device holds. The COUNT floor above protects a fixed number of frames —
|
|
1419
|
-
* but the wall-clock window that buys depends on the delivered fps (16 frames
|
|
1420
|
-
* = 1.6 s @10 fps but only 0.64 s @25 fps, and the 4K street cams deliver
|
|
1421
|
-
* ~25). The late-capture horizon is TIME (session attach + enrichment +
|
|
1422
|
-
* round-trip ≈ 0.5–1.5 s, worst on cold on-motion dials), so the guarantee
|
|
1423
|
-
* must be time-denominated: 12/13 of the zero-media/missing-firstFrame tracks
|
|
1424
|
-
* in the 2026-07-23 audit were ≤6 s cold-session subjects whose handles were
|
|
1425
|
-
* recycled before their first captures ran. Bounded by the hard byte ceiling
|
|
1426
|
-
* (pass 2) exactly like the count floor — worst case ~26 MB per ACTIVE camera
|
|
1427
|
-
* @25 fps. `CAMSTACK_SESSION_RETAIN_FLOOR_MS`; `0` disables (count floor only).
|
|
1428
|
-
*/
|
|
1429
|
-
var RETAINED_FRAME_TIME_FLOOR_MS = (() => {
|
|
1430
|
-
const raw = Number(process.env["CAMSTACK_SESSION_RETAIN_FLOOR_MS"]);
|
|
1431
|
-
return Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 3e3;
|
|
1432
|
-
})();
|
|
1433
1380
|
/** Opaque RAM-key prefix stamped into a retained frame's `FrameHandle.shmId`. */
|
|
1434
1381
|
var RETAINED_FRAME_KEY_PREFIX = "ram:";
|
|
1435
1382
|
/** A retention frame is uniquely identified by its key + slot + committed seq. */
|
|
@@ -1468,9 +1415,9 @@ var RetainedFrameStore = class RetainedFrameStore {
|
|
|
1468
1415
|
static SLOT_SPREAD = 1024;
|
|
1469
1416
|
constructor(options) {
|
|
1470
1417
|
this.nodeId = options.nodeId;
|
|
1471
|
-
this.budgetBytes = options.budgetBytes ??
|
|
1472
|
-
this.perDeviceFloor = options.perDeviceFloor ??
|
|
1473
|
-
this.timeFloorMs = options.timeFloorMs ??
|
|
1418
|
+
this.budgetBytes = options.budgetBytes ?? 167772160;
|
|
1419
|
+
this.perDeviceFloor = options.perDeviceFloor ?? 16;
|
|
1420
|
+
this.timeFloorMs = options.timeFloorMs ?? 3e3;
|
|
1474
1421
|
this.now = options.now ?? Date.now;
|
|
1475
1422
|
this.onEvict = options.onEvict;
|
|
1476
1423
|
}
|
|
@@ -2029,6 +1976,154 @@ function onboardRootDetections(slot, frame, gate) {
|
|
|
2029
1976
|
};
|
|
2030
1977
|
}
|
|
2031
1978
|
//#endregion
|
|
1979
|
+
//#region src/pipeline-runner/label-link.ts
|
|
1980
|
+
/**
|
|
1981
|
+
* The runner's half of the detector ↔ step label link (D715).
|
|
1982
|
+
*
|
|
1983
|
+
* The runner holds the camera's WHOLE step tree (the attach config), so it is
|
|
1984
|
+
* the one place that can answer "who writes this effect?" for a camera: the
|
|
1985
|
+
* root model's declaration (`fillsClassifierEffects`) against the filled
|
|
1986
|
+
* step's own `labelLink` policy and class list (`resolveLabelLink`). Three
|
|
1987
|
+
* consumers read that one answer:
|
|
1988
|
+
*
|
|
1989
|
+
* - the frame result — a linked label the executor wrote at score 0 is raised
|
|
1990
|
+
* to the detection's score when the detector is authoritative;
|
|
1991
|
+
* - the `detailSteps` announce — a skipped classifier is announced with
|
|
1992
|
+
* `filledBy`, so post-analysis does not dispatch it for a subject the
|
|
1993
|
+
* detector already named;
|
|
1994
|
+
* - `package-detection` — when the root fills it, the package root is not
|
|
1995
|
+
* dispatched at all and the root's own `package` boxes ARE the package
|
|
1996
|
+
* result (the operator's package threshold moves onto the root's gate, and
|
|
1997
|
+
* the frame says it looked).
|
|
1998
|
+
*
|
|
1999
|
+
* Pure: the caller passes the model resolver; no I/O, no clock.
|
|
2000
|
+
*/
|
|
2001
|
+
/** The root step the fills are read from — the always-on camera root. */
|
|
2002
|
+
var LABEL_LINK_ROOT_STEP_ID = "object-detection";
|
|
2003
|
+
var PACKAGE_STEP_ID = "package-detection";
|
|
2004
|
+
var PACKAGE_THRESHOLD_KEY = "minConfidencePackage";
|
|
2005
|
+
var EMPTY_LABEL_LINK_PLAN = { decisions: /* @__PURE__ */ new Map() };
|
|
2006
|
+
function findStep(steps, addonId) {
|
|
2007
|
+
for (const step of steps) {
|
|
2008
|
+
if (step.addonId === addonId) return step;
|
|
2009
|
+
const nested = step.children !== void 0 ? findStep(step.children, addonId) : void 0;
|
|
2010
|
+
if (nested !== void 0) return nested;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Plan a camera's label links from its configured tree. A root with no pinned
|
|
2015
|
+
* model runs the static default — a COCO model, which fills nothing.
|
|
2016
|
+
*/
|
|
2017
|
+
function planLabelLinks(steps, resolveModel) {
|
|
2018
|
+
if (steps === void 0) return EMPTY_LABEL_LINK_PLAN;
|
|
2019
|
+
const root = steps.find((s) => s.addonId === "object-detection" && s.enabled !== false);
|
|
2020
|
+
if (root?.modelId === void 0) return EMPTY_LABEL_LINK_PLAN;
|
|
2021
|
+
const fills = resolveModel("object-detection", root.modelId)?.fillsClassifierEffects ?? [];
|
|
2022
|
+
if (fills.length === 0) return {
|
|
2023
|
+
rootModelId: root.modelId,
|
|
2024
|
+
decisions: /* @__PURE__ */ new Map()
|
|
2025
|
+
};
|
|
2026
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
2027
|
+
for (const fill of fills) {
|
|
2028
|
+
const step = findStep(steps, fill.stepId);
|
|
2029
|
+
decisions.set(fill.stepId, require_dist.resolveLabelLink(fill, step === void 0 ? void 0 : {
|
|
2030
|
+
enabled: step.enabled !== false,
|
|
2031
|
+
...step.settings !== void 0 ? { settings: step.settings } : {}
|
|
2032
|
+
}));
|
|
2033
|
+
}
|
|
2034
|
+
return {
|
|
2035
|
+
rootModelId: root.modelId,
|
|
2036
|
+
decisions
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
/** Steps this plan says must not be dispatched, with the model that fills them. */
|
|
2040
|
+
function filledStepIds(plan) {
|
|
2041
|
+
const out = /* @__PURE__ */ new Map();
|
|
2042
|
+
if (plan.rootModelId === void 0) return out;
|
|
2043
|
+
for (const [stepId, decision] of plan.decisions) if (decision.skip) out.set(stepId, plan.rootModelId);
|
|
2044
|
+
return out;
|
|
2045
|
+
}
|
|
2046
|
+
function raiseLinkedLabels(detection, plan) {
|
|
2047
|
+
let changed = false;
|
|
2048
|
+
const labels = detection.labels.map((label) => {
|
|
2049
|
+
const stepId = require_dist.linkedStepOf(label.stepId);
|
|
2050
|
+
if (stepId === void 0 || plan.decisions.get(stepId)?.authoritative !== true) return label;
|
|
2051
|
+
if (label.score === detection.score) return label;
|
|
2052
|
+
changed = true;
|
|
2053
|
+
return {
|
|
2054
|
+
...label,
|
|
2055
|
+
score: detection.score
|
|
2056
|
+
};
|
|
2057
|
+
});
|
|
2058
|
+
return changed ? labels : void 0;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Raise each linked label to its detection's score where the detector is the
|
|
2062
|
+
* authoritative writer of that effect. Every other linked label stays the
|
|
2063
|
+
* score-0 fallback the executor wrote. Never mutates; returns the input when
|
|
2064
|
+
* nothing changes.
|
|
2065
|
+
*/
|
|
2066
|
+
function applyLabelLinkAuthority(result, plan) {
|
|
2067
|
+
if (plan.decisions.size === 0) return result;
|
|
2068
|
+
let changed = false;
|
|
2069
|
+
const detections = result.detections.map((d) => {
|
|
2070
|
+
const labels = raiseLinkedLabels(d, plan);
|
|
2071
|
+
if (labels === void 0) return d;
|
|
2072
|
+
changed = true;
|
|
2073
|
+
return {
|
|
2074
|
+
...d,
|
|
2075
|
+
labels
|
|
2076
|
+
};
|
|
2077
|
+
});
|
|
2078
|
+
return changed ? {
|
|
2079
|
+
...result,
|
|
2080
|
+
detections
|
|
2081
|
+
} : result;
|
|
2082
|
+
}
|
|
2083
|
+
/** The root step's schema default for its class allow-list. */
|
|
2084
|
+
function rootDefaultMacroClasses() {
|
|
2085
|
+
const field = require_default_detection_model.getStep(LABEL_LINK_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
|
|
2086
|
+
const value = field !== void 0 && "default" in field ? field.default : void 0;
|
|
2087
|
+
return Array.isArray(value) ? value : [];
|
|
2088
|
+
}
|
|
2089
|
+
function withPackageAdmitted(root, packageStep) {
|
|
2090
|
+
const settings = root.settings ?? {};
|
|
2091
|
+
const stored = settings["enabledMacroClasses"];
|
|
2092
|
+
const enabled = Array.isArray(stored) ? stored : rootDefaultMacroClasses();
|
|
2093
|
+
const admitted = enabled.length > 0 && !enabled.includes("package") ? { enabledMacroClasses: [...enabled, "package"] } : {};
|
|
2094
|
+
const threshold = packageStep.settings?.[PACKAGE_THRESHOLD_KEY];
|
|
2095
|
+
const gate = typeof threshold === "number" ? { [PACKAGE_THRESHOLD_KEY]: threshold } : {};
|
|
2096
|
+
return {
|
|
2097
|
+
...root,
|
|
2098
|
+
settings: {
|
|
2099
|
+
...settings,
|
|
2100
|
+
...admitted,
|
|
2101
|
+
...gate
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* When the root fills `package-detection` and that step's policy lets it be
|
|
2107
|
+
* skipped, drop the package root from the dispatch and let the camera root
|
|
2108
|
+
* report packages: its gate admits `package` at the package step's threshold.
|
|
2109
|
+
* Otherwise the tree is returned untouched.
|
|
2110
|
+
*/
|
|
2111
|
+
function applyPackageFill(steps, plan) {
|
|
2112
|
+
if (plan.decisions.get(PACKAGE_STEP_ID)?.skip !== true) return {
|
|
2113
|
+
steps,
|
|
2114
|
+
filledByRoot: false
|
|
2115
|
+
};
|
|
2116
|
+
const packageStep = steps.find((s) => s.addonId === PACKAGE_STEP_ID);
|
|
2117
|
+
if (packageStep === void 0) return {
|
|
2118
|
+
steps,
|
|
2119
|
+
filledByRoot: false
|
|
2120
|
+
};
|
|
2121
|
+
return {
|
|
2122
|
+
steps: steps.filter((s) => s.addonId !== PACKAGE_STEP_ID).map((s) => s.addonId === "object-detection" && s.enabled !== false ? withPackageAdmitted(s, packageStep) : s),
|
|
2123
|
+
filledByRoot: true
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
//#endregion
|
|
2032
2127
|
//#region src/pipeline-runner/bench-step-schema.ts
|
|
2033
2128
|
/**
|
|
2034
2129
|
* Shared bench-facing schemas: the minimal recursive step tree. Extracted from
|
|
@@ -4545,9 +4640,9 @@ var PACKAGE_DETECTION_STEP_ID = "package-detection";
|
|
|
4545
4640
|
* through) and the frame was not shed. Post-analysis reads it as "an empty
|
|
4546
4641
|
* doormat on this frame is an ABSENCE, not silence" (D687). Never mutates.
|
|
4547
4642
|
*/
|
|
4548
|
-
function withPackageLook(result, dispatched) {
|
|
4643
|
+
function withPackageLook(result, dispatched, filledByRoot = false) {
|
|
4549
4644
|
if (result.shed === true) return result;
|
|
4550
|
-
return dispatched.some((s) => s.addonId === "package-detection" && s.enabled !== false) ? {
|
|
4645
|
+
return filledByRoot || dispatched.some((s) => s.addonId === "package-detection" && s.enabled !== false) ? {
|
|
4551
4646
|
...result,
|
|
4552
4647
|
packageLooked: true
|
|
4553
4648
|
} : result;
|
|
@@ -5933,12 +6028,10 @@ var PipelineRunner = class {
|
|
|
5933
6028
|
}, (registration.occupancyRecheckSec ?? 0) * 1e3);
|
|
5934
6029
|
if (initialPhase === "active") {
|
|
5935
6030
|
this.detectionStreamHandler?.(deviceId, "subscribe");
|
|
5936
|
-
const cooldownMs = registration.motionCooldownMs ?? DEFAULT_MOTION_COOLDOWN_MS;
|
|
5937
6031
|
this.config.onPhaseChanged?.(deviceId, "active", {
|
|
5938
|
-
|
|
5939
|
-
regions: void 0,
|
|
6032
|
+
cause: "attach",
|
|
5940
6033
|
timestamp: Date.now(),
|
|
5941
|
-
|
|
6034
|
+
...registration.sessionTrigger !== void 0 ? { trigger: registration.sessionTrigger } : {}
|
|
5942
6035
|
});
|
|
5943
6036
|
}
|
|
5944
6037
|
}
|
|
@@ -5953,7 +6046,13 @@ var PipelineRunner = class {
|
|
|
5953
6046
|
clearInterval(state.occupancyTimer);
|
|
5954
6047
|
state.occupancyTimer = null;
|
|
5955
6048
|
}
|
|
5956
|
-
if (state.phase === "active")
|
|
6049
|
+
if (state.phase === "active") {
|
|
6050
|
+
this.detectionStreamHandler?.(deviceId, "unsubscribe");
|
|
6051
|
+
this.config.onPhaseChanged?.(deviceId, "watching", {
|
|
6052
|
+
cause: "detach",
|
|
6053
|
+
timestamp: Date.now()
|
|
6054
|
+
});
|
|
6055
|
+
}
|
|
5957
6056
|
state.motionQueue.clear();
|
|
5958
6057
|
this.discardQueuedDetectionFrames(deviceId, state, "detach");
|
|
5959
6058
|
this.timingSampler.clearDroppedFramesSource(deviceId);
|
|
@@ -6324,6 +6423,7 @@ var PipelineRunner = class {
|
|
|
6324
6423
|
});
|
|
6325
6424
|
this.detectionStreamHandler?.(deviceId, "subscribe");
|
|
6326
6425
|
this.config.onPhaseChanged?.(deviceId, "active", {
|
|
6426
|
+
cause: "motion",
|
|
6327
6427
|
source,
|
|
6328
6428
|
regions,
|
|
6329
6429
|
timestamp: Date.now(),
|
|
@@ -6345,6 +6445,7 @@ var PipelineRunner = class {
|
|
|
6345
6445
|
this.detectionStreamHandler?.(deviceId, "unsubscribe");
|
|
6346
6446
|
const source = state.lastArmedSource ?? "analyzer";
|
|
6347
6447
|
this.config.onPhaseChanged?.(deviceId, "watching", {
|
|
6448
|
+
cause: "motion",
|
|
6348
6449
|
source,
|
|
6349
6450
|
regions: void 0,
|
|
6350
6451
|
timestamp: Date.now(),
|
|
@@ -7159,6 +7260,25 @@ var DETECTION_REDIAL_GIVEUP_WINDOW_MS = 6e4;
|
|
|
7159
7260
|
* NOTHING — see D453 for what that costs and why it was not taken either.
|
|
7160
7261
|
*/
|
|
7161
7262
|
/**
|
|
7263
|
+
* The source fields a `DetectionPhaseTransition` carries, per cause. A motion
|
|
7264
|
+
* transition: its source and cooldown. A session attach: the motion that opened
|
|
7265
|
+
* it, as the orchestrator dispatched it — absent when it sent none. A detach:
|
|
7266
|
+
* nothing.
|
|
7267
|
+
*/
|
|
7268
|
+
function phaseTransitionSourceFields(meta) {
|
|
7269
|
+
if (meta.cause === "motion") return {
|
|
7270
|
+
source: meta.source,
|
|
7271
|
+
cooldownMs: meta.cooldownMs
|
|
7272
|
+
};
|
|
7273
|
+
if (meta.cause === "detach" || meta.trigger === void 0) return {};
|
|
7274
|
+
const { source, triggeredBy, sensorName } = meta.trigger;
|
|
7275
|
+
return {
|
|
7276
|
+
source,
|
|
7277
|
+
triggeredBy,
|
|
7278
|
+
...sensorName !== void 0 ? { sensorName } : {}
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
/**
|
|
7162
7282
|
* Pure decision helper: returns `true` when a dynamic frame-diff analyzer
|
|
7163
7283
|
* subscription should be opened for an onboard-motion camera.
|
|
7164
7284
|
*
|
|
@@ -7222,20 +7342,23 @@ function isDetailPlaneStepId(stepId) {
|
|
|
7222
7342
|
* Extracted as a pure function so the unit suite can exercise the
|
|
7223
7343
|
* derivation without instantiating the full addon.
|
|
7224
7344
|
*/
|
|
7225
|
-
function deriveDetailSteps(steps) {
|
|
7345
|
+
function deriveDetailSteps(steps, plan = EMPTY_LABEL_LINK_PLAN) {
|
|
7226
7346
|
const stepIds = /* @__PURE__ */ new Set();
|
|
7227
7347
|
collectEnabledStepIds(steps, stepIds);
|
|
7348
|
+
const filled = filledStepIds(plan);
|
|
7228
7349
|
const announces = [];
|
|
7229
7350
|
for (const stepId of stepIds) {
|
|
7230
7351
|
const def = tryGetStepDefinition(stepId);
|
|
7231
7352
|
if (!def || def.inputClasses === null) continue;
|
|
7353
|
+
const filledBy = filled.get(def.id);
|
|
7232
7354
|
announces.push({
|
|
7233
7355
|
stepId: def.id,
|
|
7234
7356
|
inputClasses: def.inputClasses,
|
|
7235
7357
|
cadence: def.cadence ?? {
|
|
7236
7358
|
trigger: "once",
|
|
7237
7359
|
maxPerTrack: 3
|
|
7238
|
-
}
|
|
7360
|
+
},
|
|
7361
|
+
...filledBy !== void 0 ? { filledBy } : {}
|
|
7239
7362
|
});
|
|
7240
7363
|
}
|
|
7241
7364
|
return announces;
|
|
@@ -8714,6 +8837,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8714
8837
|
runner.registerCamera(config.deviceId, {
|
|
8715
8838
|
detectionMode: config.detectionMode,
|
|
8716
8839
|
...config.detectionSources !== void 0 ? { detectionSources: config.detectionSources } : {},
|
|
8840
|
+
...config.sessionTrigger !== void 0 ? { sessionTrigger: config.sessionTrigger } : {},
|
|
8717
8841
|
...require_default_detection_model.rootObjectGate(config.steps) !== void 0 ? { objectGate: require_default_detection_model.rootObjectGate(config.steps) } : {},
|
|
8718
8842
|
fps: config.detectionFps,
|
|
8719
8843
|
motionCooldownMs: config.motionCooldownMs,
|
|
@@ -9550,6 +9674,42 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9550
9674
|
* read synchronously by the placement.
|
|
9551
9675
|
*/
|
|
9552
9676
|
customModels = new require_default_detection_model.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
|
|
9677
|
+
/**
|
|
9678
|
+
* Per camera, the label-link plan (D715) and the announce derived from it,
|
|
9679
|
+
* keyed on the two inputs it reads: the attach tree and the custom-model
|
|
9680
|
+
* snapshot (a Frigate+ root is a custom model). Recomputed when either
|
|
9681
|
+
* changes identity — at most once per catalog refresh.
|
|
9682
|
+
*/
|
|
9683
|
+
labelLinkCache = /* @__PURE__ */ new Map();
|
|
9684
|
+
/** The camera's label-link plan; logs once per change of what is filled. */
|
|
9685
|
+
labelLinkPlanFor(deviceId, steps) {
|
|
9686
|
+
const byStep = this.customModels.current();
|
|
9687
|
+
const cached = this.labelLinkCache.get(deviceId);
|
|
9688
|
+
if (cached !== void 0 && cached.steps === steps && cached.byStep === byStep) return cached.plan;
|
|
9689
|
+
const plan = planLabelLinks(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId));
|
|
9690
|
+
const announces = deriveDetailSteps(steps, plan);
|
|
9691
|
+
this.labelLinkCache.set(deviceId, {
|
|
9692
|
+
steps,
|
|
9693
|
+
byStep,
|
|
9694
|
+
plan,
|
|
9695
|
+
announces
|
|
9696
|
+
});
|
|
9697
|
+
const describe = (p) => p === void 0 ? "" : [...p.decisions].map(([id, d]) => `${id}:${d.skip ? "skip" : d.authoritative ? "sole" : "fallback"}`).join(",");
|
|
9698
|
+
if (describe(cached?.plan) !== describe(plan) && plan.decisions.size > 0) this.ctxIfReady?.logger.info("label link: the root model fills pipeline steps", {
|
|
9699
|
+
tags: { deviceId },
|
|
9700
|
+
meta: {
|
|
9701
|
+
rootModelId: plan.rootModelId ?? null,
|
|
9702
|
+
steps: [...plan.decisions].map(([stepId, d]) => ({
|
|
9703
|
+
stepId,
|
|
9704
|
+
policy: d.policy,
|
|
9705
|
+
authoritative: d.authoritative,
|
|
9706
|
+
skip: d.skip,
|
|
9707
|
+
openGaps: d.openGaps
|
|
9708
|
+
}))
|
|
9709
|
+
}
|
|
9710
|
+
});
|
|
9711
|
+
return plan;
|
|
9712
|
+
}
|
|
9553
9713
|
cropConvention = new CropConventionSource({ logger: {
|
|
9554
9714
|
info: (message, extras) => this.ctxIfReady?.logger.info(message, extras),
|
|
9555
9715
|
warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras)
|
|
@@ -9945,6 +10105,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9945
10105
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
9946
10106
|
this.packageGovernor.forget(deviceId);
|
|
9947
10107
|
this.packageGateSkips.delete(deviceId);
|
|
10108
|
+
this.labelLinkCache.delete(deviceId);
|
|
9948
10109
|
this.packageZoneUnionByDevice.delete(deviceId);
|
|
9949
10110
|
this.packageZoneUnionAt.delete(deviceId);
|
|
9950
10111
|
this.lastMotionNearPackageZone.delete(deviceId);
|
|
@@ -10341,7 +10502,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10341
10502
|
acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:detect:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config), true),
|
|
10342
10503
|
startPump: (source, hooks) => {
|
|
10343
10504
|
const dialMs = Date.now() - requestedAt;
|
|
10344
|
-
const useLocalFrameRef = this.executorCoLocated()
|
|
10505
|
+
const useLocalFrameRef = this.executorCoLocated();
|
|
10345
10506
|
log.info("session-decode: detection routed through per-session decode worker (rgb)", { meta: {
|
|
10346
10507
|
streamId: config.detectionStreamId,
|
|
10347
10508
|
codec: source.codec,
|
|
@@ -10601,9 +10762,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10601
10762
|
* The phase is NOT the camera's motion and writes no `motion` runtime slice
|
|
10602
10763
|
* (D696). Every on-motion detection session attaches `always-on`, so the
|
|
10603
10764
|
* phase reads `active` for a whole session, and an always-on camera is
|
|
10604
|
-
* `active` for as long as it is attached
|
|
10765
|
+
* `active` for as long as it is attached (`cause: 'attach'`), and returns to
|
|
10766
|
+
* `watching` on detach (`cause: 'detach'`) — neither names a motion source.
|
|
10605
10767
|
* The slice it used to write was therefore "a session is attached", and was
|
|
10606
|
-
* left `detected: true` after every session
|
|
10768
|
+
* left `detected: true` after every session (the detach close of the EVENT
|
|
10769
|
+
* did not exist either until 2026-10-06). The orchestrator is the only
|
|
10607
10770
|
* writer of that slice, with the EFFECTIVE motion, and this runner says so
|
|
10608
10771
|
* on `getLocalLoad().motionSliceWriter` so that it never writes the slice
|
|
10609
10772
|
* while a runner older than D696 still does.
|
|
@@ -10617,8 +10780,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10617
10780
|
from: detected ? "watching" : "active",
|
|
10618
10781
|
to: detected ? "active" : "watching",
|
|
10619
10782
|
reason: detected ? "motion_detected" : "cooldown_expired",
|
|
10620
|
-
|
|
10621
|
-
|
|
10783
|
+
cause: meta.cause,
|
|
10784
|
+
...phaseTransitionSourceFields(meta),
|
|
10622
10785
|
timestamp: meta.timestamp
|
|
10623
10786
|
};
|
|
10624
10787
|
this.ctx.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.DetectionPhaseTransition, {
|
|
@@ -10658,8 +10821,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10658
10821
|
return null;
|
|
10659
10822
|
}
|
|
10660
10823
|
if (steps.length === 0) return null;
|
|
10661
|
-
|
|
10662
|
-
|
|
10824
|
+
const labelLinkPlan = this.labelLinkPlanFor(deviceId, steps);
|
|
10825
|
+
const packageFill = applyPackageFill(steps, labelLinkPlan);
|
|
10826
|
+
let dispatchSteps = packageFill.steps;
|
|
10827
|
+
if (dispatchSteps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
|
|
10663
10828
|
this.refreshPackageZoneUnion(deviceId);
|
|
10664
10829
|
const motionInZone = this.lastMotionNearPackageZone.get(deviceId) ?? this.lastAnalyzerDetected.get(deviceId) ?? false;
|
|
10665
10830
|
const decision = this.packageGovernor.evaluate({
|
|
@@ -10669,7 +10834,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10669
10834
|
});
|
|
10670
10835
|
const skipped = this.packageGateSkips.get(deviceId) ?? 0;
|
|
10671
10836
|
if (!decision.run) {
|
|
10672
|
-
dispatchSteps =
|
|
10837
|
+
dispatchSteps = dispatchSteps.filter((s) => s.addonId !== PACKAGE_DETECTION_STEP_ID);
|
|
10673
10838
|
if (skipped === 0) log.info("package-detection gated off by the motion-settle governor", {
|
|
10674
10839
|
tags: { deviceId },
|
|
10675
10840
|
meta: { motionInZone: this.lastAnalyzerDetected.get(deviceId) ?? false }
|
|
@@ -10746,7 +10911,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10746
10911
|
return null;
|
|
10747
10912
|
}
|
|
10748
10913
|
if (result === null) return null;
|
|
10749
|
-
const looked = withPackageLook(result, dispatchSteps);
|
|
10914
|
+
const looked = withPackageLook(applyLabelLinkAuthority(result, labelLinkPlan), dispatchSteps, packageFill.filledByRoot);
|
|
10750
10915
|
return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
|
|
10751
10916
|
}
|
|
10752
10917
|
/**
|
|
@@ -11393,7 +11558,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
11393
11558
|
...this.liveWorkerSlotWaiter(deviceId),
|
|
11394
11559
|
acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:occupancy:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config)),
|
|
11395
11560
|
startPump: (source, hooks) => {
|
|
11396
|
-
const useLocalFrameRef = this.executorCoLocated()
|
|
11561
|
+
const useLocalFrameRef = this.executorCoLocated();
|
|
11397
11562
|
return startSessionDecodePump({
|
|
11398
11563
|
fork: this.forkDecodeWorkerWithLease,
|
|
11399
11564
|
lease: this.resolveSessionLeaseKnobs,
|
|
@@ -11443,7 +11608,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
11443
11608
|
emitInferenceResult(deviceId, frame, result, handle, source, occasion) {
|
|
11444
11609
|
if (!this.ctx?.eventBus) return;
|
|
11445
11610
|
const capturedAt = frame.capturedAt;
|
|
11446
|
-
const detailSteps = this.attached.get(deviceId)?.detailSteps;
|
|
11611
|
+
const detailSteps = this.labelLinkCache.get(deviceId)?.announces ?? this.attached.get(deviceId)?.detailSteps;
|
|
11447
11612
|
const payload = {
|
|
11448
11613
|
deviceId,
|
|
11449
11614
|
frame: result,
|
|
@@ -11638,6 +11803,7 @@ exports.cameraOwnsMotionViaSensor = require_dist.cameraOwnsMotionViaSensor;
|
|
|
11638
11803
|
exports.customActions = pipelineRunnerBenchActions;
|
|
11639
11804
|
exports.default = PipelineRunnerAddon;
|
|
11640
11805
|
exports.deriveDetailSteps = deriveDetailSteps;
|
|
11806
|
+
exports.phaseTransitionSourceFields = phaseTransitionSourceFields;
|
|
11641
11807
|
exports.resolveMotionFrameSource = resolveMotionFrameSource;
|
|
11642
11808
|
exports.resolveSessionDecodeHostname = resolveSessionDecodeHostname;
|
|
11643
11809
|
exports.resolveSessionDecodeOwnerNodeId = resolveSessionDecodeOwnerNodeId;
|