@camstack/addon-pipeline 1.1.43 → 1.1.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.
Files changed (36) hide show
  1. package/dist/audio-analyzer/index.js +5 -4
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/chunk-D6vf50IK.js +28 -0
  4. package/dist/detection-pipeline/index.js +2714 -2028
  5. package/dist/detection-pipeline/index.mjs +2708 -2023
  6. package/dist/{dist-NvwN60Fq.js → dist-CjxqjgEv.js} +255 -185
  7. package/dist/{dist-GFd8M6KO.mjs → dist-D04numuO.mjs} +250 -186
  8. package/dist/{model-download-service-C-IHWnXx-DxM2DSns.js → model-download-service-C-IHWnXx-BLXzxpRB.js} +3 -29
  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 +122 -287
  12. package/dist/pipeline-runner/index.mjs +118 -285
  13. package/dist/recorder/index.js +60 -20
  14. package/dist/recorder/index.mjs +57 -18
  15. package/dist/{frame-handle-plane-B3Fxww8H.js → remote-source-plane-CLuxyDV2.js} +236 -1
  16. package/dist/{frame-handle-plane-E_AsR77T.mjs → remote-source-plane-D31IILFN.mjs} +219 -2
  17. package/dist/session-decode/decode-worker-child.js +86 -2
  18. package/dist/session-decode/decode-worker-child.mjs +83 -2
  19. package/dist/stream-broker/_stub.js +550 -523
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CeielP5P.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BGKueo9D.mjs} +2 -2
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D0sKyXiy.mjs +26 -0
  22. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-8BUVCFIR.mjs +26 -0
  23. package/dist/stream-broker/{hostInit-DmjqskOR.mjs → hostInit-DazV2RCq.mjs} +2 -2
  24. package/dist/stream-broker/index.js +746 -135
  25. package/dist/stream-broker/index.mjs +737 -127
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CEsPjwzT.js → MaskShapeCanvas-DI4BY7W2-D_F4a_3H.js} +1 -1
  28. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-D43fIRgB.js → MotionZonesSettings-NcxxQN8r-CfTBrn4G.js} +1 -1
  29. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-C3wJxG8V.js → PrivacyMaskSettings-APgPLF7p-BWYkeoRj.js} +1 -1
  30. package/embed-dist/assets/{index-CSFtK41z.css → index-CA4kv43T.css} +1 -1
  31. package/embed-dist/assets/index-_ccEdNW7.js +81 -0
  32. package/embed-dist/index.html +2 -2
  33. package/package.json +2 -2
  34. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DqJvWBKS.mjs +0 -26
  35. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-dGka8yZY.mjs +0 -26
  36. package/embed-dist/assets/index-CX-rILhw.js +0 -81
@@ -1,29 +1,9 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
1
+ const require_chunk = require("./chunk-D6vf50IK.js");
22
2
  require("node:crypto");
23
3
  let node_fs = require("node:fs");
24
- node_fs = __toESM(node_fs, 1);
4
+ node_fs = require_chunk.__toESM(node_fs, 1);
25
5
  let node_path = require("node:path");
26
- node_path = __toESM(node_path, 1);
6
+ node_path = require_chunk.__toESM(node_path, 1);
27
7
  //#region ../system/dist/model-download-service-C-IHWnXx.mjs
