@camstack/addon-pipeline 1.2.317 → 1.2.320
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 +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-CILrD3sC.mjs → default-detection-model-B-iMtrXt.mjs} +853 -14
- package/dist/{default-detection-model-DdIJSdQB.js → default-detection-model-CO4pZxbd.js} +864 -13
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-3isAnyIX.mjs → dist-BK-pWZzI.mjs} +27 -7
- package/dist/{dist-DXU3Fu7j.js → dist-Bce5_ocm.js} +27 -7
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-PY3N7_ym.mjs → node-DQsiK6Vt.mjs} +1 -1
- package/dist/{node-DEpHS48Q.js → node-UJwLVGTT.js} +1 -1
- package/dist/pipeline-runner/index.js +13 -821
- package/dist/pipeline-runner/index.mjs +12 -820
- package/dist/{process-memory-DE0M2AiK.mjs → process-memory-Cn3QEo37.mjs} +1 -1
- package/dist/{process-memory-DAGs0yZp.js → process-memory-DR7jI54j.js} +1 -1
- package/dist/recorder/index.js +2 -2
- package/dist/recorder/index.mjs +2 -2
- package/dist/{segment-demux-js-k3D5kt3W.mjs → segment-demux-js-CTMbCGRL.mjs} +1 -1
- package/dist/{segment-demux-js-BZhn3qOl.js → segment-demux-js-CbazlBOP.js} +1 -1
- package/dist/session-decode/decode-worker-main.js +194 -13
- package/dist/session-decode/decode-worker-main.mjs +194 -13
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DqP6Ev3L.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DP4h-J_V.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BAYDyjOG.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-BRiacIOY.mjs → hostInit-CnrH7DvQ.mjs} +2 -2
- package/dist/stream-broker/index.js +19 -8
- package/dist/stream-broker/index.mjs +19 -8
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CVT3OmxC.mjs +0 -26
|
@@ -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-Bce5_ocm.js");
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
node_fs = require_chunk.__toESM(node_fs);
|
|
5
5
|
let node_child_process = require("node:child_process");
|
package/dist/recorder/index.js
CHANGED
|
@@ -3,8 +3,8 @@ 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-
|
|
6
|
+
const require_dist = require("../dist-Bce5_ocm.js");
|
|
7
|
+
const require_node = require("../node-UJwLVGTT.js");
|
|
8
8
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
9
9
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
10
10
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $t as stripRetiredBandPreBufferSec, An as sleep$1, B as VISIT_MERGE_GAP_MS, Bn as record, Bt as recordingExportCapability, C as ExportRecordSchema, Dt as migrateLegacyDeviceSignalConfig, En as nodePin, Et as memoizePinnedFfmpeg, F as RECORDING_SOURCE_CAMSTACK_ADDON, Gt as resolveLocationMode, I as RecordingConfigSchema, K as batteryCapability, L as RecordingSignalStatusSchema, Ln as number, Mn as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nn as boolean, Ot as modeMayWrite, P as RECORDING_SOURCE_CAMSTACK, Rn as object, Rt as recordingArchiveCapability, Sn as hydrateSchema, Tt as mayWriteToLocation, Un as EventCategory, Ut as resolveCreateExportSubject, Vn as string, Vt as recordingSignalCapability, X as clipExportFailureOf, Xt as storageEvictableCapability, Y as clipExportFailure, Zt as storageOccupancyCapability, _ as DEFAULT_EVENTS_BAND_BUFFER_SEC, b as EVENT_OWNER_TYPES, bn as defineCustomActions, bt as loadContributionCapability, dn as DeviceType, dt as exportSubjectOf, fn as errMsg$1, jn as _enum, k as OpsLogEntrySchema, kn as selectAssignedProfileSlots, lt as evictionPolicyForMode, mn as BaseAddon, mt as gbToBytes, o as BatteryStatusSchema, qt as resolveRecordingProfiles, rt as deriveRecordingMode, s as CLIP_BROKER_PACED_RATES, sn as evaluateSensorEdge, tn as videoclipsCapability, tt as deriveBatteryPresence, un as DeviceFeature, ut as evictionPolicyOfLocation, wt as mayReadLocation, x as EVENT_PAD_MS, yn as customAction, yt as isFirstLevelMacroClass, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as preprocess, zt as recordingCapability } from "../dist-
|
|
2
|
-
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
1
|
+
import { $t as stripRetiredBandPreBufferSec, An as sleep$1, B as VISIT_MERGE_GAP_MS, Bn as record, Bt as recordingExportCapability, C as ExportRecordSchema, Dt as migrateLegacyDeviceSignalConfig, En as nodePin, Et as memoizePinnedFfmpeg, F as RECORDING_SOURCE_CAMSTACK_ADDON, Gt as resolveLocationMode, I as RecordingConfigSchema, K as batteryCapability, L as RecordingSignalStatusSchema, Ln as number, Mn as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nn as boolean, Ot as modeMayWrite, P as RECORDING_SOURCE_CAMSTACK, Rn as object, Rt as recordingArchiveCapability, Sn as hydrateSchema, Tt as mayWriteToLocation, Un as EventCategory, Ut as resolveCreateExportSubject, Vn as string, Vt as recordingSignalCapability, X as clipExportFailureOf, Xt as storageEvictableCapability, Y as clipExportFailure, Zt as storageOccupancyCapability, _ as DEFAULT_EVENTS_BAND_BUFFER_SEC, b as EVENT_OWNER_TYPES, bn as defineCustomActions, bt as loadContributionCapability, dn as DeviceType, dt as exportSubjectOf, fn as errMsg$1, jn as _enum, k as OpsLogEntrySchema, kn as selectAssignedProfileSlots, lt as evictionPolicyForMode, mn as BaseAddon, mt as gbToBytes, o as BatteryStatusSchema, qt as resolveRecordingProfiles, rt as deriveRecordingMode, s as CLIP_BROKER_PACED_RATES, sn as evaluateSensorEdge, tn as videoclipsCapability, tt as deriveBatteryPresence, un as DeviceFeature, ut as evictionPolicyOfLocation, wt as mayReadLocation, x as EVENT_PAD_MS, yn as customAction, yt as isFirstLevelMacroClass, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as preprocess, zt as recordingCapability } from "../dist-BK-pWZzI.mjs";
|
|
2
|
+
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DQsiK6Vt.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
|
|
5
5
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { fn as errMsg } from "./dist-
|
|
2
|
+
import { fn as errMsg } from "./dist-BK-pWZzI.mjs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|
|
@@ -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-Bce5_ocm.js");
|
|
3
3
|
let node_child_process = require("node:child_process");
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|
|
@@ -512,9 +512,15 @@ var FrameSlot = class {
|
|
|
512
512
|
* native-quality media before any crop request has been seen;
|
|
513
513
|
* - `arm()` on every native-crop request — post-analysis snapshot crops and
|
|
514
514
|
* the detail plane re-arm it continuously while tracks are active;
|
|
515
|
+
* - `arm()` on every `markInferred` — the runner admitted the frame to
|
|
516
|
+
* detection and will lazy-`toBuffer` it after the next pull supersedes the
|
|
517
|
+
* reserved slot; without this, a quiet always-on camera closed the gate
|
|
518
|
+
* after `activityMs` and every marked supersede was freed
|
|
519
|
+
* (`toBuffer miss — released`);
|
|
515
520
|
* - after `windowMs` without demand it closes and per-frame downloads stop
|
|
516
|
-
* (a later crop request simply misses once — the
|
|
517
|
-
* detection-frame-crop fallback — and re-arms the gate
|
|
521
|
+
* for UNMARKED frames (a later crop request simply misses once — the
|
|
522
|
+
* caller's designed detection-frame-crop fallback — and re-arms the gate
|
|
523
|
+
* for the retry). A marked frame still captures when the gate is cold.
|
|
518
524
|
*
|
|
519
525
|
* `windowMs: 0` disables gating entirely (always active — the legacy eager
|
|
520
526
|
* behaviour).
|
|
@@ -642,6 +648,14 @@ var LeaseAdmissionLedger = class {
|
|
|
642
648
|
isMarked(frameId) {
|
|
643
649
|
return this.marked.has(frameId);
|
|
644
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Same keep rule as {@link admit}, without counting. Used to park a
|
|
653
|
+
* just-superseded frame for a late mark without burning an offered/declined
|
|
654
|
+
* sample that a later promote would double-count.
|
|
655
|
+
*/
|
|
656
|
+
wouldAdmit(frameId) {
|
|
657
|
+
return this.mode === "all" || this.marked.has(frameId);
|
|
658
|
+
}
|
|
645
659
|
/** The runner admitted `frameId` to its detection queue. */
|
|
646
660
|
mark(frameId) {
|
|
647
661
|
this.markedTotal++;
|
|
@@ -680,6 +694,103 @@ var LeaseAdmissionLedger = class {
|
|
|
680
694
|
}
|
|
681
695
|
};
|
|
682
696
|
//#endregion
|
|
697
|
+
//#region src/session-decode/pending-mark-hold.ts
|
|
698
|
+
/**
|
|
699
|
+
* One-frame grace for a late `markInferred` IPC.
|
|
700
|
+
*
|
|
701
|
+
* Admission for delivered frame N is decided when pull N+1 supersedes it
|
|
702
|
+
* (`FrameSlot.reserveSlot` → `retainSuperseded`). The contract says the runner
|
|
703
|
+
* sends `markInferred(N)` before that pull, and IPC order usually keeps it.
|
|
704
|
+
* When the mark lands AFTER the supersede, today's path has already freed N:
|
|
705
|
+
* a later lazy detection `toBuffer(N)` misses with `marked=true leaseFrames=0`
|
|
706
|
+
* — the reserved-slot one-frame margin only covers the NEWEST id, not an id
|
|
707
|
+
* already handed to retention and declined.
|
|
708
|
+
*
|
|
709
|
+
* This hold parks the just-superseded frame WITHOUT downloading it (no GPU→RAM
|
|
710
|
+
* burn for a frame that may never be marked). A matching mark takes it for
|
|
711
|
+
* lease capture; the next supersede (or `clear`) discards it. At most one
|
|
712
|
+
* frame per worker.
|
|
713
|
+
*/
|
|
714
|
+
var PendingMarkHold = class {
|
|
715
|
+
held = null;
|
|
716
|
+
/** Id currently parked, or `null`. */
|
|
717
|
+
get frameId() {
|
|
718
|
+
return this.held?.frameId ?? null;
|
|
719
|
+
}
|
|
720
|
+
/** 0 or 1 — exposed for metrics / tests. */
|
|
721
|
+
get size() {
|
|
722
|
+
return this.held ? 1 : 0;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Park `frame` awaiting a possible late mark. Frees any previous pending
|
|
726
|
+
* (that id lost the mark race). Takes ownership of `frame`.
|
|
727
|
+
*/
|
|
728
|
+
park(frameId, frame) {
|
|
729
|
+
this.discard();
|
|
730
|
+
this.held = {
|
|
731
|
+
frameId,
|
|
732
|
+
frame
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
/** Peek without taking — lazy `toBuffer` while still pending. */
|
|
736
|
+
get(frameId) {
|
|
737
|
+
if (!this.held || this.held.frameId !== frameId) return null;
|
|
738
|
+
return this.held.frame;
|
|
739
|
+
}
|
|
740
|
+
/** Take ownership if `frameId` matches; otherwise leave pending alone. */
|
|
741
|
+
take(frameId) {
|
|
742
|
+
if (!this.held || this.held.frameId !== frameId) return null;
|
|
743
|
+
const frame = this.held.frame;
|
|
744
|
+
this.held = null;
|
|
745
|
+
return frame;
|
|
746
|
+
}
|
|
747
|
+
/** Free the pending frame if any. Returns whether something was freed. */
|
|
748
|
+
discard() {
|
|
749
|
+
if (!this.held) return false;
|
|
750
|
+
this.held.frame.free();
|
|
751
|
+
this.held = null;
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
/** Teardown alias. */
|
|
755
|
+
clear() {
|
|
756
|
+
this.discard();
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
/** Named refusal when the per-dial create cap is hit. */
|
|
760
|
+
var LEASE_DOWNLOAD_REBUILD_CAP_REASON = "lease-download-rebuild-cap";
|
|
761
|
+
/**
|
|
762
|
+
* Counts lease-download filter creates for one dial and refuses past the bound.
|
|
763
|
+
* Reset on every fresh dial (alongside {@link LeaseActivityGate.reset}).
|
|
764
|
+
*/
|
|
765
|
+
var LeaseDownloadRebuildCap = class {
|
|
766
|
+
maxCreatesPerDial;
|
|
767
|
+
creates = 0;
|
|
768
|
+
constructor(maxCreatesPerDial = 2) {
|
|
769
|
+
this.maxCreatesPerDial = maxCreatesPerDial;
|
|
770
|
+
}
|
|
771
|
+
/** Metric: how many `FilterAPI.create` calls this dial has already paid. */
|
|
772
|
+
get createsThisDial() {
|
|
773
|
+
return this.creates;
|
|
774
|
+
}
|
|
775
|
+
/** Whether another create is still inside the dial bound. */
|
|
776
|
+
canCreate() {
|
|
777
|
+
return this.creates < this.maxCreatesPerDial;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Record a create that is about to happen. Throws
|
|
781
|
+
* {@link LEASE_DOWNLOAD_REBUILD_CAP_REASON} if the dial is already at the cap
|
|
782
|
+
* — the caller must not call `FilterAPI.create` after that.
|
|
783
|
+
*/
|
|
784
|
+
recordCreate() {
|
|
785
|
+
if (this.creates >= this.maxCreatesPerDial) throw new Error(LEASE_DOWNLOAD_REBUILD_CAP_REASON);
|
|
786
|
+
this.creates++;
|
|
787
|
+
}
|
|
788
|
+
/** Fresh dial: the create budget starts over with a new `hw_frames_ctx`. */
|
|
789
|
+
resetForDial() {
|
|
790
|
+
this.creates = 0;
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
//#endregion
|
|
683
794
|
//#region src/session-decode/media-clock.ts
|
|
684
795
|
/**
|
|
685
796
|
* Convert a container pts to milliseconds.
|
|
@@ -2618,6 +2729,13 @@ var DecodeWorkerChild = class {
|
|
|
2618
2729
|
*/
|
|
2619
2730
|
admission = new LeaseAdmissionLedger({ mode: UNSTARTED_LEASE_KNOBS.values.admission });
|
|
2620
2731
|
/**
|
|
2732
|
+
* One-frame park for a late `markInferred` after supersede. Holds the native
|
|
2733
|
+
* surface WITHOUT downloading until the mark arrives or the next supersede
|
|
2734
|
+
* discards it — closes `toBuffer miss — released` with `marked=true` when
|
|
2735
|
+
* the mark IPC loses the race with pull N+1. See `pending-mark-hold.ts`.
|
|
2736
|
+
*/
|
|
2737
|
+
pendingMark = new PendingMarkHold();
|
|
2738
|
+
/**
|
|
2621
2739
|
* The knobs this session runs under — `null` until `start` hands them over.
|
|
2622
2740
|
* The stores above are REBUILT from them at that moment (they are empty then:
|
|
2623
2741
|
* nothing is captured before a dial), and never again: a knob that changes
|
|
@@ -2638,6 +2756,12 @@ var DecodeWorkerChild = class {
|
|
|
2638
2756
|
/** Root-cause instrumentation for the sporadic EIO/EINVAL add-frame failures (task: lease-filter EIO). */
|
|
2639
2757
|
leaseDownloadFilterUses = 0;
|
|
2640
2758
|
leaseDownloadFilterBuiltAt = 0;
|
|
2759
|
+
/**
|
|
2760
|
+
* Per-dial create budget for the lease-download filtergraph (S1 / N100 OOM
|
|
2761
|
+
* unit). Metric {@link LeaseDownloadRebuildCap.createsThisDial} is emitted as
|
|
2762
|
+
* `leaseDownloadFilterCreates` on the forensics + metrics lines.
|
|
2763
|
+
*/
|
|
2764
|
+
leaseDownloadRebuildCap = new LeaseDownloadRebuildCap();
|
|
2641
2765
|
dialStartedAt = 0;
|
|
2642
2766
|
/**
|
|
2643
2767
|
* `HardwareContext.create` cost, measured once per worker.
|
|
@@ -2660,9 +2784,9 @@ var DecodeWorkerChild = class {
|
|
|
2660
2784
|
* successful uses — the decoder re-creates its hw_frames_ctx shortly after
|
|
2661
2785
|
* dial start (post-probe pool re-init), stranding the just-built filter on
|
|
2662
2786
|
* the old pool. One rebuild per dial recovers that case (the ctx has
|
|
2663
|
-
* settled by the next capture); a SECOND failure
|
|
2664
|
-
*
|
|
2665
|
-
* that OOM'd the N100).
|
|
2787
|
+
* settled by the next capture); a SECOND failure starts the cooldown, and
|
|
2788
|
+
* {@link leaseDownloadRebuildCap} refuses a third `FilterAPI.create` for
|
|
2789
|
+
* the dial (the OOM unit — never the per-frame rebuild that OOM'd the N100).
|
|
2666
2790
|
*/
|
|
2667
2791
|
leaseDownloadFailuresThisDial = 0;
|
|
2668
2792
|
/** Throttled native-crop hit/miss counters, emitted with the throughput line. */
|
|
@@ -2830,6 +2954,8 @@ var DecodeWorkerChild = class {
|
|
|
2830
2954
|
return;
|
|
2831
2955
|
case "markInferred":
|
|
2832
2956
|
this.admission.mark(message.frameId);
|
|
2957
|
+
this.leaseGate.arm();
|
|
2958
|
+
this.promotePendingMark(message.frameId);
|
|
2833
2959
|
return;
|
|
2834
2960
|
case "drain":
|
|
2835
2961
|
this.handleDrain();
|
|
@@ -2949,7 +3075,7 @@ var DecodeWorkerChild = class {
|
|
|
2949
3075
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
2950
3076
|
const rssMb = Math.round(process.memoryUsage().rss / 1048576);
|
|
2951
3077
|
const ageS = Math.round((now - this.startedAt) / 1e3);
|
|
2952
|
-
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:${this.lease?.values.admission ?? "unstarted"}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, leaseReleased:${this.leaseReleased}, leaseReleaseMisses:${this.leaseReleaseMisses}, leaseReleasedEarly:${this.leaseReleasedEarly}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMissesResolvedByWait:${this.tileMissesResolvedByWait}, tileWaitTimeouts:${this.tileWaitTimeouts}, cutLatencyMsP50:${formatLatency(this.pendingCuts.latencyMs(50))}, cutLatencyMsP95:${formatLatency(this.pendingCuts.latencyMs(95))}, cutsInFlight:${this.pendingCuts.inFlight}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, scenesCut:${this.scenesCut}, sceneHits:${this.sceneHits}, sceneRefused:${this.sceneRefused}, sceneUnclassified:${this.sceneUnclassified}, sceneEvicted:${this.sceneStore.evictedCount}, sceneMb:${Math.round(this.sceneStore.totalBytes / 1048576)}, sceneFrames:${this.sceneStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
3078
|
+
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}, leaseDownloadFilterCreates:${this.leaseDownloadRebuildCap.createsThisDial}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${this.lease?.values.admission ?? "unstarted"}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, leaseReleased:${this.leaseReleased}, leaseReleaseMisses:${this.leaseReleaseMisses}, leaseReleasedEarly:${this.leaseReleasedEarly}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMissesResolvedByWait:${this.tileMissesResolvedByWait}, tileWaitTimeouts:${this.tileWaitTimeouts}, cutLatencyMsP50:${formatLatency(this.pendingCuts.latencyMs(50))}, cutLatencyMsP95:${formatLatency(this.pendingCuts.latencyMs(95))}, cutsInFlight:${this.pendingCuts.inFlight}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, scenesCut:${this.scenesCut}, sceneHits:${this.sceneHits}, sceneRefused:${this.sceneRefused}, sceneUnclassified:${this.sceneUnclassified}, sceneEvicted:${this.sceneStore.evictedCount}, sceneMb:${Math.round(this.sceneStore.totalBytes / 1048576)}, sceneFrames:${this.sceneStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
2953
3079
|
this.lastMetricsAt = now;
|
|
2954
3080
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
2955
3081
|
if (!this.warnedLarge && rssMb >= WORKER_RSS_WARN_MB) {
|
|
@@ -2970,6 +3096,7 @@ var DecodeWorkerChild = class {
|
|
|
2970
3096
|
this.sceneStore = buildTileStore(lease.values.sceneBudgetMb);
|
|
2971
3097
|
this.leaseGate = new LeaseActivityGate(lease.values.activityMs);
|
|
2972
3098
|
this.admission = new LeaseAdmissionLedger({ mode: lease.values.admission });
|
|
3099
|
+
this.pendingMark.clear();
|
|
2973
3100
|
this.emitStderr(`decode-worker-child: ${require_worker_protocol.formatNativeLeaseKnobs(lease)}\n`, "info");
|
|
2974
3101
|
this.sessionFormat = opts.format ?? "rgb";
|
|
2975
3102
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
@@ -3018,7 +3145,7 @@ var DecodeWorkerChild = class {
|
|
|
3018
3145
|
}
|
|
3019
3146
|
}
|
|
3020
3147
|
async handleToBuffer(frameId, opts) {
|
|
3021
|
-
const frame = this.leaseStore.get(frameId)?.frame ?? this.frames.toBuffer(frameId);
|
|
3148
|
+
const frame = this.leaseStore.get(frameId)?.frame ?? this.pendingMark.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
3022
3149
|
this.settlePreRoll(frameId, frame ? "read" : "miss");
|
|
3023
3150
|
if (!frame) {
|
|
3024
3151
|
this.sendError(describeToBufferMiss({
|
|
@@ -3164,9 +3291,9 @@ var DecodeWorkerChild = class {
|
|
|
3164
3291
|
if (this.tileStore.get(frameId).length === 0 && this.sceneStore.get(frameId).length === 0) return `frame ${frameId} released and no retained tile for this frame`;
|
|
3165
3292
|
return `frame ${frameId} released and no retained subject or scene tile contains the requested rectangle`;
|
|
3166
3293
|
}
|
|
3167
|
-
/** The GPU-ring or reserved-slot frame for `frameId
|
|
3294
|
+
/** The GPU-ring, pending-mark, or reserved-slot frame for `frameId`. */
|
|
3168
3295
|
resolveRetainedFrame(frameId) {
|
|
3169
|
-
return this.nativeRing.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
3296
|
+
return this.nativeRing.get(frameId) ?? this.pendingMark.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
3170
3297
|
}
|
|
3171
3298
|
/**
|
|
3172
3299
|
* The runner has this frame's detections. Cut one native TILE per subject and
|
|
@@ -3420,6 +3547,11 @@ var DecodeWorkerChild = class {
|
|
|
3420
3547
|
* frames the runner MARKED as having reached inference in `inferred`
|
|
3421
3548
|
* mode (`nativeLeaseAdmission`).
|
|
3422
3549
|
*
|
|
3550
|
+
* When (2) or (3) would decline, the frame is PARKED in {@link pendingMark}
|
|
3551
|
+
* for one supersede — a late `markInferred` can still promote it. The next
|
|
3552
|
+
* supersede discards an unmarked pending. No GPU download until promote or
|
|
3553
|
+
* lease, so quiet cameras still avoid the per-frame bandwidth burn.
|
|
3554
|
+
*
|
|
3423
3555
|
* What reaches this function at all is the {@link FrameSlot} lifecycle, not a
|
|
3424
3556
|
* quality filter. `retainSuperseded` is called only from
|
|
3425
3557
|
* `FrameSlot.reserveSlot()` (`frame-slot.ts`), on the frame the PREVIOUS
|
|
@@ -3444,7 +3576,8 @@ var DecodeWorkerChild = class {
|
|
|
3444
3576
|
*
|
|
3445
3577
|
* The NEWEST delivered frame is never at risk either way: it is the slot's
|
|
3446
3578
|
* reserved frame and {@link handleNativeCrop} resolves it through
|
|
3447
|
-
* `FrameSlot.toBuffer`, never through the lease.
|
|
3579
|
+
* `FrameSlot.toBuffer`, never through the lease. A late mark for the PREVIOUS
|
|
3580
|
+
* frame is the pending-mark hold's job — the reserved margin does not cover it.
|
|
3448
3581
|
*
|
|
3449
3582
|
* ## What it costs / saves
|
|
3450
3583
|
*
|
|
@@ -3460,17 +3593,57 @@ var DecodeWorkerChild = class {
|
|
|
3460
3593
|
*/
|
|
3461
3594
|
captureNativeFrame(frameId, frame) {
|
|
3462
3595
|
if (!this.leaseStore.enabled) {
|
|
3596
|
+
this.flushPendingMark();
|
|
3463
3597
|
this.settlePreRoll(frameId, "released");
|
|
3464
3598
|
return this.nativeRing.retain(frameId, frame);
|
|
3465
3599
|
}
|
|
3466
|
-
if (!this.leaseGate.active)
|
|
3600
|
+
if (!this.leaseGate.active && !this.admission.isMarked(frameId)) return this.parkPendingMark(frameId, frame);
|
|
3601
|
+
if (!this.admission.wouldAdmit(frameId)) return this.parkPendingMark(frameId, frame);
|
|
3602
|
+
this.flushPendingMark();
|
|
3603
|
+
if (!this.admission.admit(frameId)) {
|
|
3467
3604
|
this.settlePreRoll(frameId, "released");
|
|
3468
3605
|
return false;
|
|
3469
3606
|
}
|
|
3607
|
+
return this.leaseNativeFrame(frameId, frame);
|
|
3608
|
+
}
|
|
3609
|
+
/**
|
|
3610
|
+
* Discard a pending mark that lost the race (never marked before the next
|
|
3611
|
+
* supersede). Counts as an offered decline so metrics still see the delivery.
|
|
3612
|
+
*/
|
|
3613
|
+
flushPendingMark() {
|
|
3614
|
+
const id = this.pendingMark.frameId;
|
|
3615
|
+
if (id === null) return;
|
|
3616
|
+
this.pendingMark.discard();
|
|
3617
|
+
this.admission.admit(id);
|
|
3618
|
+
this.settlePreRoll(id, "released");
|
|
3619
|
+
}
|
|
3620
|
+
/** Park a just-superseded frame awaiting a possible late `markInferred`. */
|
|
3621
|
+
parkPendingMark(frameId, frame) {
|
|
3622
|
+
this.flushPendingMark();
|
|
3623
|
+
this.pendingMark.park(frameId, frame);
|
|
3624
|
+
return true;
|
|
3625
|
+
}
|
|
3626
|
+
/**
|
|
3627
|
+
* Promote a parked frame into the lease after a late mark. No-op when the
|
|
3628
|
+
* pending id does not match (mark arrived in time and the frame was already
|
|
3629
|
+
* leased, or a newer supersede already discarded it).
|
|
3630
|
+
*/
|
|
3631
|
+
promotePendingMark(frameId) {
|
|
3632
|
+
const frame = this.pendingMark.take(frameId);
|
|
3633
|
+
if (!frame) return;
|
|
3470
3634
|
if (!this.admission.admit(frameId)) {
|
|
3635
|
+
frame.free();
|
|
3471
3636
|
this.settlePreRoll(frameId, "released");
|
|
3472
|
-
return
|
|
3637
|
+
return;
|
|
3473
3638
|
}
|
|
3639
|
+
this.leaseNativeFrame(frameId, frame);
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Download (if HW) and put into {@link leaseStore}. Consumes ownership of
|
|
3643
|
+
* `frame` whether or not the download succeeds (returns `true` either way
|
|
3644
|
+
* so the slot / pending hold never double-frees).
|
|
3645
|
+
*/
|
|
3646
|
+
leaseNativeFrame(frameId, frame) {
|
|
3474
3647
|
if (frame.isHwFrame()) {
|
|
3475
3648
|
const software = this.downloadToSoftware(frame);
|
|
3476
3649
|
frame.free();
|
|
@@ -3502,7 +3675,12 @@ var DecodeWorkerChild = class {
|
|
|
3502
3675
|
for (let i = 1; i < outputs.length; i++) outputs[i]?.free();
|
|
3503
3676
|
return first;
|
|
3504
3677
|
} catch (err) {
|
|
3505
|
-
|
|
3678
|
+
const reason = errMessage(err);
|
|
3679
|
+
if (reason === "lease-download-rebuild-cap") {
|
|
3680
|
+
this.emitStderr(`decode-worker-child: native lease download refused — ${LEASE_DOWNLOAD_REBUILD_CAP_REASON} [forensics: frame=${frame.width}x${frame.height} fmt=${frame.format} hw=${frame.isHwFrame()} filterUses=${this.leaseDownloadFilterUses} leaseDownloadFilterCreates=${this.leaseDownloadRebuildCap.createsThisDial} dialAgeMs=${Date.now() - this.dialStartedAt} decoded=${this.framesDecoded}]\n`);
|
|
3681
|
+
return null;
|
|
3682
|
+
}
|
|
3683
|
+
this.emitStderr(`decode-worker-child: native lease download failed — ${reason} [forensics: frame=${frame.width}x${frame.height} fmt=${frame.format} hw=${frame.isHwFrame()} filterUses=${this.leaseDownloadFilterUses} filterAgeMs=${Date.now() - this.leaseDownloadFilterBuiltAt} leaseDownloadFilterCreates=${this.leaseDownloadRebuildCap.createsThisDial} dialAgeMs=${Date.now() - this.dialStartedAt} decoded=${this.framesDecoded}]\n`);
|
|
3506
3684
|
this.native.run("nativeLeaseDownloadFilter", () => this.nativeLeaseDownloadFilter?.close());
|
|
3507
3685
|
this.nativeLeaseDownloadFilter = null;
|
|
3508
3686
|
this.nativeLeaseDownloadFilterKey = "";
|
|
@@ -3531,6 +3709,7 @@ var DecodeWorkerChild = class {
|
|
|
3531
3709
|
this.leaseDownloadFilterUses++;
|
|
3532
3710
|
return cached;
|
|
3533
3711
|
}
|
|
3712
|
+
this.leaseDownloadRebuildCap.recordCreate();
|
|
3534
3713
|
this.native.run("nativeLeaseDownloadFilter", () => this.nativeLeaseDownloadFilter?.close());
|
|
3535
3714
|
this.nativeLeaseDownloadFilter = null;
|
|
3536
3715
|
this.nativeLeaseDownloadFilterKey = "";
|
|
@@ -3735,6 +3914,7 @@ var DecodeWorkerChild = class {
|
|
|
3735
3914
|
this.dialStartedAt = Date.now();
|
|
3736
3915
|
this.dialSeq++;
|
|
3737
3916
|
this.leaseDownloadFailuresThisDial = 0;
|
|
3917
|
+
this.leaseDownloadRebuildCap.resetForDial();
|
|
3738
3918
|
this.abortController = new AbortController();
|
|
3739
3919
|
const opened = await openPullDemuxer({
|
|
3740
3920
|
url,
|
|
@@ -4237,6 +4417,7 @@ var DecodeWorkerChild = class {
|
|
|
4237
4417
|
*/
|
|
4238
4418
|
closeInput() {
|
|
4239
4419
|
this.native.run("nativeRing", () => this.nativeRing.clear());
|
|
4420
|
+
this.native.run("pendingMark", () => this.pendingMark.clear());
|
|
4240
4421
|
this.native.run("leaseStore", () => this.leaseStore.clear());
|
|
4241
4422
|
const cleared = this.preRollSettle.settleAll("cleared");
|
|
4242
4423
|
if (cleared) this.send({
|