@camstack/addon-pipeline 1.2.70 → 1.2.71

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.
Files changed (29) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +2 -2
  4. package/dist/detection-pipeline/index.mjs +2 -2
  5. package/dist/{dist-cR-pvD_z.js → dist-CtOVWTCi.js} +78 -33
  6. package/dist/{dist-BPaa4_z6.mjs → dist-D1Xk5Tji.mjs} +78 -33
  7. package/dist/{event-loop-stall-monitor-STRdnQWm.js → event-loop-stall-monitor-BJs7Yws7.js} +1 -1
  8. package/dist/{event-loop-stall-monitor-B79REtL2.mjs → event-loop-stall-monitor-Cmbt664E.mjs} +1 -1
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/pipeline-runner/index.js +95 -28
  12. package/dist/pipeline-runner/index.mjs +95 -28
  13. package/dist/recorder/index.js +365 -73
  14. package/dist/recorder/index.mjs +365 -73
  15. package/dist/session-decode/decode-worker-child.js +483 -52
  16. package/dist/session-decode/decode-worker-child.mjs +483 -52
  17. package/dist/stream-broker/_stub.js +660 -556
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B-HGBNad.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BWf4H4Nl.mjs} +3 -3
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cu_kQpIy.mjs +26 -0
  20. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CExO0piw.mjs +26 -0
  21. package/dist/stream-broker/{hostInit-B12DHOv7.mjs → hostInit-DD5Z1U6p.mjs} +3 -3
  22. package/dist/stream-broker/index.js +1 -1
  23. package/dist/stream-broker/index.mjs +1 -1
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/dist/{worker-protocol-C7V1qIIA.js → worker-protocol-DEV7g32b.js} +26 -19
  26. package/dist/{worker-protocol-CBO8nwQj.mjs → worker-protocol-Dm6LQ9Ls.mjs} +26 -19
  27. package/package.json +1 -1
  28. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D1CqbPrT.mjs +0 -26
  29. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-NjvjJWz-.mjs +0 -26