28
8
  /**
29
9
  * Map a rel path to one candidate absolute path PER root, keeping only roots the
@@ -349,12 +329,6 @@ function deleteModelFromDisk(modelsDir, entry, format) {
349
329
  return true;
350
330
  }
351
331
  //#endregion
352
- Object.defineProperty(exports, "__toESM", {
353
- enumerable: true,
354
- get: function() {
355
- return __toESM;
356
- }
357
- });
358
332
  Object.defineProperty(exports, "createFileDataPlaneHandler", {
359
333
  enumerable: true,
360
334
  get: function() {
@@ -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-NvwN60Fq.js");
5
+ const require_dist = require("../dist-CjxqjgEv.js");
6
6
  let _camstack_shm_ring = require("@camstack/shm-ring");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { B as DeviceType, E as motionDetectionCapability, L as BaseAddon, U as hydrateSchema, x as evaluateZoneRules } from "../dist-GFd8M6KO.mjs";
1
+ import { B as DeviceType, E as motionDetectionCapability, L as BaseAddon, U as hydrateSchema, x as evaluateZoneRules } from "../dist-D04numuO.mjs";
2
2
  import { FrameRingReaderCache } from "@camstack/shm-ring";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-NvwN60Fq.js");
6
- const require_frame_handle_plane = require("../frame-handle-plane-B3Fxww8H.js");
5
+ const require_dist = require("../dist-CjxqjgEv.js");
6
+ const require_remote_source_plane = require("../remote-source-plane-CLuxyDV2.js");
7
7
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
8
8
  const require_worker_protocol = require("../worker-protocol-BCfO8gUF.js");
9
9
  let _camstack_shm_ring = require("@camstack/shm-ring");
@@ -426,221 +426,6 @@ function startPolling(options, source, subscriptionId, resolvedMaxFps, lifecycle
426
426
  };
427
427
  }
428
428
  //#endregion
429
- //#region src/pipeline-runner/remote-source-plane.ts
430
- /**
431
- * Runner-side mode selection (pure): whether an attach payload routes this
432
- * camera through the remote-source leg. Absent `frameSource` (a pre-P2c
433
- * payload) and `local-broker` both take today's co-located broker path —
434
- * the rollout-OFF safety invariant.
435
- */
436
- function isRemoteRestream(frameSource) {
437
- return frameSource !== void 0 && frameSource.kind === "remote-restream";
438
- }
439
- /**
440
- * Resolve WHICH broker profile a camStream id feeds for a device (pure).
441
- *
442
- * `getStreamWithCodec` targets a PROFILE (its assigned camStream), while the
443
- * runner is dispatched with camStream ids (`motionStreamId` /
444
- * `detectionStreamId`). The broker's profile slots carry the mapping
445
- * (`sourceCamStreamId`); a stream no slot feeds returns `null` — the acquire
446
- * fails fast and the poller's backoff retries (the assignment may still be
447
- * propagating).
448
- */
449
- function profileForStreamId(slots, deviceId, streamId) {
450
- for (const slot of slots) if (slot.deviceId === deviceId && slot.sourceCamStreamId === streamId) return slot.profile;
451
- return null;
452
- }
453
- var RemoteSourcePlane = class {
454
- api;
455
- logger;
456
- deviceId;
457
- ownNodeId;
458
- hostname;
459
- /** Single-flight entry creation per camStream id. */
460
- entries = /* @__PURE__ */ new Map();
461
- disposed = false;
462
- constructor(options) {
463
- this.api = options.api;
464
- this.logger = options.logger.withTags({ deviceId: options.deviceId });
465
- this.deviceId = options.deviceId;
466
- this.ownNodeId = options.ownNodeId;
467
- this.hostname = options.hostname;
468
- }
469
- /**
470
- * A {@link FrameHandleSource} for one camStream id, pluggable into
471
- * `startFrameHandlePoller`. Motion (`gray`) and detection (`rgb`)
472
- * subscriptions of the same stream share ONE acquired restream and ONE
473
- * satellite plane (per-format decoder sessions inside it — hub parity).
474
- */
475
- makeSource(streamId) {
476
- return {
477
- subscribe: async (input) => {
478
- if (this.disposed) throw new Error("RemoteSourcePlane: subscribe after dispose");
479
- const entry = await this.ensureEntry(streamId);
480
- const result = await entry.plane.subscribe({
481
- format: input.format,
482
- maxFps: input.maxFps,
483
- tag: input.tag
484
- });
485
- entry.subscriptionIds.add(result.subscriptionId);
486
- return result;
487
- },
488
- pullHandles: async (input) => {
489
- const entry = await this.peekEntry(streamId);
490
- if (!entry) return [];
491
- return entry.plane.pullHandles(input.subscriptionId, input.maxCount);
492
- },
493
- unsubscribe: async (input) => {
494
- const entry = await this.peekEntry(streamId);
495
- if (!entry) return;
496
- entry.subscriptionIds.delete(input.subscriptionId);
497
- await entry.plane.unsubscribe(input.subscriptionId).catch(() => {});
498
- if (entry.subscriptionIds.size === 0) {
499
- this.entries.delete(streamId);
500
- await this.teardownEntry(entry, streamId);
501
- }
502
- }
503
- };
504
- }
505
- /** Dispose every entry: satellite planes destroyed, restreams released. Idempotent. */
506
- async dispose() {
507
- if (this.disposed) return;
508
- this.disposed = true;
509
- const pending = [...this.entries.entries()];
510
- this.entries.clear();
511
- for (const [streamId, entryPromise] of pending) try {
512
- const entry = await entryPromise;
513
- await this.teardownEntry(entry, streamId);
514
- } catch {}
515
- }
516
- /** The live entry for a stream, or `null` (never creates one). */
517
- async peekEntry(streamId) {
518
- const pending = this.entries.get(streamId);
519
- if (!pending) return null;
520
- try {
521
- return await pending;
522
- } catch {
523
- return null;
524
- }
525
- }
526
- /** Single-flight: acquire the restream + build the satellite plane once per stream. */
527
- ensureEntry(streamId) {
528
- const existing = this.entries.get(streamId);
529
- if (existing) return existing;
530
- const creation = this.createEntry(streamId);
531
- this.entries.set(streamId, creation);
532
- creation.catch(() => {
533
- if (this.entries.get(streamId) === creation) this.entries.delete(streamId);
534
- });
535
- return creation;
536
- }
537
- async createEntry(streamId) {
538
- const { api, deviceId, ownNodeId } = this;
539
- if (this.hostname === void 0) throw new Error("RemoteSourcePlane: owner hostname unresolvable — set frameSource.hubHostnameOverride or CAMSTACK_HUB_URL (a 127.0.0.1 restream URL would dial the wrong box)");
540
- const profile = profileForStreamId(await api.streamBroker.listAllProfileSlots.query(), deviceId, streamId);
541
- if (profile === null) throw new Error(`RemoteSourcePlane: no broker profile is fed by camStream '${streamId}' of device ${deviceId} — cannot acquire the owner restream`);
542
- const rtpSource = await api.streamBroker.getStreamWithCodec.mutate({
543
- deviceId,
544
- video: "copy",
545
- audio: "none",
546
- profile,
547
- hostname: this.hostname,
548
- tag: `runner:${ownNodeId}:remote-src:${deviceId}/${streamId}`
549
- });
550
- const codec = rtpSource.videoCodec === "H265" ? "h265" : "h264";
551
- const plane = new require_frame_handle_plane.FrameHandlePlane({
552
- decoderApi: this.buildPinnedDecoderCapApi(),
553
- logger: this.logger,
554
- localNodeId: ownNodeId,
555
- resolveStreamInfo: () => ({
556
- codec,
557
- numericDeviceId: deviceId,
558
- tag: `runner-remote:${deviceId}/${streamId}`,
559
- pullModeUrl: rtpSource.url
560
- })
561
- });
562
- this.logger.info("remote-source plane: owner restream acquired", { meta: {
563
- streamId,
564
- profile,
565
- codec,
566
- pipelineKey: rtpSource.pipelineKey,
567
- hostname: this.hostname
568
- } });
569
- return {
570
- plane,
571
- pipelineKey: rtpSource.pipelineKey,
572
- subscriptionIds: /* @__PURE__ */ new Set(),
573
- released: false
574
- };
575
- }
576
- /** Destroy an entry's satellite plane and release its restream refcount once. */
577
- async teardownEntry(entry, streamId) {
578
- await entry.plane.dispose().catch((err) => {
579
- this.logger.warn("remote-source plane: satellite plane dispose failed", { meta: {
580
- streamId,
581
- error: require_dist.errMsg(err)
582
- } });
583
- });
584
- if (entry.released) return;
585
- entry.released = true;
586
- await this.api.streamBroker.releaseStreamWithCodec.mutate({ pipelineKey: entry.pipelineKey }).catch((err) => {
587
- this.logger.debug("remote-source plane: releaseStreamWithCodec failed (benign on hub restart)", { meta: {
588
- streamId,
589
- pipelineKey: entry.pipelineKey,
590
- error: require_dist.errMsg(err)
591
- } });
592
- });
593
- }
594
- /**
595
- * Every decoder call pinned to THIS runner's node — mirror of the hub
596
- * broker's wrapper (`stream-broker-manager.buildDecoderCapApi`): the
597
- * `decoder` cap is a singleton whose UNPINNED calls resolve to the
598
- * cluster-elected node, which is generally NOT this agent. Pinning create
599
- * AND every session op to the same local node keeps the session + its shm
600
- * ring here, where this plane's readers live.
601
- */
602
- buildPinnedDecoderCapApi() {
603
- const api = this.api;
604
- const pin = require_dist.nodePin(this.ownNodeId);
605
- const toWireBytes = (bytes) => {
606
- const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
607
- copy.set(bytes);
608
- return copy;
609
- };
610
- return {
611
- supportsCodec: async (input) => api.decoder.supportsCodec.query({ ...input }, pin),
612
- getInfo: async () => api.decoder.getInfo.query(void 0, pin),
613
- createSession: async (config) => api.decoder.createSession.query({ ...config }, pin),
614
- pushPacket: async (input) => {
615
- await api.decoder.pushPacket.query({
616
- sessionId: input.sessionId,
617
- packet: {
618
- ...input.packet,
619
- data: toWireBytes(input.packet.data)
620
- }
621
- }, pin);
622
- },
623
- pullFrames: async (input) => {
624
- return (await api.decoder.pullFrames.query({ ...input }, pin)).map((frame) => ({
625
- ...frame,
626
- data: Buffer.from(frame.data)
627
- }));
628
- },
629
- pullHandles: async (input) => api.decoder.pullHandles.query({ ...input }, pin),
630
- destroySession: async (input) => {
631
- await api.decoder.destroySession.query({ ...input }, pin);
632
- },
633
- openStream: async (input) => {
634
- await api.decoder.openStream.query({ ...input }, pin);
635
- },
636
- updateConfig: async (input) => {
637
- await api.decoder.updateConfig.query({ ...input }, pin);
638
- },
639
- getStats: async (input) => api.decoder.getStats.query({ ...input }, pin)
640
- };
641
- }
642
- };
643
- //#endregion
644
429
  //#region src/pipeline-runner/frame-queue.ts
