@camstack/addon-pipeline 1.2.218 → 1.2.220

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 (38) hide show
  1. package/dist/audio-analyzer/index.js +49 -5
  2. package/dist/audio-analyzer/index.mjs +46 -3
  3. package/dist/chunk-emK7D4bc.js +35 -0
  4. package/dist/{default-detection-model-AJyhoTLF.mjs → default-detection-model-DBNEc3iw.mjs} +1 -1
  5. package/dist/{default-detection-model-Bf45EQ98.js → default-detection-model-x6oNMBDH.js} +1 -1
  6. package/dist/detection-pipeline/index.js +10 -9
  7. package/dist/detection-pipeline/index.mjs +4 -4
  8. package/dist/{dist-BbvtiBNc.js → dist-ByPsWlTa.js} +57 -38
  9. package/dist/{dist-Dl6GntEh.mjs → dist-ClihWGOZ.mjs} +57 -3
  10. package/dist/{lazy-sharp-L9Lcr62v.js → lazy-sharp-BuwKlQLR.js} +2 -2
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-CbCVHEvJ.js → node-CNPnaux4.js} +4 -3
  14. package/dist/{node-CEyw52D2.mjs → node-DiaTmz84.mjs} +1 -1
  15. package/dist/pipeline-runner/index.js +222 -157
  16. package/dist/pipeline-runner/index.mjs +218 -154
  17. package/dist/{process-memory-DBxdiCSF.js → process-memory-9KqRtUHt.js} +3 -2
  18. package/dist/{process-memory-Du00R6-e.mjs → process-memory-D4t8hpJd.mjs} +1 -1
  19. package/dist/recorder/index.js +334 -123
  20. package/dist/recorder/index.mjs +332 -122
  21. package/dist/{segment-demux-js-B278aBtB.mjs → segment-demux-js-CFFXVae4.mjs} +1 -1
  22. package/dist/{segment-demux-js-D7bjzY8M.js → segment-demux-js-CZefXDLx.js} +4 -3
  23. package/dist/session-decode/decode-worker-child.js +155 -74
  24. package/dist/session-decode/decode-worker-child.mjs +154 -73
  25. package/dist/stream-broker/_stub.js +1 -1
  26. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BnmbfnAd.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CtdpN7Sz.mjs} +3 -3
  27. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-yJ2CyixO.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-7hqrrd88.mjs} +1 -1
  28. package/dist/stream-broker/demux-worker-child.js +1 -1
  29. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  30. package/dist/stream-broker/{hostInit-MIKQqs86.mjs → hostInit-McoUiljf.mjs} +3 -3
  31. package/dist/stream-broker/index.js +24 -25
  32. package/dist/stream-broker/index.mjs +17 -19
  33. package/dist/stream-broker/remoteEntry.js +1 -1
  34. package/dist/worker-protocol-B2MfQLlu.js +184 -0
  35. package/dist/worker-protocol-C_W-P_g-.mjs +161 -0
  36. package/package.json +1 -1
  37. package/dist/worker-protocol-BEbD5adq.mjs +0 -264
  38. package/dist/worker-protocol-C4rh5uiS.js +0 -299
@@ -2,26 +2,27 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BbvtiBNc.js");
6
- const require_node = require("../node-CbCVHEvJ.js");
5
+ const require_chunk = require("../chunk-emK7D4bc.js");
6
+ const require_dist = require("../dist-ByPsWlTa.js");
7
+ const require_node = require("../node-CNPnaux4.js");
7
8
  const require_restream_intent = require("../restream-intent-B1Difcnq.js");
8
9
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
9
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
10
- const require_segment_demux_js = require("../segment-demux-js-D7bjzY8M.js");
11
+ const require_segment_demux_js = require("../segment-demux-js-CZefXDLx.js");
11
12
  let node_os = require("node:os");
12
- node_os = require_dist.__toESM(node_os);
13
+ node_os = require_chunk.__toESM(node_os);
13
14
  let node_crypto = require("node:crypto");
14
- node_crypto = require_dist.__toESM(node_crypto);
15
+ node_crypto = require_chunk.__toESM(node_crypto);
15
16
  let node_fs = require("node:fs");
16
- node_fs = require_dist.__toESM(node_fs);
17
+ node_fs = require_chunk.__toESM(node_fs);
17
18
  let node_path = require("node:path");
18
- node_path = require_dist.__toESM(node_path);
19
+ node_path = require_chunk.__toESM(node_path);
19
20
  let node_child_process = require("node:child_process");
20
21
  let node_fs_promises = require("node:fs/promises");
21
22
  let node_url = require("node:url");
22
23
  let node_module = require("node:module");
23
24
  let node_net = require("node:net");