@@ -1,6 +1,6 @@
1
- import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-BPaa4_z6.mjs";
2
- import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-B79REtL2.mjs";
3
- import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-CBO8nwQj.mjs";
1
+ import { At as string, Ct as boolean, Dt as number, Et as literal, K as pipelineRunnerCapability, M as deriveDetailCropRect, Mt as EventCategory, Ot as object, St as array, Tt as lazy, U as pickDetailCropConvention, W as pickNativeLeaseOverride, dt as createEvent, gt as nodePin, j as defineCustomActions, k as customAction, s as DEFAULT_DETAIL_CROP_CONVENTION, st as BaseAddon, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT, xt as _enum } from "../dist-D1Xk5Tji.mjs";
2
+ import { c as getStepDefinition, t as attributeStall } from "../event-loop-stall-monitor-Cmbt664E.mjs";
3
+ import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-Dm6LQ9Ls.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-B-mb8uWx.mjs";
6
6
  import { i as withDetectionIntent, n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-Ci7RXNGb.mjs";
@@ -10,28 +10,6 @@ import { fileURLToPath } from "node:url";
10
10
  import { PerformanceObserver } from "node:perf_hooks";
11
11
  import * as os from "node:os";
12
12
  //#region src/session-decode/fork-decode-worker.ts
13
- /**
14
- * Real `fork` implementation for the session-decode coordinator — Epic C P1,
15
- * Task 4.
16
- *
17
- * Spawns the decode-worker child (`decode-worker-child.ts`, Task 2) as a
18
- * dedicated `child_process.fork` and adapts the returned `ChildProcess` to the
19
- * coordinator's minimal {@link ForkedChildLike} contract WITHOUT any cast —
20
- * an explicit forwarding wrapper, so a wider Node `ChildProcess` signature
21
- * never has to structurally satisfy the narrower interface.
22
- *
23
- * `serialization: 'advanced'` is MANDATORY: Node's default 'json' IPC
24
- * base64-serialises the `buffer` reply's `Uint8Array`, breaking the "raw
25
- * bytes, not base64" contract the coordinator + `worker-protocol.ts`'s
26
- * `value.bytes instanceof Uint8Array` guard both depend on. Advanced (V8
27
- * structured-clone) preserves the typed array on the wire.
28
- *
29
- * The child entry is resolved relative to THIS module's built location. Both
30
- * this module and `pipeline-runner/index.ts` bundle into the SAME entry
31
- * (`dist/pipeline-runner/index.js`, `manualChunks: () => null`), so
32
- * `import.meta.url` here is that file's URL and the sibling child lands at
33
- * `dist/session-decode/decode-worker-child.js` (its own vite build entry).
34
- */
35
13
  /** Absolute path to the built decode-worker child entry (a vite build entry). */
36
14
  var CHILD_ENTRY_PATH = fileURLToPath(new URL("../session-decode/decode-worker-child.js", import.meta.url));
37
15
  /**
@@ -689,8 +667,24 @@ var SessionDecodeSession = class {
689
667
  });
690
668
  }
691
669
  /**
692
- * Prompt-release the worker's RAM native-frame lease for `frameId`. Fire and
693
- * forget (no reply): a lease also self-expires on its TTL, so a dropped
670
+ * The runner has this frame's detections: cut one native subject tile per box
671
+ * and END the hold on `frameId`. Fire and forget (no reply) so the encode can
672
+ * never delay the runner; a dropped message costs only the tiles.
673
+ *
674
+ * An EMPTY box list is still sent — it is how a frame that detected nothing
675
+ * releases its hold, and that is the common case.
676
+ */
677
+ cutTiles(frameId, bboxes) {
678
+ if (this.exited) return;
679
+ this.trySend({
680
+ kind: "cutTiles",
681
+ frameId,
682
+ bboxes
683
+ });
684
+ }
685
+ /**
686
+ * Prompt-release the worker's RAM native-frame hold for `frameId`. Fire and
687
+ * forget (no reply): the hold is also bounded by a frame count, so a dropped
694
688
  * release only defers reclaim. No-op once the worker has exited.
695
689
  */
696
690
  releaseLease(frameId) {
@@ -970,6 +964,7 @@ function buildFrameImage(session, reply) {
970
964
  return session.requestBuffer(reply.frameId, opts);
971
965
  },
972
966
  nativeCrop: (bbox, maxWidth) => session.requestNativeCrop(reply.frameId, bbox, maxWidth),
967
+ cutTiles: (bboxes) => session.cutTiles(reply.frameId, bboxes),
973
968
  releaseNativeLease: () => session.releaseLease(reply.frameId),
974
969
  markInferred: () => session.markInferred(reply.frameId),
975
970
  close: () => {
@@ -1110,6 +1105,7 @@ function startSessionDecodePump(deps) {
1110
1105
  const image = frame.image;
1111
1106
  const cropFn = image.nativeCrop;
1112
1107
  const nativeCrop = cropFn ? Object.assign((bbox, maxWidth) => cropFn(bbox, maxWidth), {
1108
+ cutTiles: (bboxes) => image.cutTiles?.(bboxes),
1113
1109
  release: () => image.releaseNativeLease?.(),
1114
1110
  markInferred: () => image.markInferred?.()
1115
1111
  }) : void 0;
@@ -2517,7 +2513,7 @@ var NATIVE_LEASE_OWNER_ADDON_ID = "pipeline-orchestrator";
2517
2513
  * old value.
2518
2514
  */
2519
2515
  function sameOverride(a, b) {
2520
- return a.ttlMs === b.ttlMs && a.budgetMb === b.budgetMb && a.activityMs === b.activityMs && a.admission === b.admission;
2516
+ return a.holdFrames === b.holdFrames && a.budgetMb === b.budgetMb && a.activityMs === b.activityMs && a.admission === b.admission && a.tileBudgetMb === b.tileBudgetMb;
2521
2517
  }
2522
2518
  /**
2523
2519
  * TTL-cached read of the cluster native-lease knobs.
@@ -3794,6 +3790,42 @@ async function runStatelessStep(deps, input) {
3794
3790
  details
3795
3791
  };
3796
3792
  }
3793
+ /**
3794
+ * Normalised boxes for the subjects worth a tile, highest confidence first,
3795
+ * capped.
3796
+ *
3797
+ * Returns an empty array for a frame with no usable detections — and the caller
3798
+ * still sends it: an empty `cutTiles` is how a frame that detected nothing ends
3799
+ * its hold, which is the majority of frames and the whole reason this design
3800
+ * costs less than the one it replaces.
3801
+ *
3802
+ * A degenerate or non-finite box is DROPPED rather than clamped: a zero-area
3803
+ * container serves nothing, and a NaN one would poison the worker's geometry.
3804
+ */
3805
+ function planTileCuts(detections, frame, maxSubjects = 4) {
3806
+ if (frame.width <= 0 || frame.height <= 0) return [];
3807
+ const ordered = [...detections.filter((d) => isUsable(d.bbox))].sort((a, b) => b.score - a.score);
3808
+ const out = [];
3809
+ for (const detection of ordered.slice(0, Math.max(0, maxSubjects))) {
3810
+ const { x, y, width, height } = detection.bbox;
3811
+ out.push({
3812
+ x: x / frame.width,
3813
+ y: y / frame.height,
3814
+ w: width / frame.width,
3815
+ h: height / frame.height
3816
+ });
3817
+ }
3818
+ return out;
3819
+ }
3820
+ function isUsable(bbox) {
3821
+ if (![
3822
+ bbox.x,
3823
+ bbox.y,
3824
+ bbox.width,
3825
+ bbox.height
3826
+ ].every((v) => Number.isFinite(v))) return false;
3827
+ return bbox.width > 0 && bbox.height > 0;
3828
+ }
3797
3829
  //#endregion
3798
3830
  //#region src/pipeline-runner/index.ts
3799
3831
  var DEFAULT_CONFIG = {
@@ -4282,6 +4314,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
4282
4314
  this.handleDetectionStreamChange(deviceId, action);
4283
4315
  });
4284
4316
  this.runner.onResult(async (deviceId, frame, result, _streamType, handle) => {
4317
+ this.cutSubjectTiles(deviceId, result, handle);
4285
4318
  this.emitInferenceResult(deviceId, frame, result, handle);
4286
4319
  const detectionCount = result.detections?.length ?? 0;
4287
4320
  this.runner?.noteDetectionResult(deviceId, detectionCount);
@@ -5103,6 +5136,40 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
5103
5136
  }, resolved.tier, input.encodeJpeg);
5104
5137
  }
5105
5138
  /**
5139
+ * Turn a finished frame into subject TILES and end its hold — the runner half
5140
+ * of R2 (`docs/design/2026-08-13-native-lease-two-tier-redesign.md`).
5141
+ *
5142
+ * Called from the result callback, which is the last instant at which the
5143
+ * frame's native pixels exist and its detections are known at the same time.
5144
+ * Every path through here ends the hold, including the one where there is
5145
+ * nothing to cut: a frame that detected nothing is the majority of frames and
5146
+ * releasing it promptly is most of the RAM this redesign gives back.
5147
+ *
5148
+ * Deliberately synchronous and unawaited. The worker replies to nothing, so
5149
+ * there is nothing to wait for, and a detection result must never be delayed
5150
+ * by an encode.
5151
+ */
5152
+ cutSubjectTiles(deviceId, result, handle) {
5153
+ if (!handle) return;
5154
+ const found = this.nativeCropRegistry.get(handle);
5155
+ if (!found) return;
5156
+ const boxes = planTileCuts(result.detections ?? [], {
5157
+ width: result.width,
5158
+ height: result.height
5159
+ });
5160
+ try {
5161
+ found.request.cutTiles(boxes);
5162
+ } catch (err) {
5163
+ this.ctx.logger.debug("subject tile cut could not be dispatched", {
5164
+ tags: { deviceId },
5165
+ meta: {
5166
+ subjects: boxes.length,
5167
+ error: err instanceof Error ? err.message : String(err)
5168
+ }
5169
+ });
5170
+ }
5171
+ }
5172
+ /**
5106
5173
  * The native-lease half of {@link getNativeCrop}, extracted so the DETAIL path
5107
5174
  * can keep the miss REASON the cap's `null` return has no room for. Both
5108
5175
  * callers share this one decision — a second copy of the tier arithmetic is