645
430
  /**
646
431
  * Latest-frame-only buffer. Keeps only the most recent item, dropping all
@@ -1928,21 +1713,25 @@ function startSessionDecodePump(deps) {
1928
1713
  const decoded = await toDecodedFrame(frame, format);
1929
1714
  deps.onDecodedFrame(decoded);
1930
1715
  } catch (err) {
1931
- deps.logger.debug("session-decode pump: skipped a frame (decode/consume error)", { meta: {
1932
- deviceId: deps.source.deviceId,
1933
- format,
1934
- error: require_dist.errMsg(err)
1935
- } });
1716
+ deps.logger.debug("session-decode pump: skipped a frame (decode/consume error)", {
1717
+ tags: { deviceId: deps.source.deviceId },
1718
+ meta: {
1719
+ format,
1720
+ error: require_dist.errMsg(err)
1721
+ }
1722
+ });
1936
1723
  } finally {
1937
1724
  frame.image.close();
1938
1725
  }
1939
1726
  }
1940
1727
  } catch (err) {
1941
- deps.logger.warn("session-decode pump: frame loop ended with error", { meta: {
1942
- deviceId: deps.source.deviceId,
1943
- format,
1944
- error: require_dist.errMsg(err)
1945
- } });
1728
+ deps.logger.warn("session-decode pump: frame loop ended with error", {
1729
+ tags: { deviceId: deps.source.deviceId },
1730
+ meta: {
1731
+ format,
1732
+ error: require_dist.errMsg(err)
1733
+ }
1734
+ });
1946
1735
  } finally {
1947
1736
  releaseSpawnSlotOnce();
1948
1737
  }
@@ -2064,10 +1853,96 @@ function resolveMotionFrameSource(config, sharedWithDetection) {
2064
1853
  */