24
- node_net = require_dist.__toESM(node_net);
25
+ node_net = require_chunk.__toESM(node_net);
25
26
  let node_dgram = require("node:dgram");
26
27
  let events = require("events");
27
28
  let net = require("net");
@@ -8380,14 +8381,14 @@ function depacketizeRfc3640(payload, opts = {
8380
8381
  }
8381
8382
  //#endregion
8382
8383
  //#region src/stream-broker/rtsp/audio-rtp-decoder.ts
8383
- /** Target sample rate for decoded audio (matches ffmpeg path). */
8384
- var TARGET_SAMPLE_RATE = 16e3;
8385
- var CHUNK_SAMPLES = TARGET_SAMPLE_RATE * 500 / 1e3;
8384
+ /** G.711 is 8 kHz by definition (RFC 3551): the plane carries that rate. */
8385
+ var G711_SAMPLE_RATE = 8e3;
8386
+ var CHUNK_SAMPLES = G711_SAMPLE_RATE * 500 / 1e3;
8386
8387
  /**
8387
- * Decodes G.711 audio RTP packets into PCM f32le chunks.
8388
+ * Decodes G.711 audio RTP packets into PCM f32le chunks at 8 kHz.
8388
8389
  *
8389
- * Buffers decoded samples and emits DecodedAudioChunk when enough
8390
- * samples accumulate (~0.5s), matching the ffmpeg audio output cadence.
8390
+ * Buffers decoded samples and emits a DecodedAudioChunk when ~0.5 s has
8391
+ * accumulated (4000 samples, 16 000 bytes).
8391
8392
  */
8392
8393
  var AudioRtpDecoder = class {
8393
8394
  codec;
@@ -8401,18 +8402,14 @@ var AudioRtpDecoder = class {
8401
8402
  }
8402
8403
  /**
8403
8404
  * Process a raw RTP packet (header + payload).
8404
- * Strips the 12-byte RTP header, decodes G.711 payload, upsamples 8→16kHz.
8405
+ * Strips the 12-byte RTP header and decodes the G.711 payload: one byte,
8406
+ * one 8 kHz sample. Nothing is interpolated here.
8405
8407
  */
8406
8408
  processPacket(rtpData) {
8407
8409
  if (rtpData.length <= 12) return;
8408
8410
  const payload = rtpData.subarray(12);
8409
8411
  const decode = this.codec === "PCMU" ? decodeUlaw : decodeAlaw;
8410
- for (let i = 0; i < payload.length; i++) {
8411
- const current = decode(payload[i]);
8412
- const next = i + 1 < payload.length ? decode(payload[i + 1]) : current;
8413
- this.pushSample(current);
8414
- this.pushSample((current + next) * .5);
8415
- }
8412
+ for (let i = 0; i < payload.length; i++) this.pushSample(decode(payload[i]));
8416
8413
  }
8417
8414
  /** Flush any remaining buffered samples as a partial chunk. */
8418
8415
  flush() {
@@ -8420,7 +8417,7 @@ var AudioRtpDecoder = class {
8420
8417
  const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, this.bufferOffset * 4);
8421
8418
  this.onChunk({
8422
8419
  data: Buffer.from(data),
8423
- sampleRate: TARGET_SAMPLE_RATE,
8420
+ sampleRate: G711_SAMPLE_RATE,
8424
8421
  channels: 1,
8425
8422
  timestamp: Date.now()
8426
8423
  });
@@ -8436,7 +8433,7 @@ var AudioRtpDecoder = class {
8436
8433
  const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, CHUNK_SAMPLES * 4);
8437
8434
  this.onChunk({
8438
8435
  data: Buffer.from(data),
8439
- sampleRate: TARGET_SAMPLE_RATE,
8436
+ sampleRate: G711_SAMPLE_RATE,
8440
8437
  channels: 1,
8441
8438
  timestamp: Date.now()
8442
8439
  });
@@ -13694,7 +13691,9 @@ var StreamBroker = class StreamBroker {
13694
13691
  this.logger?.info("native: audio decoder initialized", { meta: {
13695
13692
  codec: codecUpper,
13696
13693
  sampleRate: audioTrack.clockRate,
13697
- channels: audioTrack.channels
13694
+ channels: audioTrack.channels,
13695
+ planeSampleRate: G711_SAMPLE_RATE,
13696
+ planeFormat: "f32le"
13698
13697
  } });
13699
13698
  } else if (this.audioCodecApi) {
13700
13699
  const capCfg = this.buildAudioCodecSessionConfig(audioTrack);
@@ -27485,7 +27484,7 @@ async function demuxSegmentToPcmu(bytes, ffmpegPath = "ffmpeg") {
27485
27484
  }
27486
27485
  //#endregion
27487
27486
  //#region src/stream-broker/recorded/segment-audio-js.ts
27488
- var import_mp4box_all = /* @__PURE__ */ require_dist.__toESM(require_segment_demux_js.require_mp4box_all());
27487
+ var import_mp4box_all = /* @__PURE__ */ require_chunk.__toESM(require_segment_demux_js.require_mp4box_all());
27489
27488
  /** True for an mp4box audio codec string that is Opus (`Opus`/`opus`). */
27490
27489
  function isOpusCodec(codec) {
27491
27490
  return /^opus$/i.test(codec);
@@ -1,10 +1,10 @@
1
1
  import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
2
- import { A as addonWidgetsSourceCapability, Bt as invocationFromEncodeProfile, C as RATE_CONTROL_RELAXED, F as cameraStreamsCapability, Ft as webrtcSessionCapability, Gt as DeviceFeature, H as deriveBatteryPresence, Ht as BOOT_RECOVERY_BACKOFF_MS, I as createHwAccelCache, It as errMsg, K as egressTranscodeSharingKey, Kt as DeviceType, L as createLogChannelsProvider, Lt as AUDIO_PRESETS, Mt as streamBrokerCapability, O as RingBuffer, P as batteryCapability, Qt as nodePin, Tt as resolveEgressDecodeHwAccel, Ut as BaseAddon, Vt as isSoftwareDecode, Wt as CAM_PROFILE_ORDER, Xt as makeProfileBrokerId, Yt as isEvent, Zt as makeSourceBrokerId, an as boolean, at as logChannelsCapability, cn as literal, en as parseProfileBrokerId, fn as record, h as EncodeProfileSchema, hn as EventCategory, i as BatteryStatusSchema, in as array, it as loadContributionCapability, ln as number, mn as union, on as discriminatedUnion, pn as string, q as egressTransportFromRequest, qt as createEvent, rn as _enum, st as maskUrlCredentials, un as object, w as RATE_CONTROL_TIGHT, z as declareLogChannel, zt as buildFfmpegArgs$1 } from "../dist-Dl6GntEh.mjs";
3
- import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-CEyw52D2.mjs";
2
+ import { A as addonWidgetsSourceCapability, Bt as invocationFromEncodeProfile, C as RATE_CONTROL_RELAXED, F as cameraStreamsCapability, Ft as webrtcSessionCapability, Gt as DeviceFeature, H as deriveBatteryPresence, Ht as BOOT_RECOVERY_BACKOFF_MS, I as createHwAccelCache, It as errMsg, K as egressTranscodeSharingKey, Kt as DeviceType, L as createLogChannelsProvider, Lt as AUDIO_PRESETS, Mt as streamBrokerCapability, O as RingBuffer, P as batteryCapability, Qt as nodePin, Tt as resolveEgressDecodeHwAccel, Ut as BaseAddon, Vt as isSoftwareDecode, Wt as CAM_PROFILE_ORDER, Xt as makeProfileBrokerId, Yt as isEvent, Zt as makeSourceBrokerId, an as boolean, at as logChannelsCapability, cn as literal, en as parseProfileBrokerId, fn as record, h as EncodeProfileSchema, hn as EventCategory, i as BatteryStatusSchema, in as array, it as loadContributionCapability, ln as number, mn as union, on as discriminatedUnion, pn as string, q as egressTransportFromRequest, qt as createEvent, rn as _enum, st as maskUrlCredentials, un as object, w as RATE_CONTROL_TIGHT, z as declareLogChannel, zt as buildFfmpegArgs$1 } from "../dist-ClihWGOZ.mjs";
3
+ import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-DiaTmz84.mjs";
4
4
  import { c as PREBUFFER_MAX_MS, d as resolvePrebuffer, i as parseRestreamPath, l as capPreRollRequestMs, n as NO_DECLARATION, u as prebufferRetentionMs } from "../restream-intent-DWe2kInY.mjs";
5
5
  import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
6
6
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
7
- import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-B278aBtB.mjs";
7
+ import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-CFFXVae4.mjs";
8
8
  import { createRequire } from "node:module";
9
9
  import * as os from "node:os";
10
10
  import { networkInterfaces, tmpdir } from "node:os";
@@ -8377,14 +8377,14 @@ function depacketizeRfc3640(payload, opts = {
8377
8377
  }
8378
8378
  //#endregion
8379
8379
  //#region src/stream-broker/rtsp/audio-rtp-decoder.ts
8380
- /** Target sample rate for decoded audio (matches ffmpeg path). */
8381
- var TARGET_SAMPLE_RATE = 16e3;
8382
- var CHUNK_SAMPLES = TARGET_SAMPLE_RATE * 500 / 1e3;
8380
+ /** G.711 is 8 kHz by definition (RFC 3551): the plane carries that rate. */
8381
+ var G711_SAMPLE_RATE = 8e3;
8382
+ var CHUNK_SAMPLES = G711_SAMPLE_RATE * 500 / 1e3;
8383
8383
  /**
8384
- * Decodes G.711 audio RTP packets into PCM f32le chunks.
8384
+ * Decodes G.711 audio RTP packets into PCM f32le chunks at 8 kHz.
8385
8385
  *
8386
- * Buffers decoded samples and emits DecodedAudioChunk when enough
8387
- * samples accumulate (~0.5s), matching the ffmpeg audio output cadence.
8386
+ * Buffers decoded samples and emits a DecodedAudioChunk when ~0.5 s has
8387
+ * accumulated (4000 samples, 16 000 bytes).
8388
8388
  */
8389
8389
  var AudioRtpDecoder = class {
8390
8390
  codec;
@@ -8398,18 +8398,14 @@ var AudioRtpDecoder = class {
8398
8398
  }
8399
8399
  /**
8400
8400
  * Process a raw RTP packet (header + payload).
8401
- * Strips the 12-byte RTP header, decodes G.711 payload, upsamples 8→16kHz.
8401
+ * Strips the 12-byte RTP header and decodes the G.711 payload: one byte,
8402
+ * one 8 kHz sample. Nothing is interpolated here.
8402
8403
  */
8403
8404
  processPacket(rtpData) {
8404
8405
  if (rtpData.length <= 12) return;
8405
8406
  const payload = rtpData.subarray(12);
8406
8407
  const decode = this.codec === "PCMU" ? decodeUlaw : decodeAlaw;
8407
- for (let i = 0; i < payload.length; i++) {
8408
- const current = decode(payload[i]);
8409
- const next = i + 1 < payload.length ? decode(payload[i + 1]) : current;
8410
- this.pushSample(current);
8411
- this.pushSample((current + next) * .5);
8412
- }
8408
+ for (let i = 0; i < payload.length; i++) this.pushSample(decode(payload[i]));
8413
8409
  }
8414
8410
  /** Flush any remaining buffered samples as a partial chunk. */
8415
8411
  flush() {
@@ -8417,7 +8413,7 @@ var AudioRtpDecoder = class {
8417
8413
  const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, this.bufferOffset * 4);
8418
8414
  this.onChunk({
8419
8415
  data: Buffer.from(data),
8420
- sampleRate: TARGET_SAMPLE_RATE,
8416
+ sampleRate: G711_SAMPLE_RATE,
8421
8417
  channels: 1,
8422
8418
  timestamp: Date.now()
8423
8419
  });
@@ -8433,7 +8429,7 @@ var AudioRtpDecoder = class {
8433
8429
  const data = Buffer.from(this.buffer.buffer, this.buffer.byteOffset, CHUNK_SAMPLES * 4);
8434
8430
  this.onChunk({
8435
8431
  data: Buffer.from(data),
8436
- sampleRate: TARGET_SAMPLE_RATE,
8432
+ sampleRate: G711_SAMPLE_RATE,
8437
8433
  channels: 1,
8438
8434
  timestamp: Date.now()
8439
8435
  });
@@ -13691,7 +13687,9 @@ var StreamBroker = class StreamBroker {
13691
13687
  this.logger?.info("native: audio decoder initialized", { meta: {
13692
13688
  codec: codecUpper,
13693
13689
  sampleRate: audioTrack.clockRate,
13694
- channels: audioTrack.channels
13690
+ channels: audioTrack.channels,
13691
+ planeSampleRate: G711_SAMPLE_RATE,
13692
+ planeFormat: "f32le"
13695
13693
  } });
13696
13694
  } else if (this.audioCodecApi) {
13697
13695
  const capCfg = this.buildAudioCodecSessionConfig(audioTrack);
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BnmbfnAd.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CtdpN7Sz.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
@@ -0,0 +1,184 @@
1
+ //#region src/session-decode/native-lease-knobs.ts
2
+ /** Every knob, in the order the spawn line names them. */
3
+ var NATIVE_LEASE_KNOB_NAMES = [
4
+ "holdFrames",
5
+ "budgetMb",
6
+ "activityMs",
7
+ "admission",
8
+ "tileBudgetMb",
9
+ "sceneBudgetMb"
10
+ ];
11
+ /** The numeric knobs — every knob but `admission`. */
12
+ var NATIVE_LEASE_NUMBER_KNOBS = [
13
+ "holdFrames",
14
+ "budgetMb",
15
+ "activityMs",
16
+ "tileBudgetMb",
17
+ "sceneBudgetMb"
18
+ ];
19
+ var KNOB_SOURCES = [
20
+ "setting",
21
+ "env",
22
+ "default"
23
+ ];
24
+ function isRecord$1(value) {
25
+ return typeof value === "object" && value !== null;
26
+ }
27
+ function isNonNegativeInteger(value) {
28
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
29
+ }
30
+ function isAdmission(value) {
31
+ return value === "all" || value === "inferred";
32
+ }
33
+ function isKnobSource(value) {
34
+ return typeof value === "string" && KNOB_SOURCES.some((s) => s === value);
35
+ }
36
+ /**
37
+ * Shape guard for the wire. The parent already bounded every value through the
38
+ * knob's own Zod schema; what is checked here is what a store constructor would
39
+ * otherwise turn into a silent misconfiguration — a non-integer, a negative
40
+ * budget, a hold of zero (a worker that holds nothing can crop nothing), an
41
+ * admission word that is neither mode, a source that names no level.
42
+ */
43
+ function isWorkerLeaseKnobs(value) {
44
+ if (!isRecord$1(value) || !isRecord$1(value.values) || !isRecord$1(value.sources)) return false;
45
+ const { values, sources } = value;
46
+ for (const knob of NATIVE_LEASE_NUMBER_KNOBS) if (!isNonNegativeInteger(values[knob])) return false;
47
+ if (!isNonNegativeInteger(values.holdFrames) || values.holdFrames < 1) return false;
48
+ if (!isAdmission(values.admission)) return false;
49
+ return NATIVE_LEASE_KNOB_NAMES.every((knob) => isKnobSource(sources[knob]));
50
+ }
51
+ /**
52
+ * The one-line spawn diagnostic. Names the effective value AND its source for
53
+ * every knob: without the source, a log saying `holdFrames=8` cannot distinguish
54
+ * "the operator's setting arrived" from "someone left a shell variable set", and
55
+ * a setting that silently fails to arrive is exactly the failure the source
56
+ * field exists to make visible.
57
+ */
58
+ function formatNativeLeaseKnobs(resolved) {
59
+ const part = (knob) => `${knob}=${resolved.values[knob]} source=${resolved.sources[knob]}`;
60
+ return `native lease knobs — ${NATIVE_LEASE_KNOB_NAMES.map(part).join("; ")}`;
61
+ }
62
+ //#endregion
63
+ //#region src/session-decode/worker-protocol.ts
64
+ /**
65
+ * Typed IPC protocol between the per-session decode coordinator (Task 3 —
66
+ * `child_process.fork`s the worker) and the decode-worker child
67
+ * (`decode-worker-child.ts`, Task 2). Pure module: no node-av, no
68
+ * `process.on`/`process.send` here — those live in `decode-worker-child.ts`,
69
+ * which documents why raw IPC is sanctioned for that file.
70
+ *
71
+ * The protocol is a request/response pull: the parent asks for the next
72
+ * frame (`pull`), gets back metadata + an opaque `frameId`, then asks for
73
+ * pixel bytes for that id with an optional ROI (`toBuffer`). No shared
74
+ * memory, no `/dev/shm` — frames travel as IPC messages only.
75
+ */
76
+ /**
77
+ * Classify a worker diagnostic line into a {@link WorkerLogLevel}. Failure /
78
+ * fault lines surface at `warn` (visible at the default log level so a
79
+ * native-crop failure is finally seen in `addons.getLogs`); routine
80
+ * provenance / transition / lifecycle lines stay `debug`. Pure + exported so
81
+ * the mapping is unit-testable without spawning a worker.
82
+ */
83
+ function logLevelForLine(line) {
84
+ return /\b(fail(?:ed|ure)?|error|exception|unhandled rejection|corrupt|EIO|EINVAL)\b/i.test(line) ? "warn" : "debug";
85
+ }
86
+ function isRecord(value) {
87
+ return typeof value === "object" && value !== null;
88
+ }
89
+ function isFiniteNumber(value) {
90
+ return typeof value === "number" && Number.isFinite(value);
91
+ }
92
+ function isWorkerFrameFormat(value) {
93
+ return value === "rgb" || value === "gray";
94
+ }
95
+ function isWorkerBufferFormat(value) {
96
+ return isWorkerFrameFormat(value) || value === "jpeg";
97
+ }
98
+ function isWorkerLogLevel(value) {
99
+ return value === "debug" || value === "info" || value === "warn" || value === "error";
100
+ }
101
+ function isWorkerCropRegion(value) {
102
+ return isRecord(value) && isFiniteNumber(value.left) && isFiniteNumber(value.top) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
103
+ }
104
+ function isWorkerResizeTarget(value) {
105
+ return isRecord(value) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
106
+ }
107
+ function isFrameImageOptions(value) {
108
+ if (!isRecord(value)) return false;
109
+ if (value.crop !== void 0 && !isWorkerCropRegion(value.crop)) return false;
110
+ if (value.resize !== void 0 && !isWorkerResizeTarget(value.resize)) return false;
111
+ if (value.format !== void 0 && !isWorkerBufferFormat(value.format)) return false;
112
+ return true;
113
+ }
114
+ function isNormalizedBbox(value) {
115
+ return isRecord(value) && isFiniteNumber(value.x) && isFiniteNumber(value.y) && isFiniteNumber(value.w) && isFiniteNumber(value.h);
116
+ }
117
+ function isVideoFrameSource(value) {
118
+ return isRecord(value) && typeof value.restreamUrl === "string" && isFiniteNumber(value.deviceId) && typeof value.codec === "string";
119
+ }
120
+ function isVideoFrameGenOptions(value) {
121
+ if (!isRecord(value)) return false;
122
+ if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
123
+ if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
124
+ if (value.preRollMs !== void 0 && !isFiniteNumber(value.preRollMs)) return false;
125
+ if (value.preRollMaxFrames !== void 0 && !isFiniteNumber(value.preRollMaxFrames)) return false;
126
+ return true;
127
+ }
128
+ /** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
129
+ function isWorkerRequest(value) {
130
+ if (!isRecord(value)) return false;
131
+ switch (value.kind) {
132
+ case "start": return isVideoFrameSource(value.source) && isVideoFrameGenOptions(value.opts) && isWorkerLeaseKnobs(value.lease);
133
+ case "pull": return true;
134
+ case "toBuffer": return isFiniteNumber(value.frameId) && (value.opts === void 0 || isFrameImageOptions(value.opts));
135
+ case "nativeCrop": return isFiniteNumber(value.requestId) && isFiniteNumber(value.frameId) && isNormalizedBbox(value.bbox) && (value.maxWidth === void 0 || isFiniteNumber(value.maxWidth));
136
+ case "cutTiles": return isFiniteNumber(value.frameId) && Array.isArray(value.bboxes) && value.bboxes.every(isNormalizedBbox);
137
+ case "releaseNativeLease":
138
+ case "markInferred": return isFiniteNumber(value.frameId);
139
+ case "stop": return true;
140
+ default: return false;
141
+ }
142
+ }
143
+ /** Discriminated-union guard for {@link WorkerReply} — no `as` casts. */
144
+ function isWorkerReply(value) {
145
+ if (!isRecord(value)) return false;
146
+ switch (value.kind) {
147
+ case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format) && (value.capturedAt === void 0 || isFiniteNumber(value.capturedAt)) && (value.preRoll === void 0 || typeof value.preRoll === "boolean");
148
+ case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
149
+ case "nativeCropResult": return isFiniteNumber(value.requestId) && value.bytes instanceof Uint8Array && isFiniteNumber(value.width) && isFiniteNumber(value.height) && (value.source === void 0 || value.source === "lease" || value.source === "tile");
150
+ case "nativeCropMiss": return isFiniteNumber(value.requestId) && (value.reason === void 0 || typeof value.reason === "string");
151
+ case "ended": return true;
152
+ case "ready": return typeof value.warm === "boolean";
153
+ case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
154
+ case "log": return isWorkerLogLevel(value.level) && typeof value.message === "string";
155
+ case "preRollReport": return isFiniteNumber(value.offered) && isFiniteNumber(value.inferred) && isFiniteNumber(value.dropped) && isRecord(value.droppedByReason) && isFiniteNumber(value.strideMs) && typeof value.subsampled === "boolean" && isFiniteNumber(value.spanMs);
156
+ case "dialTiming": return isFiniteNumber(value.hwContextMs) && isFiniteNumber(value.demuxerOpenMs) && isFiniteNumber(value.decoderCreateMs) && isFiniteNumber(value.firstDecodeMs) && typeof value.streamInfoProbed === "boolean";
157
+ default: return false;
158
+ }
159
+ }
160
+ //#endregion
161
+ Object.defineProperty(exports, "formatNativeLeaseKnobs", {
162
+ enumerable: true,
163
+ get: function() {
164
+ return formatNativeLeaseKnobs;
165
+ }
166
+ });
167
+ Object.defineProperty(exports, "isWorkerReply", {
168
+ enumerable: true,
169
+ get: function() {
170
+ return isWorkerReply;
171
+ }
172
+ });
173
+ Object.defineProperty(exports, "isWorkerRequest", {
174
+ enumerable: true,
175
+ get: function() {
176
+ return isWorkerRequest;
177
+ }
178
+ });
179
+ Object.defineProperty(exports, "logLevelForLine", {
180
+ enumerable: true,
181
+ get: function() {
182
+ return logLevelForLine;
183
+ }
184
+ });
@@ -0,0 +1,161 @@
1
+ //#region src/session-decode/native-lease-knobs.ts
2
+ /** Every knob, in the order the spawn line names them. */
3
+ var NATIVE_LEASE_KNOB_NAMES = [
4
+ "holdFrames",
5
+ "budgetMb",
6
+ "activityMs",
7
+ "admission",
8
+ "tileBudgetMb",
9
+ "sceneBudgetMb"
10
+ ];
11
+ /** The numeric knobs — every knob but `admission`. */
12
+ var NATIVE_LEASE_NUMBER_KNOBS = [
13
+ "holdFrames",
14
+ "budgetMb",
15
+ "activityMs",
16
+ "tileBudgetMb",
17
+ "sceneBudgetMb"
18
+ ];
19
+ var KNOB_SOURCES = [
20
+ "setting",
21
+ "env",
22
+ "default"
23
+ ];
24
+ function isRecord$1(value) {
25
+ return typeof value === "object" && value !== null;
26
+ }
27
+ function isNonNegativeInteger(value) {
28
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
29
+ }
30
+ function isAdmission(value) {
31
+ return value === "all" || value === "inferred";
32
+ }
33
+ function isKnobSource(value) {
34
+ return typeof value === "string" && KNOB_SOURCES.some((s) => s === value);
35
+ }
36
+ /**
37
+ * Shape guard for the wire. The parent already bounded every value through the
38
+ * knob's own Zod schema; what is checked here is what a store constructor would
39
+ * otherwise turn into a silent misconfiguration — a non-integer, a negative
40
+ * budget, a hold of zero (a worker that holds nothing can crop nothing), an
41
+ * admission word that is neither mode, a source that names no level.
42
+ */
43
+ function isWorkerLeaseKnobs(value) {
44
+ if (!isRecord$1(value) || !isRecord$1(value.values) || !isRecord$1(value.sources)) return false;
45
+ const { values, sources } = value;
46
+ for (const knob of NATIVE_LEASE_NUMBER_KNOBS) if (!isNonNegativeInteger(values[knob])) return false;
47
+ if (!isNonNegativeInteger(values.holdFrames) || values.holdFrames < 1) return false;
48
+ if (!isAdmission(values.admission)) return false;
49
+ return NATIVE_LEASE_KNOB_NAMES.every((knob) => isKnobSource(sources[knob]));
50
+ }
51
+ /**
52
+ * The one-line spawn diagnostic. Names the effective value AND its source for
53
+ * every knob: without the source, a log saying `holdFrames=8` cannot distinguish
54
+ * "the operator's setting arrived" from "someone left a shell variable set", and
55
+ * a setting that silently fails to arrive is exactly the failure the source
56
+ * field exists to make visible.
57
+ */
58
+ function formatNativeLeaseKnobs(resolved) {
59
+ const part = (knob) => `${knob}=${resolved.values[knob]} source=${resolved.sources[knob]}`;
60
+ return `native lease knobs — ${NATIVE_LEASE_KNOB_NAMES.map(part).join("; ")}`;
61
+ }
62
+ //#endregion
63
+ //#region src/session-decode/worker-protocol.ts
64
+ /**
65
+ * Typed IPC protocol between the per-session decode coordinator (Task 3 —
66
+ * `child_process.fork`s the worker) and the decode-worker child
67
+ * (`decode-worker-child.ts`, Task 2). Pure module: no node-av, no
68
+ * `process.on`/`process.send` here — those live in `decode-worker-child.ts`,
69
+ * which documents why raw IPC is sanctioned for that file.
70
+ *
71
+ * The protocol is a request/response pull: the parent asks for the next
72
+ * frame (`pull`), gets back metadata + an opaque `frameId`, then asks for
73
+ * pixel bytes for that id with an optional ROI (`toBuffer`). No shared
74
+ * memory, no `/dev/shm` — frames travel as IPC messages only.
75
+ */
76
+ /**
77
+ * Classify a worker diagnostic line into a {@link WorkerLogLevel}. Failure /
78
+ * fault lines surface at `warn` (visible at the default log level so a
79
+ * native-crop failure is finally seen in `addons.getLogs`); routine
80
+ * provenance / transition / lifecycle lines stay `debug`. Pure + exported so
81
+ * the mapping is unit-testable without spawning a worker.
82
+ */
83
+ function logLevelForLine(line) {
84
+ return /\b(fail(?:ed|ure)?|error|exception|unhandled rejection|corrupt|EIO|EINVAL)\b/i.test(line) ? "warn" : "debug";
85
+ }
86
+ function isRecord(value) {
87
+ return typeof value === "object" && value !== null;
88
+ }
89
+ function isFiniteNumber(value) {
90
+ return typeof value === "number" && Number.isFinite(value);
91
+ }
92
+ function isWorkerFrameFormat(value) {
93
+ return value === "rgb" || value === "gray";
94
+ }
95
+ function isWorkerBufferFormat(value) {
96
+ return isWorkerFrameFormat(value) || value === "jpeg";
97
+ }
98
+ function isWorkerLogLevel(value) {
99
+ return value === "debug" || value === "info" || value === "warn" || value === "error";
100
+ }
101
+ function isWorkerCropRegion(value) {
102
+ return isRecord(value) && isFiniteNumber(value.left) && isFiniteNumber(value.top) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
103
+ }
104
+ function isWorkerResizeTarget(value) {
105
+ return isRecord(value) && isFiniteNumber(value.width) && isFiniteNumber(value.height);
106
+ }
107
+ function isFrameImageOptions(value) {
108
+ if (!isRecord(value)) return false;
109
+ if (value.crop !== void 0 && !isWorkerCropRegion(value.crop)) return false;
110
+ if (value.resize !== void 0 && !isWorkerResizeTarget(value.resize)) return false;
111
+ if (value.format !== void 0 && !isWorkerBufferFormat(value.format)) return false;
112
+ return true;
113
+ }
114
+ function isNormalizedBbox(value) {
115
+ return isRecord(value) && isFiniteNumber(value.x) && isFiniteNumber(value.y) && isFiniteNumber(value.w) && isFiniteNumber(value.h);
116
+ }
117
+ function isVideoFrameSource(value) {
118
+ return isRecord(value) && typeof value.restreamUrl === "string" && isFiniteNumber(value.deviceId) && typeof value.codec === "string";
119
+ }
120
+ function isVideoFrameGenOptions(value) {
121
+ if (!isRecord(value)) return false;
122
+ if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
123
+ if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
124
+ if (value.preRollMs !== void 0 && !isFiniteNumber(value.preRollMs)) return false;
125
+ if (value.preRollMaxFrames !== void 0 && !isFiniteNumber(value.preRollMaxFrames)) return false;
126
+ return true;
127
+ }
128
+ /** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
129
+ function isWorkerRequest(value) {
130
+ if (!isRecord(value)) return false;
131
+ switch (value.kind) {
132
+ case "start": return isVideoFrameSource(value.source) && isVideoFrameGenOptions(value.opts) && isWorkerLeaseKnobs(value.lease);
133
+ case "pull": return true;
134
+ case "toBuffer": return isFiniteNumber(value.frameId) && (value.opts === void 0 || isFrameImageOptions(value.opts));
135
+ case "nativeCrop": return isFiniteNumber(value.requestId) && isFiniteNumber(value.frameId) && isNormalizedBbox(value.bbox) && (value.maxWidth === void 0 || isFiniteNumber(value.maxWidth));
136
+ case "cutTiles": return isFiniteNumber(value.frameId) && Array.isArray(value.bboxes) && value.bboxes.every(isNormalizedBbox);
137
+ case "releaseNativeLease":
138
+ case "markInferred": return isFiniteNumber(value.frameId);
139
+ case "stop": return true;
140
+ default: return false;
141
+ }
142
+ }
143
+ /** Discriminated-union guard for {@link WorkerReply} — no `as` casts. */
144
+ function isWorkerReply(value) {
145
+ if (!isRecord(value)) return false;
146
+ switch (value.kind) {
147
+ case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format) && (value.capturedAt === void 0 || isFiniteNumber(value.capturedAt)) && (value.preRoll === void 0 || typeof value.preRoll === "boolean");
148
+ case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
149
+ case "nativeCropResult": return isFiniteNumber(value.requestId) && value.bytes instanceof Uint8Array && isFiniteNumber(value.width) && isFiniteNumber(value.height) && (value.source === void 0 || value.source === "lease" || value.source === "tile");
150
+ case "nativeCropMiss": return isFiniteNumber(value.requestId) && (value.reason === void 0 || typeof value.reason === "string");
151
+ case "ended": return true;
152
+ case "ready": return typeof value.warm === "boolean";
153
+ case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
154
+ case "log": return isWorkerLogLevel(value.level) && typeof value.message === "string";
155
+ case "preRollReport": return isFiniteNumber(value.offered) && isFiniteNumber(value.inferred) && isFiniteNumber(value.dropped) && isRecord(value.droppedByReason) && isFiniteNumber(value.strideMs) && typeof value.subsampled === "boolean" && isFiniteNumber(value.spanMs);
156
+ case "dialTiming": return isFiniteNumber(value.hwContextMs) && isFiniteNumber(value.demuxerOpenMs) && isFiniteNumber(value.decoderCreateMs) && isFiniteNumber(value.firstDecodeMs) && typeof value.streamInfoProbed === "boolean";
157
+ default: return false;
158
+ }
159
+ }
160
+ //#endregion
161
+ export { formatNativeLeaseKnobs as i, isWorkerRequest as n, logLevelForLine as r, isWorkerReply as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.218",
3
+ "version": "1.2.220",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",