@camstack/addon-pipeline 1.2.43 → 1.2.45
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 +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +158 -81
- package/dist/detection-pipeline/index.mjs +158 -81
- package/dist/{dist-B-VVBzrL.mjs → dist-BDkEhx4Z.mjs} +1315 -127
- package/dist/{dist-UHbKZiSa.js → dist-QyZ7QSRP.js} +1386 -126
- package/dist/{event-loop-stall-monitor-C7_L6vHO.js → event-loop-stall-monitor-COEKVBQK.js} +53 -8
- package/dist/{event-loop-stall-monitor-DfKdC7G4.mjs → event-loop-stall-monitor-IAuNEdmG.mjs} +53 -8
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +342 -69
- package/dist/pipeline-runner/index.mjs +342 -69
- package/dist/recorder/index.js +189 -6
- package/dist/recorder/index.mjs +189 -6
- package/dist/session-decode/decode-worker-child.js +197 -24
- package/dist/session-decode/decode-worker-child.mjs +197 -24
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D7iQbuKY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C68YfSUH.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CT2bGWqM.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-54IklEep.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B3GxBnXM.mjs} +1 -1
- package/dist/stream-broker/{hostInit-CKYS9yZO.mjs → hostInit-BMCrULpc.mjs} +2 -2
- package/dist/stream-broker/index.js +4602 -1442
- package/dist/stream-broker/index.mjs +4601 -1442
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/worker-protocol-DFH_z2-q.mjs +244 -0
- package/dist/worker-protocol-le27Mw_8.js +279 -0
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-DxZ75vB8.js → MaskShapeCanvas-DI4BY7W2-C7bsz4cP.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-BBY2Ztz5.js → MotionZonesSettings-NcxxQN8r-BmhLinUd.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-BC-3QmHa.js → PrivacyMaskSettings-APgPLF7p-BJjyGWFy.js} +1 -1
- package/embed-dist/assets/index-DC63og2c.css +2 -0
- package/embed-dist/assets/{index-on1wyOnd.js → index-a4CMt-Uc.js} +12 -12
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/postprocessors/__init__.py +2 -0
- package/python/postprocessors/rfdetr.py +161 -0
- package/python/postprocessors/test_rfdetr.py +193 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CCC9fqHH.mjs +0 -26
- package/dist/worker-protocol-D7RzZIla.mjs +0 -77
- package/dist/worker-protocol-DextwlTX.js +0 -94
- package/embed-dist/assets/index-CGdwTcwE.css +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
3
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
3
|
+
const require_worker_protocol = require("../worker-protocol-le27Mw_8.js");
|
|
4
4
|
let node_url = require("node:url");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -375,6 +375,94 @@ var LeaseActivityGate = class {
|
|
|
375
375
|
return this.now() < this.armedUntil;
|
|
376
376
|
}
|
|
377
377
|
};
|
|
378
|
+
/**
|
|
379
|
+
* The marked-frame window plus the offered/admitted/unmarked-crop counters for
|
|
380
|
+
* ONE decode worker. Pure and node-av-free so the filter is unit-testable.
|
|
381
|
+
*/
|
|
382
|
+
var LeaseAdmissionLedger = class {
|
|
383
|
+
mode;
|
|
384
|
+
capacity;
|
|
385
|
+
/** Insertion-ordered so the oldest mark is `keys().next()` — a FIFO window. */
|
|
386
|
+
marked = /* @__PURE__ */ new Set();
|
|
387
|
+
markedTotal = 0;
|
|
388
|
+
offeredCount = 0;
|
|
389
|
+
admittedCount = 0;
|
|
390
|
+
unmarkedCropCount = 0;
|
|
391
|
+
constructor(options) {
|
|
392
|
+
this.mode = options.mode;
|
|
393
|
+
this.capacity = Math.max(1, options.capacity ?? 1024);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Marks RECEIVED since the worker started.
|
|
397
|
+
*
|
|
398
|
+
* Not a curiosity: in `all` mode every other counter looks identical whether
|
|
399
|
+
* the mark signal is working or has silently stopped arriving, and
|
|
400
|
+
* {@link unmarkedCrops} reads 0 in BOTH cases — an empty marked window
|
|
401
|
+
* answers "was this frame inferred?" with "I cannot say", which is correctly
|
|
402
|
+
* not counted as evidence. So a premise measured while `marks` is 0 measures
|
|
403
|
+
* nothing. This is the counter that tells the two apart.
|
|
404
|
+
*/
|
|
405
|
+
get marks() {
|
|
406
|
+
return this.markedTotal;
|
|
407
|
+
}
|
|
408
|
+
/** Frames offered to the retention hook since the worker started. */
|
|
409
|
+
get offered() {
|
|
410
|
+
return this.offeredCount;
|
|
411
|
+
}
|
|
412
|
+
/** Frames the ledger let through to the lease store. */
|
|
413
|
+
get admitted() {
|
|
414
|
+
return this.admittedCount;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Crop requests naming a frame inside the tracked window that was never
|
|
418
|
+
* marked. **This is the premise counter**: any value above 0 means something
|
|
419
|
+
* can ask for a frame the runner never sent to inference, and `inferred` is
|
|
420
|
+
* dropping frames that someone wants.
|
|
421
|
+
*/
|
|
422
|
+
get unmarkedCrops() {
|
|
423
|
+
return this.unmarkedCropCount;
|
|
424
|
+
}
|
|
425
|
+
/** Marks currently remembered (tests / bound checks). */
|
|
426
|
+
get markedSize() {
|
|
427
|
+
return this.marked.size;
|
|
428
|
+
}
|
|
429
|
+
/** The runner admitted `frameId` to its detection queue. */
|
|
430
|
+
mark(frameId) {
|
|
431
|
+
this.markedTotal++;
|
|
432
|
+
this.marked.delete(frameId);
|
|
433
|
+
this.marked.add(frameId);
|
|
434
|
+
while (this.marked.size > this.capacity) {
|
|
435
|
+
const oldest = this.marked.values().next().value;
|
|
436
|
+
if (oldest === void 0) break;
|
|
437
|
+
this.marked.delete(oldest);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Should this delivered frame get a native lease? `true` in `all` mode
|
|
442
|
+
* always; in `inferred` mode only when the runner marked it. Counts both
|
|
443
|
+
* either way, so the saving is visible on the metrics line in both modes.
|
|
444
|
+
*/
|
|
445
|
+
admit(frameId) {
|
|
446
|
+
this.offeredCount++;
|
|
447
|
+
const keep = this.mode === "all" || this.marked.has(frameId);
|
|
448
|
+
if (keep) this.admittedCount++;
|
|
449
|
+
return keep;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Record a native-crop request for `frameId`. Returns `true` when that frame
|
|
453
|
+
* was never marked AND is recent enough for the window to be sure of it —
|
|
454
|
+
* a frame older than the oldest remembered mark is UNKNOWABLE, not evidence,
|
|
455
|
+
* and is deliberately not counted (over-reporting here would read as a broken
|
|
456
|
+
* premise and send the next session chasing nothing).
|
|
457
|
+
*/
|
|
458
|
+
noteCropRequest(frameId) {
|
|
459
|
+
if (this.marked.has(frameId)) return false;
|
|
460
|
+
const oldest = this.marked.values().next().value;
|
|
461
|
+
if (oldest === void 0 || frameId < oldest) return false;
|
|
462
|
+
this.unmarkedCropCount++;
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
378
466
|
//#endregion
|
|
379
467
|
//#region src/session-decode/native-frame-ring.ts
|
|
380
468
|
/**
|
|
@@ -702,6 +790,17 @@ var METRICS_INTERVAL_MS = 1e4;
|
|
|
702
790
|
* leak site), so the cap MUST stay small; a miss is free (caller falls back to
|
|
703
791
|
* today's detection-frame crop). Override with `CAMSTACK_SESSION_NATIVE_CROP_RING`
|
|
704
792
|
* (0 disables retention entirely); clamped to [0,4], default 2.
|
|
793
|
+
*
|
|
794
|
+
* **Deliberately NOT an operator setting** (unlike the three lease knobs, which
|
|
795
|
+
* became cluster-wide `pipeline-orchestrator` settings on 2026-08-07). Every
|
|
796
|
+
* slot here pins a GPU/VAAPI surface out of the decoder's own pool, and this is
|
|
797
|
+
* the historic leak site — a value large enough to look useful in a settings UI
|
|
798
|
+
* is a value large enough to starve the decoder, and the failure shows up as a
|
|
799
|
+
* camera at 0 fps rather than as memory pressure anyone would trace back to a
|
|
800
|
+
* slider. The ring is also only reachable at all when the lease is DISABLED
|
|
801
|
+
* (budget 0), so exposing it would offer a knob that does nothing in every
|
|
802
|
+
* default configuration. It stays an env-var escape hatch for the person
|
|
803
|
+
* debugging the lease itself.
|
|
705
804
|
*/
|
|
706
805
|
var NATIVE_RING_CAP = (() => {
|
|
707
806
|
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_CROP_RING"]);
|
|
@@ -709,21 +808,33 @@ var NATIVE_RING_CAP = (() => {
|
|
|
709
808
|
return Math.min(4, Math.max(0, Math.floor(raw)));
|
|
710
809
|
})();
|
|
711
810
|
/**
|
|
811
|
+
* The three native-lease knobs in force for THIS worker, resolved once at
|
|
812
|
+
* module load (the lease store and the demand gate are constructed with the
|
|
813
|
+
* worker instance, before the first `start` message).
|
|
814
|
+
*
|
|
815
|
+
* These are **operator settings**, not env vars: they live on
|
|
816
|
+
* `pipeline-orchestrator`'s cluster-wide globals, the pipeline-runner reads them
|
|
817
|
+
* over the hub-routed `addon-settings` cap, and it injects the ones the operator
|
|
818
|
+
* SET into this child's environment at fork time. Precedence is
|
|
819
|
+
* `setting > env > default` — the `CAMSTACK_SESSION_NATIVE_LEASE_*` vars survive
|
|
820
|
+
* as an emergency/dev override for knobs the operator has not touched. The
|
|
821
|
+
* resolved values AND their source are logged once per worker in
|
|
822
|
+
* {@link DecodeWorkerChild.handleStart}. Full contract:
|
|
823
|
+
* `native-lease-config.ts` and `packages/types/src/pipeline/native-lease.ts`.
|
|
824
|
+
*/
|
|
825
|
+
var NATIVE_LEASE_KNOBS = require_worker_protocol.resolveNativeLeaseKnobs(process.env);
|
|
826
|
+
/**
|
|
712
827
|
* Hard RAM budget (bytes) for the native-frame LEASE store — the primary
|
|
713
828
|
* native-crop survival window. Unlike {@link NATIVE_RING_CAP} (GPU surfaces,
|
|
714
829
|
* leak-prone → tiny), a lease is a downloaded RAM copy, so the window is sized
|
|
715
830
|
* by memory, not a 2-frame count, and the late cross-process crop reliably
|
|
716
831
|
* hits. The primary eviction is the {@link NATIVE_LEASE_TTL_MS} TTL — a 64MB
|
|
717
832
|
* budget held only ~5 native 4K frames (~0.2s), which the late crop still
|
|
718
|
-
* outran; the budget is
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
* lease and falls back to the {@link NATIVE_RING_CAP} GPU ring (today's path).
|
|
833
|
+
* outran; the budget is a HIGH safety ceiling (default 1024MB) so the TTL is
|
|
834
|
+
* the effective cap and the ~40-200ms crop reliably lands within it. `0`
|
|
835
|
+
* DISABLES the lease and falls back to the {@link NATIVE_RING_CAP} GPU ring.
|
|
722
836
|
*/
|
|
723
|
-
var NATIVE_LEASE_BUDGET_BYTES =
|
|
724
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_BUDGET_MB"]);
|
|
725
|
-
return (Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 1024) * 1024 * 1024;
|
|
726
|
-
})();
|
|
837
|
+
var NATIVE_LEASE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.budgetMb * 1024 * 1024;
|
|
727
838
|
/**
|
|
728
839
|
* TTL (ms) after which a native-frame lease is treated as a miss and reclaimed.
|
|
729
840
|
* The backstop that bounds in-flight RAM even if the explicit `releaseNativeLease`
|
|
@@ -731,25 +842,19 @@ var NATIVE_LEASE_BUDGET_BYTES = (() => {
|
|
|
731
842
|
* inference + the cross-process inference-result hop to hub post-analysis +
|
|
732
843
|
* tracking + the tRPC crop round-trip back — on a busy camera with many
|
|
733
844
|
* concurrent tracks that is well past the old 500 ms (which the busiest cameras'
|
|
734
|
-
* subject crops outran, missing to the ≤640 fallback).
|
|
845
|
+
* subject crops outran, missing to the ≤640 fallback). Default 1200 ms; still
|
|
735
846
|
* bounded by the per-session {@link NATIVE_LEASE_BUDGET_BYTES} ceiling and the
|
|
736
847
|
* {@link NATIVE_LEASE_ACTIVITY_WINDOW_MS} demand gate, so idle cameras retain
|
|
737
|
-
* nothing.
|
|
848
|
+
* nothing.
|
|
738
849
|
*/
|
|
739
|
-
var NATIVE_LEASE_TTL_MS =
|
|
740
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS"]);
|
|
741
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1200;
|
|
742
|
-
})();
|
|
850
|
+
var NATIVE_LEASE_TTL_MS = NATIVE_LEASE_KNOBS.values.ttlMs;
|
|
743
851
|
/**
|
|
744
852
|
* Demand window (ms) for the lease-capture {@link LeaseActivityGate}: eager
|
|
745
853
|
* per-frame native downloads run only within this window of the last
|
|
746
|
-
* native-crop request (or dial start). `0` disables
|
|
747
|
-
* eager capture).
|
|
854
|
+
* native-crop request (or dial start). `0` disables the GATE (legacy always-on
|
|
855
|
+
* eager capture) — it does not disable retention. Default 15000.
|
|
748
856
|
*/
|
|
749
|
-
var NATIVE_LEASE_ACTIVITY_WINDOW_MS =
|
|
750
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_ACTIVITY_MS"]);
|
|
751
|
-
return Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 15e3;
|
|
752
|
-
})();
|
|
857
|
+
var NATIVE_LEASE_ACTIVITY_WINDOW_MS = NATIVE_LEASE_KNOBS.values.activityMs;
|
|
753
858
|
/**
|
|
754
859
|
* `pinnedRgbCrop` — pinned-color RGB24 native-crop retention. DEFAULT **ON**
|
|
755
860
|
* (2026-07-20 rollout); set `CAMSTACK_SESSION_PINNED_RGB_CROP=0` to opt out.
|
|
@@ -1040,6 +1145,15 @@ var DecodeWorkerChild = class {
|
|
|
1040
1145
|
*/
|
|
1041
1146
|
leaseGate = new LeaseActivityGate(NATIVE_LEASE_ACTIVITY_WINDOW_MS);
|
|
1042
1147
|
/**
|
|
1148
|
+
* WHICH delivered frames are worth a native lease — the runner marks the ones
|
|
1149
|
+
* it admitted to inference (`markInferred`), and in `inferred` mode only
|
|
1150
|
+
* those are retained. Also the premise counter: it tallies every crop request
|
|
1151
|
+
* naming a frame that was never marked, in BOTH modes, so the assumption the
|
|
1152
|
+
* filter rests on is measured on live traffic rather than argued. See
|
|
1153
|
+
* `lease-admission-ledger.ts`.
|
|
1154
|
+
*/
|
|
1155
|
+
admission = new LeaseAdmissionLedger({ mode: NATIVE_LEASE_KNOBS.values.admission });
|
|
1156
|
+
/**
|
|
1043
1157
|
* DEDICATED full-frame GPU→system download filtergraph
|
|
1044
1158
|
* (`scale_<be>=iw:ih,hwdownload,format=nv12,format=yuv420p`) used ONLY to
|
|
1045
1159
|
* materialize a HW surface into a leasable software frame at NATIVE
|
|
@@ -1151,6 +1265,9 @@ var DecodeWorkerChild = class {
|
|
|
1151
1265
|
case "releaseNativeLease":
|
|
1152
1266
|
this.leaseStore.release(message.frameId);
|
|
1153
1267
|
return;
|
|
1268
|
+
case "markInferred":
|
|
1269
|
+
this.admission.mark(message.frameId);
|
|
1270
|
+
return;
|
|
1154
1271
|
case "stop":
|
|
1155
1272
|
this.handleStop();
|
|
1156
1273
|
return;
|
|
@@ -1210,7 +1327,7 @@ var DecodeWorkerChild = class {
|
|
|
1210
1327
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
1211
1328
|
const rssMb = Math.round(process.memoryUsage().rss / 1048576);
|
|
1212
1329
|
const ageS = Math.round((now - this.startedAt) / 1e3);
|
|
1213
|
-
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1330
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1214
1331
|
this.lastMetricsAt = now;
|
|
1215
1332
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
1216
1333
|
if (!this.warnedLarge && rssMb >= WORKER_RSS_WARN_MB) {
|
|
@@ -1225,6 +1342,7 @@ var DecodeWorkerChild = class {
|
|
|
1225
1342
|
}
|
|
1226
1343
|
this.started = true;
|
|
1227
1344
|
this.deviceId = source.deviceId;
|
|
1345
|
+
this.emitStderr(`decode-worker-child: ${require_worker_protocol.formatNativeLeaseKnobs(NATIVE_LEASE_KNOBS)}\n`, "info");
|
|
1228
1346
|
this.sessionFormat = opts.format ?? "rgb";
|
|
1229
1347
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
1230
1348
|
this.lastMetricsAt = Date.now();
|
|
@@ -1275,6 +1393,7 @@ var DecodeWorkerChild = class {
|
|
|
1275
1393
|
*/
|
|
1276
1394
|
handleNativeCrop(requestId, frameId, bbox, maxWidth) {
|
|
1277
1395
|
this.leaseGate.arm();
|
|
1396
|
+
if (this.admission.noteCropRequest(frameId)) this.emitStderr(`decode-worker-child: native crop named an UNINFERRED frame (frameId ${frameId}) — the 'inferred' lease-admission premise does not hold on this path; total=${this.admission.unmarkedCrops}\n`);
|
|
1278
1397
|
const frame = this.leaseStore.get(frameId)?.frame ?? this.resolveRetainedFrame(frameId);
|
|
1279
1398
|
if (!frame) {
|
|
1280
1399
|
this.nativeCropMisses++;
|
|
@@ -1344,10 +1463,59 @@ var DecodeWorkerChild = class {
|
|
|
1344
1463
|
* is leased directly. A download failure still consumes ownership (freed
|
|
1345
1464
|
* here) so the slot never double-frees — the crop simply misses → fallback.
|
|
1346
1465
|
* - Lease DISABLED (`budget 0`): fall back to today's tiny GPU {@link nativeRing}.
|
|
1466
|
+
*
|
|
1467
|
+
* ## LEASE ADMISSION — what actually gets stored
|
|
1468
|
+
*
|
|
1469
|
+
* Three conditions decide it, and only the third is per-frame:
|
|
1470
|
+
* 1. the lease is enabled (`budgetMb > 0`);
|
|
1471
|
+
* 2. {@link leaseGate} is inside its demand window (`activityMs` since the
|
|
1472
|
+
* last native-crop request or the dial);
|
|
1473
|
+
* 3. {@link admission} accepts the frame — everything in `all` mode, only
|
|
1474
|
+
* frames the runner MARKED as having reached inference in `inferred`
|
|
1475
|
+
* mode (`nativeLeaseAdmission`).
|
|
1476
|
+
*
|
|
1477
|
+
* What reaches this function at all is the {@link FrameSlot} lifecycle, not a
|
|
1478
|
+
* quality filter. `retainSuperseded` is called only from
|
|
1479
|
+
* `FrameSlot.reserveSlot()` (`frame-slot.ts`), on the frame the PREVIOUS
|
|
1480
|
+
* `pull` delivered to the parent, at the moment the next `pull` supersedes it.
|
|
1481
|
+
* So:
|
|
1482
|
+
* - frames dropped by the `minIntervalMs` fps throttle never enter the slot
|
|
1483
|
+
* and are never offered;
|
|
1484
|
+
* - frames superseded in the slot before any `pull` took them (latest-wins)
|
|
1485
|
+
* are freed by `publish` and never offered;
|
|
1486
|
+
* - every DELIVERED frame is offered exactly once, and then rule 3 decides.
|
|
1487
|
+
*
|
|
1488
|
+
* ## Why `inferred` is safe, and how that is checked rather than assumed
|
|
1489
|
+
*
|
|
1490
|
+
* A native crop names a `frameId`, and the only way a `frameId` leaves the
|
|
1491
|
+
* runner is on a `FrameHandle` riding an INFERENCE RESULT — post-analysis
|
|
1492
|
+
* reaches every one of its handles through `processFrame`, whose only
|
|
1493
|
+
* handle-bearing caller is `handleInferenceResult`. A frame the runner never
|
|
1494
|
+
* enqueued is therefore a frame nobody can ask for. The counter that keeps
|
|
1495
|
+
* this honest is `leaseUnmarkedCrops` on the metrics line: it counts crop
|
|
1496
|
+
* requests naming an unmarked frame in BOTH modes, so the premise is a
|
|
1497
|
+
* production number, not a reading of the call graph.
|
|
1498
|
+
*
|
|
1499
|
+
* The NEWEST delivered frame is never at risk either way: it is the slot's
|
|
1500
|
+
* reserved frame and {@link handleNativeCrop} resolves it through
|
|
1501
|
+
* `FrameSlot.toBuffer`, never through the lease.
|
|
1502
|
+
*
|
|
1503
|
+
* ## What it costs / saves
|
|
1504
|
+
*
|
|
1505
|
+
* One 4K frame is ~24.9 MB on the default pinned-RGB24 lease path
|
|
1506
|
+
* ({@link PINNED_RGB_CROP}), ~12.4 MB as YUV420P. At the ~25 delivered fps of
|
|
1507
|
+
* `all` mode that is ~620 MB/s of GPU→RAM download while the gate is open and
|
|
1508
|
+
* a steady-state resident set of roughly `frameBytes × deliveredFps ×
|
|
1509
|
+
* ttlSeconds`. `inferred` cuts both by the governor's admit ratio — ~5 of 25
|
|
1510
|
+
* frames on a two-plane camera, so ~80%. Prompt `releaseNativeLease` reclaims
|
|
1511
|
+
* earlier still, so the observed figure is lower than the ceiling; the
|
|
1512
|
+
* ceiling is what the budget knob has to survive. See
|
|
1513
|
+
* `docs/design/decode-path.md` → "Lease admission".
|
|
1347
1514
|
*/
|
|
1348
1515
|
captureNativeFrame(frameId, frame) {
|
|
1349
1516
|
if (!this.leaseStore.enabled) return this.nativeRing.retain(frameId, frame);
|
|
1350
1517
|
if (!this.leaseGate.active) return false;
|
|
1518
|
+
if (!this.admission.admit(frameId)) return false;
|
|
1351
1519
|
if (frame.isHwFrame()) {
|
|
1352
1520
|
const software = this.downloadToSoftware(frame);
|
|
1353
1521
|
frame.free();
|
|
@@ -1935,14 +2103,19 @@ var DecodeWorkerChild = class {
|
|
|
1935
2103
|
* output. Public so the module-level process handlers — which only hold the
|
|
1936
2104
|
* `worker` instance — emit device-tagged lines too. Callers pass the line
|
|
1937
2105
|
* INCLUDING its trailing newline (unchanged from the prior direct writes).
|
|
2106
|
+
*
|
|
2107
|
+
* `level` overrides the derived severity for the few lines whose importance
|
|
2108
|
+
* is not inferable from their words — the lease-knob line states an
|
|
2109
|
+
* operator's configuration taking effect and would otherwise sit at `debug`,
|
|
2110
|
+
* where a setting that failed to arrive is indistinguishable from silence.
|
|
1938
2111
|
*/
|
|
1939
|
-
emitStderr(line) {
|
|
2112
|
+
emitStderr(line, level) {
|
|
1940
2113
|
const tag = this.deviceId === null ? "[dev:?]" : `[dev:${this.deviceId}]`;
|
|
1941
2114
|
process.stderr.write(`${tag} ${line}`);
|
|
1942
2115
|
try {
|
|
1943
2116
|
this.send({
|
|
1944
2117
|
kind: "log",
|
|
1945
|
-
level: require_worker_protocol.logLevelForLine(line),
|
|
2118
|
+
level: level ?? require_worker_protocol.logLevelForLine(line),
|
|
1946
2119
|
message: `${tag} ${line.trimEnd()}`
|
|
1947
2120
|
});
|
|
1948
2121
|
} catch {}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as isWorkerRequest, r as logLevelForLine } from "../worker-protocol-
|
|
1
|
+
import { i as formatNativeLeaseKnobs, n as isWorkerRequest, o as resolveNativeLeaseKnobs, r as logLevelForLine } from "../worker-protocol-DFH_z2-q.mjs";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -371,6 +371,94 @@ var LeaseActivityGate = class {
|
|
|
371
371
|
return this.now() < this.armedUntil;
|
|
372
372
|
}
|
|
373
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* The marked-frame window plus the offered/admitted/unmarked-crop counters for
|
|
376
|
+
* ONE decode worker. Pure and node-av-free so the filter is unit-testable.
|
|
377
|
+
*/
|
|
378
|
+
var LeaseAdmissionLedger = class {
|
|
379
|
+
mode;
|
|
380
|
+
capacity;
|
|
381
|
+
/** Insertion-ordered so the oldest mark is `keys().next()` — a FIFO window. */
|
|
382
|
+
marked = /* @__PURE__ */ new Set();
|
|
383
|
+
markedTotal = 0;
|
|
384
|
+
offeredCount = 0;
|
|
385
|
+
admittedCount = 0;
|
|
386
|
+
unmarkedCropCount = 0;
|
|
387
|
+
constructor(options) {
|
|
388
|
+
this.mode = options.mode;
|
|
389
|
+
this.capacity = Math.max(1, options.capacity ?? 1024);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Marks RECEIVED since the worker started.
|
|
393
|
+
*
|
|
394
|
+
* Not a curiosity: in `all` mode every other counter looks identical whether
|
|
395
|
+
* the mark signal is working or has silently stopped arriving, and
|
|
396
|
+
* {@link unmarkedCrops} reads 0 in BOTH cases — an empty marked window
|
|
397
|
+
* answers "was this frame inferred?" with "I cannot say", which is correctly
|
|
398
|
+
* not counted as evidence. So a premise measured while `marks` is 0 measures
|
|
399
|
+
* nothing. This is the counter that tells the two apart.
|
|
400
|
+
*/
|
|
401
|
+
get marks() {
|
|
402
|
+
return this.markedTotal;
|
|
403
|
+
}
|
|
404
|
+
/** Frames offered to the retention hook since the worker started. */
|
|
405
|
+
get offered() {
|
|
406
|
+
return this.offeredCount;
|
|
407
|
+
}
|
|
408
|
+
/** Frames the ledger let through to the lease store. */
|
|
409
|
+
get admitted() {
|
|
410
|
+
return this.admittedCount;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Crop requests naming a frame inside the tracked window that was never
|
|
414
|
+
* marked. **This is the premise counter**: any value above 0 means something
|
|
415
|
+
* can ask for a frame the runner never sent to inference, and `inferred` is
|
|
416
|
+
* dropping frames that someone wants.
|
|
417
|
+
*/
|
|
418
|
+
get unmarkedCrops() {
|
|
419
|
+
return this.unmarkedCropCount;
|
|
420
|
+
}
|
|
421
|
+
/** Marks currently remembered (tests / bound checks). */
|
|
422
|
+
get markedSize() {
|
|
423
|
+
return this.marked.size;
|
|
424
|
+
}
|
|
425
|
+
/** The runner admitted `frameId` to its detection queue. */
|
|
426
|
+
mark(frameId) {
|
|
427
|
+
this.markedTotal++;
|
|
428
|
+
this.marked.delete(frameId);
|
|
429
|
+
this.marked.add(frameId);
|
|
430
|
+
while (this.marked.size > this.capacity) {
|
|
431
|
+
const oldest = this.marked.values().next().value;
|
|
432
|
+
if (oldest === void 0) break;
|
|
433
|
+
this.marked.delete(oldest);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Should this delivered frame get a native lease? `true` in `all` mode
|
|
438
|
+
* always; in `inferred` mode only when the runner marked it. Counts both
|
|
439
|
+
* either way, so the saving is visible on the metrics line in both modes.
|
|
440
|
+
*/
|
|
441
|
+
admit(frameId) {
|
|
442
|
+
this.offeredCount++;
|
|
443
|
+
const keep = this.mode === "all" || this.marked.has(frameId);
|
|
444
|
+
if (keep) this.admittedCount++;
|
|
445
|
+
return keep;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Record a native-crop request for `frameId`. Returns `true` when that frame
|
|
449
|
+
* was never marked AND is recent enough for the window to be sure of it —
|
|
450
|
+
* a frame older than the oldest remembered mark is UNKNOWABLE, not evidence,
|
|
451
|
+
* and is deliberately not counted (over-reporting here would read as a broken
|
|
452
|
+
* premise and send the next session chasing nothing).
|
|
453
|
+
*/
|
|
454
|
+
noteCropRequest(frameId) {
|
|
455
|
+
if (this.marked.has(frameId)) return false;
|
|
456
|
+
const oldest = this.marked.values().next().value;
|
|
457
|
+
if (oldest === void 0 || frameId < oldest) return false;
|
|
458
|
+
this.unmarkedCropCount++;
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
374
462
|
//#endregion
|
|
375
463
|
//#region src/session-decode/native-frame-ring.ts
|
|
376
464
|
/**
|
|
@@ -698,6 +786,17 @@ var METRICS_INTERVAL_MS = 1e4;
|
|
|
698
786
|
* leak site), so the cap MUST stay small; a miss is free (caller falls back to
|
|
699
787
|
* today's detection-frame crop). Override with `CAMSTACK_SESSION_NATIVE_CROP_RING`
|
|
700
788
|
* (0 disables retention entirely); clamped to [0,4], default 2.
|
|
789
|
+
*
|
|
790
|
+
* **Deliberately NOT an operator setting** (unlike the three lease knobs, which
|
|
791
|
+
* became cluster-wide `pipeline-orchestrator` settings on 2026-08-07). Every
|
|
792
|
+
* slot here pins a GPU/VAAPI surface out of the decoder's own pool, and this is
|
|
793
|
+
* the historic leak site — a value large enough to look useful in a settings UI
|
|
794
|
+
* is a value large enough to starve the decoder, and the failure shows up as a
|
|
795
|
+
* camera at 0 fps rather than as memory pressure anyone would trace back to a
|
|
796
|
+
* slider. The ring is also only reachable at all when the lease is DISABLED
|
|
797
|
+
* (budget 0), so exposing it would offer a knob that does nothing in every
|
|
798
|
+
* default configuration. It stays an env-var escape hatch for the person
|
|
799
|
+
* debugging the lease itself.
|
|
701
800
|
*/
|
|
702
801
|
var NATIVE_RING_CAP = (() => {
|
|
703
802
|
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_CROP_RING"]);
|
|
@@ -705,21 +804,33 @@ var NATIVE_RING_CAP = (() => {
|
|
|
705
804
|
return Math.min(4, Math.max(0, Math.floor(raw)));
|
|
706
805
|
})();
|
|
707
806
|
/**
|
|
807
|
+
* The three native-lease knobs in force for THIS worker, resolved once at
|
|
808
|
+
* module load (the lease store and the demand gate are constructed with the
|
|
809
|
+
* worker instance, before the first `start` message).
|
|
810
|
+
*
|
|
811
|
+
* These are **operator settings**, not env vars: they live on
|
|
812
|
+
* `pipeline-orchestrator`'s cluster-wide globals, the pipeline-runner reads them
|
|
813
|
+
* over the hub-routed `addon-settings` cap, and it injects the ones the operator
|
|
814
|
+
* SET into this child's environment at fork time. Precedence is
|
|
815
|
+
* `setting > env > default` — the `CAMSTACK_SESSION_NATIVE_LEASE_*` vars survive
|
|
816
|
+
* as an emergency/dev override for knobs the operator has not touched. The
|
|
817
|
+
* resolved values AND their source are logged once per worker in
|
|
818
|
+
* {@link DecodeWorkerChild.handleStart}. Full contract:
|
|
819
|
+
* `native-lease-config.ts` and `packages/types/src/pipeline/native-lease.ts`.
|
|
820
|
+
*/
|
|
821
|
+
var NATIVE_LEASE_KNOBS = resolveNativeLeaseKnobs(process.env);
|
|
822
|
+
/**
|
|
708
823
|
* Hard RAM budget (bytes) for the native-frame LEASE store — the primary
|
|
709
824
|
* native-crop survival window. Unlike {@link NATIVE_RING_CAP} (GPU surfaces,
|
|
710
825
|
* leak-prone → tiny), a lease is a downloaded RAM copy, so the window is sized
|
|
711
826
|
* by memory, not a 2-frame count, and the late cross-process crop reliably
|
|
712
827
|
* hits. The primary eviction is the {@link NATIVE_LEASE_TTL_MS} TTL — a 64MB
|
|
713
828
|
* budget held only ~5 native 4K frames (~0.2s), which the late crop still
|
|
714
|
-
* outran; the budget is
|
|
715
|
-
*
|
|
716
|
-
*
|
|
717
|
-
* lease and falls back to the {@link NATIVE_RING_CAP} GPU ring (today's path).
|
|
829
|
+
* outran; the budget is a HIGH safety ceiling (default 1024MB) so the TTL is
|
|
830
|
+
* the effective cap and the ~40-200ms crop reliably lands within it. `0`
|
|
831
|
+
* DISABLES the lease and falls back to the {@link NATIVE_RING_CAP} GPU ring.
|
|
718
832
|
*/
|
|
719
|
-
var NATIVE_LEASE_BUDGET_BYTES =
|
|
720
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_BUDGET_MB"]);
|
|
721
|
-
return (Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 1024) * 1024 * 1024;
|
|
722
|
-
})();
|
|
833
|
+
var NATIVE_LEASE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.budgetMb * 1024 * 1024;
|
|
723
834
|
/**
|
|
724
835
|
* TTL (ms) after which a native-frame lease is treated as a miss and reclaimed.
|
|
725
836
|
* The backstop that bounds in-flight RAM even if the explicit `releaseNativeLease`
|
|
@@ -727,25 +838,19 @@ var NATIVE_LEASE_BUDGET_BYTES = (() => {
|
|
|
727
838
|
* inference + the cross-process inference-result hop to hub post-analysis +
|
|
728
839
|
* tracking + the tRPC crop round-trip back — on a busy camera with many
|
|
729
840
|
* concurrent tracks that is well past the old 500 ms (which the busiest cameras'
|
|
730
|
-
* subject crops outran, missing to the ≤640 fallback).
|
|
841
|
+
* subject crops outran, missing to the ≤640 fallback). Default 1200 ms; still
|
|
731
842
|
* bounded by the per-session {@link NATIVE_LEASE_BUDGET_BYTES} ceiling and the
|
|
732
843
|
* {@link NATIVE_LEASE_ACTIVITY_WINDOW_MS} demand gate, so idle cameras retain
|
|
733
|
-
* nothing.
|
|
844
|
+
* nothing.
|
|
734
845
|
*/
|
|
735
|
-
var NATIVE_LEASE_TTL_MS =
|
|
736
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS"]);
|
|
737
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1200;
|
|
738
|
-
})();
|
|
846
|
+
var NATIVE_LEASE_TTL_MS = NATIVE_LEASE_KNOBS.values.ttlMs;
|
|
739
847
|
/**
|
|
740
848
|
* Demand window (ms) for the lease-capture {@link LeaseActivityGate}: eager
|
|
741
849
|
* per-frame native downloads run only within this window of the last
|
|
742
|
-
* native-crop request (or dial start). `0` disables
|
|
743
|
-
* eager capture).
|
|
850
|
+
* native-crop request (or dial start). `0` disables the GATE (legacy always-on
|
|
851
|
+
* eager capture) — it does not disable retention. Default 15000.
|
|
744
852
|
*/
|
|
745
|
-
var NATIVE_LEASE_ACTIVITY_WINDOW_MS =
|
|
746
|
-
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_ACTIVITY_MS"]);
|
|
747
|
-
return Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 15e3;
|
|
748
|
-
})();
|
|
853
|
+
var NATIVE_LEASE_ACTIVITY_WINDOW_MS = NATIVE_LEASE_KNOBS.values.activityMs;
|
|
749
854
|
/**
|
|
750
855
|
* `pinnedRgbCrop` — pinned-color RGB24 native-crop retention. DEFAULT **ON**
|
|
751
856
|
* (2026-07-20 rollout); set `CAMSTACK_SESSION_PINNED_RGB_CROP=0` to opt out.
|
|
@@ -1036,6 +1141,15 @@ var DecodeWorkerChild = class {
|
|
|
1036
1141
|
*/
|
|
1037
1142
|
leaseGate = new LeaseActivityGate(NATIVE_LEASE_ACTIVITY_WINDOW_MS);
|
|
1038
1143
|
/**
|
|
1144
|
+
* WHICH delivered frames are worth a native lease — the runner marks the ones
|
|
1145
|
+
* it admitted to inference (`markInferred`), and in `inferred` mode only
|
|
1146
|
+
* those are retained. Also the premise counter: it tallies every crop request
|
|
1147
|
+
* naming a frame that was never marked, in BOTH modes, so the assumption the
|
|
1148
|
+
* filter rests on is measured on live traffic rather than argued. See
|
|
1149
|
+
* `lease-admission-ledger.ts`.
|
|
1150
|
+
*/
|
|
1151
|
+
admission = new LeaseAdmissionLedger({ mode: NATIVE_LEASE_KNOBS.values.admission });
|
|
1152
|
+
/**
|
|
1039
1153
|
* DEDICATED full-frame GPU→system download filtergraph
|
|
1040
1154
|
* (`scale_<be>=iw:ih,hwdownload,format=nv12,format=yuv420p`) used ONLY to
|
|
1041
1155
|
* materialize a HW surface into a leasable software frame at NATIVE
|
|
@@ -1147,6 +1261,9 @@ var DecodeWorkerChild = class {
|
|
|
1147
1261
|
case "releaseNativeLease":
|
|
1148
1262
|
this.leaseStore.release(message.frameId);
|
|
1149
1263
|
return;
|
|
1264
|
+
case "markInferred":
|
|
1265
|
+
this.admission.mark(message.frameId);
|
|
1266
|
+
return;
|
|
1150
1267
|
case "stop":
|
|
1151
1268
|
this.handleStop();
|
|
1152
1269
|
return;
|
|
@@ -1206,7 +1323,7 @@ var DecodeWorkerChild = class {
|
|
|
1206
1323
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
1207
1324
|
const rssMb = Math.round(process.memoryUsage().rss / 1048576);
|
|
1208
1325
|
const ageS = Math.round((now - this.startedAt) / 1e3);
|
|
1209
|
-
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1326
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1210
1327
|
this.lastMetricsAt = now;
|
|
1211
1328
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
1212
1329
|
if (!this.warnedLarge && rssMb >= WORKER_RSS_WARN_MB) {
|
|
@@ -1221,6 +1338,7 @@ var DecodeWorkerChild = class {
|
|
|
1221
1338
|
}
|
|
1222
1339
|
this.started = true;
|
|
1223
1340
|
this.deviceId = source.deviceId;
|
|
1341
|
+
this.emitStderr(`decode-worker-child: ${formatNativeLeaseKnobs(NATIVE_LEASE_KNOBS)}\n`, "info");
|
|
1224
1342
|
this.sessionFormat = opts.format ?? "rgb";
|
|
1225
1343
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
1226
1344
|
this.lastMetricsAt = Date.now();
|
|
@@ -1271,6 +1389,7 @@ var DecodeWorkerChild = class {
|
|
|
1271
1389
|
*/
|
|
1272
1390
|
handleNativeCrop(requestId, frameId, bbox, maxWidth) {
|
|
1273
1391
|
this.leaseGate.arm();
|
|
1392
|
+
if (this.admission.noteCropRequest(frameId)) this.emitStderr(`decode-worker-child: native crop named an UNINFERRED frame (frameId ${frameId}) — the 'inferred' lease-admission premise does not hold on this path; total=${this.admission.unmarkedCrops}\n`);
|
|
1274
1393
|
const frame = this.leaseStore.get(frameId)?.frame ?? this.resolveRetainedFrame(frameId);
|
|
1275
1394
|
if (!frame) {
|
|
1276
1395
|
this.nativeCropMisses++;
|
|
@@ -1340,10 +1459,59 @@ var DecodeWorkerChild = class {
|
|
|
1340
1459
|
* is leased directly. A download failure still consumes ownership (freed
|
|
1341
1460
|
* here) so the slot never double-frees — the crop simply misses → fallback.
|
|
1342
1461
|
* - Lease DISABLED (`budget 0`): fall back to today's tiny GPU {@link nativeRing}.
|
|
1462
|
+
*
|
|
1463
|
+
* ## LEASE ADMISSION — what actually gets stored
|
|
1464
|
+
*
|
|
1465
|
+
* Three conditions decide it, and only the third is per-frame:
|
|
1466
|
+
* 1. the lease is enabled (`budgetMb > 0`);
|
|
1467
|
+
* 2. {@link leaseGate} is inside its demand window (`activityMs` since the
|
|
1468
|
+
* last native-crop request or the dial);
|
|
1469
|
+
* 3. {@link admission} accepts the frame — everything in `all` mode, only
|
|
1470
|
+
* frames the runner MARKED as having reached inference in `inferred`
|
|
1471
|
+
* mode (`nativeLeaseAdmission`).
|
|
1472
|
+
*
|
|
1473
|
+
* What reaches this function at all is the {@link FrameSlot} lifecycle, not a
|
|
1474
|
+
* quality filter. `retainSuperseded` is called only from
|
|
1475
|
+
* `FrameSlot.reserveSlot()` (`frame-slot.ts`), on the frame the PREVIOUS
|
|
1476
|
+
* `pull` delivered to the parent, at the moment the next `pull` supersedes it.
|
|
1477
|
+
* So:
|
|
1478
|
+
* - frames dropped by the `minIntervalMs` fps throttle never enter the slot
|
|
1479
|
+
* and are never offered;
|
|
1480
|
+
* - frames superseded in the slot before any `pull` took them (latest-wins)
|
|
1481
|
+
* are freed by `publish` and never offered;
|
|
1482
|
+
* - every DELIVERED frame is offered exactly once, and then rule 3 decides.
|
|
1483
|
+
*
|
|
1484
|
+
* ## Why `inferred` is safe, and how that is checked rather than assumed
|
|
1485
|
+
*
|
|
1486
|
+
* A native crop names a `frameId`, and the only way a `frameId` leaves the
|
|
1487
|
+
* runner is on a `FrameHandle` riding an INFERENCE RESULT — post-analysis
|
|
1488
|
+
* reaches every one of its handles through `processFrame`, whose only
|
|
1489
|
+
* handle-bearing caller is `handleInferenceResult`. A frame the runner never
|
|
1490
|
+
* enqueued is therefore a frame nobody can ask for. The counter that keeps
|
|
1491
|
+
* this honest is `leaseUnmarkedCrops` on the metrics line: it counts crop
|
|
1492
|
+
* requests naming an unmarked frame in BOTH modes, so the premise is a
|
|
1493
|
+
* production number, not a reading of the call graph.
|
|
1494
|
+
*
|
|
1495
|
+
* The NEWEST delivered frame is never at risk either way: it is the slot's
|
|
1496
|
+
* reserved frame and {@link handleNativeCrop} resolves it through
|
|
1497
|
+
* `FrameSlot.toBuffer`, never through the lease.
|
|
1498
|
+
*
|
|
1499
|
+
* ## What it costs / saves
|
|
1500
|
+
*
|
|
1501
|
+
* One 4K frame is ~24.9 MB on the default pinned-RGB24 lease path
|
|
1502
|
+
* ({@link PINNED_RGB_CROP}), ~12.4 MB as YUV420P. At the ~25 delivered fps of
|
|
1503
|
+
* `all` mode that is ~620 MB/s of GPU→RAM download while the gate is open and
|
|
1504
|
+
* a steady-state resident set of roughly `frameBytes × deliveredFps ×
|
|
1505
|
+
* ttlSeconds`. `inferred` cuts both by the governor's admit ratio — ~5 of 25
|
|
1506
|
+
* frames on a two-plane camera, so ~80%. Prompt `releaseNativeLease` reclaims
|
|
1507
|
+
* earlier still, so the observed figure is lower than the ceiling; the
|
|
1508
|
+
* ceiling is what the budget knob has to survive. See
|
|
1509
|
+
* `docs/design/decode-path.md` → "Lease admission".
|
|
1343
1510
|
*/
|
|
1344
1511
|
captureNativeFrame(frameId, frame) {
|
|
1345
1512
|
if (!this.leaseStore.enabled) return this.nativeRing.retain(frameId, frame);
|
|
1346
1513
|
if (!this.leaseGate.active) return false;
|
|
1514
|
+
if (!this.admission.admit(frameId)) return false;
|
|
1347
1515
|
if (frame.isHwFrame()) {
|
|
1348
1516
|
const software = this.downloadToSoftware(frame);
|
|
1349
1517
|
frame.free();
|
|
@@ -1931,14 +2099,19 @@ var DecodeWorkerChild = class {
|
|
|
1931
2099
|
* output. Public so the module-level process handlers — which only hold the
|
|
1932
2100
|
* `worker` instance — emit device-tagged lines too. Callers pass the line
|
|
1933
2101
|
* INCLUDING its trailing newline (unchanged from the prior direct writes).
|
|
2102
|
+
*
|
|
2103
|
+
* `level` overrides the derived severity for the few lines whose importance
|
|
2104
|
+
* is not inferable from their words — the lease-knob line states an
|
|
2105
|
+
* operator's configuration taking effect and would otherwise sit at `debug`,
|
|
2106
|
+
* where a setting that failed to arrive is indistinguishable from silence.
|
|
1934
2107
|
*/
|
|
1935
|
-
emitStderr(line) {
|
|
2108
|
+
emitStderr(line, level) {
|
|
1936
2109
|
const tag = this.deviceId === null ? "[dev:?]" : `[dev:${this.deviceId}]`;
|
|
1937
2110
|
process.stderr.write(`${tag} ${line}`);
|
|
1938
2111
|
try {
|
|
1939
2112
|
this.send({
|
|
1940
2113
|
kind: "log",
|
|
1941
|
-
level: logLevelForLine(line),
|
|
2114
|
+
level: level ?? logLevelForLine(line),
|
|
1942
2115
|
message: `${tag} ${line.trimEnd()}`
|
|
1943
2116
|
});
|
|
1944
2117
|
} catch {}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-
|
|
1
|
+
import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B3GxBnXM.mjs";
|
|
2
2
|
import { a as d, i as f, n as p, o as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
|
|
3
3
|
import { n as _, r as v, t as y } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
|
|
4
4
|
import { n as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
|
|
5
|
-
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-
|
|
5
|
+
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CT2bGWqM.mjs";
|
|
6
6
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
7
7
|
var C = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), w = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), T = (e) => {
|
|
8
8
|
let t = w(e);
|