2065
1854
  function resolveSessionDecodeHostname(config, hubUrl) {
2066
1855
  const frameSource = config.frameSource;
2067
- if (!isRemoteRestream(frameSource)) return void 0;
1856
+ if (!require_remote_source_plane.isRemoteRestream(frameSource)) return void 0;
1857
+ return resolveOwnerRestreamHost(frameSource, hubUrl);
1858
+ }
1859
+ /**
1860
+ * The camera-source OWNER node for a session-decode acquire, or `undefined`
1861
+ * when the source is node-local (co-located, pre-cross-node). Mirrors
1862
+ * `resolveSessionDecodeHostname`: only a `remote-restream` frame source names an
1863
+ * owner. Used to `nodePin` the broker calls at the actual owner.
1864
+ */
1865
+ function resolveSessionDecodeOwnerNodeId(config) {
1866
+ const frameSource = config.frameSource;
1867
+ if (!require_remote_source_plane.isRemoteRestream(frameSource)) return void 0;
1868
+ return frameSource.ownerNodeId;
1869
+ }
1870
+ /**
1871
+ * The host a cross-node consumer dials for the owner's compressed restream.
1872
+ * Precedence: the orchestrator-resolved `ownerReachableHost` (the per-node
1873
+ * Cluster-UI override) → the operator `hubHostnameOverride` escape hatch →
1874
+ * the `CAMSTACK_HUB_URL`-derived host (the auto-detected Moleculer address).
1875
+ * `undefined` only when none resolve (the acquire then fails loudly rather
1876
+ * than silently dialing `127.0.0.1` on a remote node).
1877
+ */
1878
+ function resolveOwnerRestreamHost(frameSource, hubUrl) {
1879
+ const reachable = frameSource.ownerReachableHost?.trim();
1880
+ if (reachable !== void 0 && reachable.length > 0) return reachable;
2068
1881
  return require_hub_hostname.resolveHubHostname(frameSource.hubHostnameOverride, hubUrl);
2069
1882
  }
2070
1883
  /**
1884
+ * Acquire the broker's COMPRESSED passthrough restream for one camStream and
1885
+ * wrap it as a {@link VideoFrameSource} the decode worker can dial. Mirrors
1886
+ * {@link RemoteSourcePlane}'s acquire: resolve the profile the camStream
1887
+ * feeds, then `getStreamWithCodec({ video: 'copy' })` (refcounted — rides the
1888
+ * broker's single source pull, never a second camera dial). No `hostname`:
1889
+ * the P1 flagged camera is HUB-LOCAL, so the returned `127.0.0.1` URL is
1890
+ * dialable from this hub-resident runner. Returns `null` (loudly logged) when
1891
+ * no profile is bound yet — the caller opens no subscription.
1892
+ *
1893
+ * Extracted as a standalone exported function (rather than a private method)
1894
+ * so the owner-node pin threading through the three `streamBroker` calls
1895
+ * (`listAllProfileSlots`, `getStreamWithCodec`, `releaseStreamWithCodec`) is
1896
+ * directly unit-testable without instantiating the addon class. The private
1897
+ * `acquireSessionDecodeRestream` method on {@link PipelineRunnerAddon} is a
1898
+ * thin delegator to this function — its two call sites are unchanged.
1899
+ */
1900
+ async function acquireSessionDecodeRestreamFrom(api, logger, params) {
1901
+ const { deviceId, streamId, tag, hostname, ownerNodeId } = params;
1902
+ const pin = ownerNodeId !== void 0 ? require_dist.nodePin(ownerNodeId) : void 0;
1903
+ const profile = require_remote_source_plane.profileForStreamId(await api.streamBroker.listAllProfileSlots.query(void 0, pin), deviceId, streamId);
1904
+ if (profile === null) {
1905
+ logger.error("session-decode: no broker profile feeds camStream — cannot acquire restream", {
1906
+ tags: { deviceId },
1907
+ meta: { streamId }
1908
+ });
1909
+ return null;
1910
+ }
1911
+ const rtpSource = await api.streamBroker.getStreamWithCodec.mutate({
1912
+ deviceId,
1913
+ video: "copy",
1914
+ audio: "none",
1915
+ profile,
1916
+ tag,
1917
+ ...hostname !== void 0 ? { hostname } : {}
1918
+ }, pin);
1919
+ const codec = rtpSource.videoCodec === "H265" ? "h265" : "h264";
1920
+ const source = {
1921
+ restreamUrl: rtpSource.url,
1922
+ deviceId,
1923
+ codec
1924
+ };
1925
+ const pipelineKey = rtpSource.pipelineKey;
1926
+ let released = false;
1927
+ const release = async () => {
1928
+ if (released) return;
1929
+ released = true;
1930
+ await api.streamBroker.releaseStreamWithCodec.mutate({ pipelineKey }, pin).catch((err) => {
1931
+ logger.debug("session-decode: releaseStreamWithCodec failed (benign on hub restart)", {
1932
+ tags: { deviceId },
1933
+ meta: {
1934
+ pipelineKey,
1935
+ error: require_dist.errMsg(err)
1936
+ }
1937
+ });
1938
+ });
1939
+ };
1940
+ return {
1941
+ source,
1942
+ release
1943
+ };
1944
+ }
1945
+ /**
2071
1946
  * Interval for periodic metric snapshot emission. ~1 Hz strikes the
2072
1947
  * balance between UI freshness (overlay phase / fps numbers feel live)
2073
1948
  * and bus traffic (one snapshot per attached camera + one per node
@@ -2628,7 +2503,6 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2628
2503
  if (config.motionSources.includes("analyzer")) attachment.motionUnsubscribe = await this.subscribeMotionFrames(config);
2629
2504
  const stepsCount = config.steps?.length ?? 0;
2630
2505
  const dispatch = stepsCount > 0 ? `runPipeline(${stepsCount}step${stepsCount === 1 ? "" : "s"})` : config.steps !== void 0 ? "skip(0steps)" : "runFrame(legacy)";
2631
- const engineLabel = config.engine ? `${config.engine.runtime}+${config.engine.backend}/${config.engine.format}` : "default";
2632
2506
  this.ctx.logger.info("attachCamera", {
2633
2507
  tags: { deviceId: config.deviceId },
2634
2508
  meta: {
@@ -2638,7 +2512,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2638
2512
  detectionFps: config.detectionFps,
2639
2513
  motionSources: config.motionSources,
2640
2514
  dispatch,
2641
- engine: engineLabel
2515
+ engine: "node-local"
2642
2516
  }
2643
2517
  });
2644
2518
  return { success: true };
@@ -2822,17 +2696,18 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2822
2696
  */
2823
2697
  remoteSourceFor(config) {
2824
2698
  const frameSource = config.frameSource;
2825
- if (!isRemoteRestream(frameSource)) return null;
2699
+ if (!require_remote_source_plane.isRemoteRestream(frameSource)) return null;
2826
2700
  const existing = this.remotePlanes.get(config.deviceId);
2827
2701
  if (existing) return existing;
2828
2702
  const api = this.ctx.api;
2829
2703
  if (!api) return null;
2830
- const hostname = require_hub_hostname.resolveHubHostname(frameSource.hubHostnameOverride, process.env["CAMSTACK_HUB_URL"]);
2831
- const plane = new RemoteSourcePlane({
2704
+ const hostname = resolveOwnerRestreamHost(frameSource, process.env["CAMSTACK_HUB_URL"]);
2705
+ const plane = new require_remote_source_plane.RemoteSourcePlane({
2832
2706
  api,
2833
2707
  logger: this.ctx.logger,
2834
2708
  deviceId: config.deviceId,
2835
2709
  ownNodeId: this.nodeId,
2710
+ ownerNodeId: frameSource.ownerNodeId,
2836
2711
  hostname
2837
2712
  });
2838
2713
  this.ctx.logger.info("remote-source leg armed for camera", {
@@ -2846,57 +2721,18 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2846
2721
  return plane;
2847
2722
  }
2848
2723
  /**
2849
- * Acquire the broker's COMPRESSED passthrough restream for one camStream and
2850
- * wrap it as a {@link VideoFrameSource} the decode worker can dial. Mirrors
2851
- * {@link RemoteSourcePlane}'s acquire: resolve the profile the camStream
2852
- * feeds, then `getStreamWithCodec({ video: 'copy' })` (refcounted — rides the
2853
- * broker's single source pull, never a second camera dial). No `hostname`:
2854
- * the P1 flagged camera is HUB-LOCAL, so the returned `127.0.0.1` URL is
2855
- * dialable from this hub-resident runner. Returns `null` (loudly logged) when
2856
- * no profile is bound yet — the caller opens no subscription.
2724
+ * Thin delegator to the module-level {@link acquireSessionDecodeRestreamFrom}
2725
+ * (see its doc for the acquire behavior) supplies this addon's `ctx.logger`.
2726
+ * Kept as a method so the two call sites need no change.
2857
2727
  */
2858
- async acquireSessionDecodeRestream(api, deviceId, streamId, tag, hostname) {
2859
- const profile = profileForStreamId(await api.streamBroker.listAllProfileSlots.query(), deviceId, streamId);
2860
- if (profile === null) {
2861
- this.ctx.logger.error("session-decode: no broker profile feeds camStream — cannot acquire restream", {
2862
- tags: { deviceId },
2863
- meta: { streamId }
2864
- });
2865
- return null;
2866
- }
2867
- const rtpSource = await api.streamBroker.getStreamWithCodec.mutate({
2728
+ async acquireSessionDecodeRestream(api, deviceId, streamId, tag, hostname, ownerNodeId) {
2729
+ return acquireSessionDecodeRestreamFrom(api, this.ctx.logger, {
2868
2730
  deviceId,
2869
- video: "copy",
2870
- audio: "none",
2871
- profile,
2731
+ streamId,
2872
2732
  tag,
2873
- ...hostname !== void 0 ? { hostname } : {}
2733
+ hostname,
2734
+ ownerNodeId
2874
2735
  });
2875
- const codec = rtpSource.videoCodec === "H265" ? "h265" : "h264";
2876
- const source = {
2877
- restreamUrl: rtpSource.url,
2878
- deviceId,
2879
- codec
2880
- };
2881
- const pipelineKey = rtpSource.pipelineKey;
2882
- let released = false;
2883
- const release = async () => {
2884
- if (released) return;
2885
- released = true;
2886
- await api.streamBroker.releaseStreamWithCodec.mutate({ pipelineKey }).catch((err) => {
2887
- this.ctx.logger.debug("session-decode: releaseStreamWithCodec failed (benign on hub restart)", {
2888
- tags: { deviceId },
2889
- meta: {
2890
- pipelineKey,
2891
- error: require_dist.errMsg(err)
2892
- }
2893
- });
2894
- });
2895
- };
2896
- return {
2897
- source,
2898
- release
2899
- };
2900
2736
  }
2901
2737
  /**
2902
2738
  * Register this device's motion to ride the detection session-decode worker
@@ -2974,7 +2810,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2974
2810
  if (!runner) return () => {};
2975
2811
  return startGuardedSessionDecode({
2976
2812
  logger: log,
2977
- acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:detect:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"])),
2813
+ acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:detect:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config)),
2978
2814
  startPump: (source) => {
2979
2815
  log.info("session-decode: detection routed through per-session decode worker (rgb)", { meta: {
2980
2816
  streamId: config.detectionStreamId,
@@ -3013,7 +2849,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
3013
2849
  if (!runner) return () => {};
3014
2850
  return startGuardedSessionDecode({
3015
2851
  logger: log,
3016
- acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.motionStreamId, `session-decode:motion:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"])),
2852
+ acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.motionStreamId, `session-decode:motion:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config)),
3017
2853
  startPump: (source) => {
3018
2854
  log.info("session-decode: motion routed through per-session decode worker (gray)", { meta: {
3019
2855
  streamId: config.motionStreamId,
@@ -3195,9 +3031,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
3195
3031
  log.error("runInference: this.ctx.api not available");
3196
3032
  return null;
3197
3033
  }
3198
- const camConfig = this.attached.get(deviceId)?.config;
3199
- const steps = camConfig?.steps;
3200
- const engine = camConfig?.engine;
3034
+ const steps = (this.attached.get(deviceId)?.config)?.steps;
3201
3035
  if (!steps) {
3202
3036
  log.warn("runInference: no steps in attach config — skipping frame (legacy attach?)");
3203
3037
  return null;
@@ -3208,8 +3042,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
3208
3042
  return await api.pipelineExecutor.runPipeline.mutate({
3209
3043
  steps: [...steps],
3210
3044
  ...useHandle ? { frameHandle: handle } : { frame },
3211
- deviceId,
3212
- ...engine ? { engine } : {}
3045
+ deviceId
3213
3046
  });
3214
3047
  } catch (err) {
3215
3048
  const msg = require_dist.errMsg(err);
@@ -3572,9 +3405,11 @@ exports.FrameQueue = FrameQueue;
3572
3405
  exports.PipelineRunner = PipelineRunner;
3573
3406
  exports.PipelineTimingSampler = PipelineTimingSampler;
3574
3407
  exports.Semaphore = Semaphore;
3408
+ exports.acquireSessionDecodeRestreamFrom = acquireSessionDecodeRestreamFrom;
3575
3409
  exports.customActions = pipelineRunnerBenchActions;
3576
3410
  exports.default = PipelineRunnerAddon;
3577
3411
  exports.resolveMotionFrameSource = resolveMotionFrameSource;
3578
3412
  exports.resolveSessionDecodeHostname = resolveSessionDecodeHostname;
3413
+ exports.resolveSessionDecodeOwnerNodeId = resolveSessionDecodeOwnerNodeId;
3579
3414
  exports.rgbFrameToGrayFrame = rgbFrameToGrayFrame;
3580
3415
  exports.shouldStartOnboardAnalyzer = shouldStartOnboardAnalyzer;