@camstack/addon-pipeline 1.2.93 → 1.2.95

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/{addon-utils-CTRpEiSd.js → addon-utils-UMWiMAxR.js} +1 -1
  2. package/dist/audio-analyzer/index.js +58 -4
  3. package/dist/audio-analyzer/index.mjs +56 -2
  4. package/dist/detection-pipeline/index.js +194 -6
  5. package/dist/detection-pipeline/index.mjs +191 -3
  6. package/dist/{dist-6POVIYmC.mjs → dist-C49o1k7u.mjs} +359 -21
  7. package/dist/{dist-D9To_r_d.js → dist-hyryByiw.js} +376 -20
  8. package/dist/{event-loop-stall-monitor-C-VvWOxS.mjs → event-loop-stall-monitor-CfxZBgNi.mjs} +1 -1
  9. package/dist/{event-loop-stall-monitor-CGacbzXq.js → event-loop-stall-monitor-DPNTzGOT.js} +1 -1
  10. package/dist/{lazy-sharp-C3zOnmiU.js → lazy-sharp-Dxg6UeW-.js} +1 -1
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +195 -14
  14. package/dist/pipeline-runner/index.mjs +194 -13
  15. package/dist/process-memory-B9-UD7Rp.js +78 -0
  16. package/dist/process-memory-DXOB6eHX.mjs +66 -0
  17. package/dist/recorder/index.js +943 -109
  18. package/dist/recorder/index.mjs +942 -108
  19. package/dist/session-decode/decode-worker-child.js +4 -4
  20. package/dist/session-decode/decode-worker-child.mjs +3 -3
  21. package/dist/stream-broker/_stub.js +1 -1
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C2woB6bY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CF-c0cDZ.mjs} +3 -3
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BjvmJK_d.mjs +26 -0
  24. package/dist/stream-broker/{hostInit-VCURcZLt.mjs → hostInit-Cpcf2hDB.mjs} +3 -3
  25. package/dist/stream-broker/index.js +25 -7
  26. package/dist/stream-broker/index.mjs +25 -7
  27. package/dist/stream-broker/remoteEntry.js +1 -1
  28. package/dist/{worker-protocol-Bq28qobd.js → worker-protocol-DR_a77D5.js} +1 -1
  29. package/dist/{worker-protocol-PzAKg8mC.mjs → worker-protocol-DqQ2GI4M.mjs} +1 -1
  30. package/package.json +13 -7
  31. package/python/inference_pool.py +178 -3
  32. package/python/test_inference_pool_backpressure.py +7 -1
  33. package/python/test_inference_pool_memstats.py +146 -0
  34. package/dist/node-topology-platform-BkR_k6WT.mjs +0 -15
  35. package/dist/node-topology-platform-CFZ7F4xW.js +0 -20
  36. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Dut56EeQ.mjs +0 -26
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-D9To_r_d.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CGacbzXq.js");
7
- const require_worker_protocol = require("../worker-protocol-Bq28qobd.js");
5
+ const require_dist = require("../dist-hyryByiw.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DPNTzGOT.js");
7
+ const require_worker_protocol = require("../worker-protocol-DR_a77D5.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
- const require_lazy_sharp = require("../lazy-sharp-C3zOnmiU.js");
9
+ const require_lazy_sharp = require("../lazy-sharp-Dxg6UeW-.js");
10
10
  const require_remote_restream = require("../remote-restream-BYbAsgUf.js");
11
11
  let sharp = require("sharp");
12
12
  sharp = require_dist.__toESM(sharp);
@@ -134,6 +134,8 @@ function startGuardedSessionDecode(deps) {
134
134
  let stopPump = null;
135
135
  let release = null;
136
136
  let retryTimer = null;
137
+ /** The live-worker slot held by the RUNNING session, released on teardown. */
138
+ let workerLease = null;
137
139
  const delays = deps.acquireRetryDelaysMs && deps.acquireRetryDelaysMs.length > 0 ? deps.acquireRetryDelaysMs : DEFAULT_ACQUIRE_RETRY_DELAYS_MS;
138
140
  /** Resolves after the backoff for `attempt`, or immediately once aborted. */
139
141
  const waitBackoff = (attempt) => new Promise((resolve) => {
@@ -146,10 +148,19 @@ function startGuardedSessionDecode(deps) {
146
148
  });
147
149
  (async () => {
148
150
  for (let attempt = 0; !aborted; attempt++) {
151
+ let lease = null;
152
+ if (deps.admitWorker) {
153
+ lease = deps.admitWorker();
154
+ if (!lease) {
155
+ await waitBackoff(attempt);
156
+ continue;
157
+ }
158
+ }
149
159
  let acquired = null;
150
160
  try {
151
161
  acquired = await deps.acquire();
152
162
  } catch (err) {
163
+ lease?.release();
153
164
  (attempt === 0 ? deps.logger.warn : deps.logger.debug).call(deps.logger, "session-decode: acquire failed — retrying", { meta: {
154
165
  attempt: attempt + 1,
155
166
  error: require_dist.errMsg(err)
@@ -158,16 +169,19 @@ function startGuardedSessionDecode(deps) {
158
169
  continue;
159
170
  }
160
171
  if (aborted) {
172
+ lease?.release();
161
173
  if (acquired) acquired.release();
162
174
  return;
163
175
  }
164
176
  if (!acquired) {
177
+ lease?.release();
165
178
  (attempt === 0 ? deps.logger.warn : deps.logger.debug).call(deps.logger, "session-decode: no broker profile bound yet — retrying", { meta: { attempt: attempt + 1 } });
166
179
  await waitBackoff(attempt);
167
180
  continue;
168
181
  }
169
182
  if (attempt > 0) deps.logger.info("session-decode: acquire succeeded after retry", { meta: { attempts: attempt + 1 } });
170
183
  release = acquired.release;
184
+ workerLease = lease;
171
185
  stopPump = deps.startPump(acquired.source);
172
186
  return;
173
187
  }
@@ -181,6 +195,8 @@ function startGuardedSessionDecode(deps) {
181
195
  retryTimer = null;
182
196
  }
183
197
  stopPump?.();
198
+ workerLease?.release();
199
+ workerLease = null;
184
200
  if (release) release();
185
201
  };
186
202
  }
@@ -1394,7 +1410,9 @@ var RunSyntheticBenchInputSchema = require_dist.object({
1394
1410
  sessionId: require_dist.string().optional(),
1395
1411
  simulatePipeline: require_dist.boolean().optional(),
1396
1412
  nodeId: require_dist.string().optional(),
1397
- engine: BenchEngineChoiceSchema.optional()
1413
+ engine: BenchEngineChoiceSchema.optional(),
1414
+ deviceKey: require_dist.string().optional(),
1415
+ plane: require_dist._enum(["full", "frame"]).optional()
1398
1416
  });
1399
1417
  var TimingSplitSchema = require_dist.object({
1400
1418
  mean: require_dist.number(),
@@ -1449,6 +1467,48 @@ var pipelineRunnerBenchActions = require_dist.defineCustomActions({
1449
1467
  abortCapacityRamp: require_dist.customAction(AbortCapacityRampInputSchema, AbortCapacityRampResultSchema, { kind: "mutation" })
1450
1468
  });
1451
1469
  //#endregion
1470
+ //#region src/pipeline-runner/bench-dispatch.ts
1471
+ /**
1472
+ * Why the cached fast path is unavailable, or `null` if nothing blocks it.
1473
+ *
1474
+ * The fast path is `cacheFrameInPool` once + `inferCached` per call, and
1475
+ * NEITHER cap method takes a `deviceKey` or a `plane` — only `runPipeline`
1476
+ * does. Forwarding a device selector into it would scope the WARMUP
1477
+ * `runPipeline` to the chosen accelerator and leave every TIMED call on the
1478
+ * node's default pool: a measurement of the wrong device, reported as the
1479
+ * right one. Refusing the fast path costs a slower bench and buys a true
1480
+ * number.
1481
+ */
1482
+ function fastPathRefusalReason(input) {
1483
+ if (input.deviceKey) return `deviceKey '${input.deviceKey}' requested — inferCached has no device selector, so the timed calls would land on the node's default pool; using the runPipeline path instead`;
1484
+ if (input.plane) return `plane '${input.plane}' requested — inferCached has no plane selector; using the runPipeline path instead`;
1485
+ return null;
1486
+ }
1487
+ /**
1488
+ * The cached fast path is only for a plain, single-step, default-device,
1489
+ * default-plane inference bench.
1490
+ */
1491
+ function canUseCachedFastPath(isSingleStep, input) {
1492
+ return isSingleStep && !input.simulatePipeline && fastPathRefusalReason(input) === null;
1493
+ }
1494
+ /**
1495
+ * Build the `pipelineExecutor.runPipeline` argument for one bench call.
1496
+ *
1497
+ * Every optional is ABSENT when unset rather than present-and-undefined: an
1498
+ * omitted `deviceKey` means "the runner's default device" and an omitted
1499
+ * `plane` means the cap's `'full'` default, and the two are not the same as
1500
+ * an explicit `undefined` on a `.loose()` router input.
1501
+ */
1502
+ function buildBenchRunPipelineInput(req) {
1503
+ return {
1504
+ steps: [...req.steps],
1505
+ frame: req.frame,
1506
+ ...req.engine ? { engine: req.engine } : {},
1507
+ ...req.deviceKey ? { deviceKey: req.deviceKey } : {},
1508
+ ...req.plane ? { plane: req.plane } : {}
1509
+ };
1510
+ }
1511
+ //#endregion
1452
1512
  //#region src/pipeline-runner/capacity/capacity-runner.ts
1453
1513
  /**
1454
1514
  * Run ONE ramp step: `cameras` independent open-loop pacers for
@@ -3870,6 +3930,76 @@ var PipelineRunner = class {
3870
3930
  }
3871
3931
  };
3872
3932
  //#endregion
3933
+ //#region src/session-decode/live-worker-admission.ts
3934
+ /**
3935
+ * The node's live-decode-worker budget.
3936
+ *
3937
+ * One instance per runner addon. Not a semaphore: a semaphore's contract is
3938
+ * "wait", and waiting is the thing this deliberately does not do — see
3939
+ * {@link DEFAULT_MAX_LIVE_DECODE_WORKERS}.
3940
+ */
3941
+ var LiveDecodeWorkerAdmission = class {
3942
+ limit;
3943
+ logger;
3944
+ live = 0;
3945
+ constructor(options) {
3946
+ this.limit = Math.max(1, options.limit);
3947
+ this.logger = options.logger;
3948
+ }
3949
+ /** Workers currently holding a slot. */
3950
+ get inFlight() {
3951
+ return this.live;
3952
+ }
3953
+ get ceiling() {
3954
+ return this.limit;
3955
+ }
3956
+ /**
3957
+ * Apply an operator-changed ceiling.
3958
+ *
3959
+ * Lowering it never kills a running session — a decode worker torn down
3960
+ * mid-motion loses the event it was started for. It only stops the next
3961
+ * admission, so the count drains to the new ceiling as sessions end.
3962
+ */
3963
+ resize(limit) {
3964
+ this.limit = Math.max(1, limit);
3965
+ }
3966
+ /**
3967
+ * Take a slot, or refuse.
3968
+ *
3969
+ * A refusal is a WARN and carries `tags.deviceId`, because the only useful
3970
+ * form of this question is per-camera ("why did 617 not run when 615 did?").
3971
+ * A branch that drops work silently reads as "it never happened".
3972
+ */
3973
+ admit(deviceId, label) {
3974
+ if (this.live >= this.limit) {
3975
+ this.logger.warn("session-decode: REFUSED — node live decode-worker ceiling reached, will retry", {
3976
+ tags: { deviceId },
3977
+ meta: {
3978
+ label,
3979
+ live: this.live,
3980
+ ceiling: this.limit
3981
+ }
3982
+ });
3983
+ return null;
3984
+ }
3985
+ this.live += 1;
3986
+ this.logger.debug("session-decode: live decode-worker slot taken", {
3987
+ tags: { deviceId },
3988
+ meta: {
3989
+ label,
3990
+ live: this.live,
3991
+ ceiling: this.limit
3992
+ }
3993
+ });
3994
+ let released = false;
3995
+ return { release: () => {
3996
+ if (released) return;
3997
+ released = true;
3998
+ this.live -= 1;
3999
+ } };
4000
+ }
4001
+ };
4002
+ //#endregion
3873
4003
  //#region src/pipeline-runner/stateless-step.ts
3874
4004
  function modelServability(step, modelId, format) {
3875
4005
  const entry = step.models.find((m) => m.id === modelId);
@@ -4008,7 +4138,8 @@ var DEFAULT_CONFIG = {
4008
4138
  maxConcurrentInferences: 16,
4009
4139
  targetLoadPercent: 80,
4010
4140
  minThrottledFps: 1,
4011
- maxConcurrentDecodeSpawns: 3
4141
+ maxConcurrentDecodeSpawns: 3,
4142
+ maxLiveDecodeWorkers: 8
4012
4143
  };
4013
4144
  /**
4014
4145
  * Camera attachment record kept by the runner addon. Holds the original
@@ -4311,6 +4442,14 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
4311
4442
  */
4312
4443
  spawnGate = null;
4313
4444
  /**
4445
+ * Per-node ceiling on decode workers ALIVE at once, sized to
4446
+ * `config.maxLiveDecodeWorkers`. The spawn gate above bounds how fast workers
4447
+ * START; this bounds how many EXIST — measured live at 1 -> 8 workers and a
4448
+ * ~4 GiB move in 8 seconds, with nothing stopping the 9th. `resize()`d live in
4449
+ * `onConfigChanged`. Null until `onInitialize`.
4450
+ */
4451
+ liveWorkers = null;
4452
+ /**
4314
4453
  * Node-level map from a retention-ring `FrameHandle` to the decode worker
4315
4454
  * still holding that frame's NATIVE surface — backs the `getNativeCrop` cap
4316
4455
  * so post-analysis can cut native-res crops (best-effort; a miss falls back
@@ -4534,6 +4673,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
4534
4673
  }
4535
4674
  });
4536
4675
  this.spawnGate = new Semaphore(Math.max(1, this.config.maxConcurrentDecodeSpawns));
4676
+ this.liveWorkers = new LiveDecodeWorkerAdmission({
4677
+ limit: this.config.maxLiveDecodeWorkers,
4678
+ logger: this.ctx.logger
4679
+ });
4537
4680
  this.runner.timingSampler.setLogger(this.ctx.logger.child("timing"));
4538
4681
  this.runner.onDetectionStreamChange((deviceId, action) => {
4539
4682
  this.handleDetectionStreamChange(deviceId, action);
@@ -4694,8 +4837,14 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
4694
4837
  children: s.children ?? []
4695
4838
  }));
4696
4839
  const enabledSteps = stepsToRun.filter((s) => s.enabled);
4697
- const useFastPath = enabledSteps.length === 1 && (!enabledSteps[0].children || enabledSteps[0].children.filter((c) => c.enabled).length === 0) && !input.simulatePipeline;
4840
+ const isSingleStep = enabledSteps.length === 1 && (!enabledSteps[0].children || enabledSteps[0].children.filter((c) => c.enabled).length === 0);
4841
+ const useFastPath = canUseCachedFastPath(isSingleStep, input);
4698
4842
  const rootStep = enabledSteps[0];
4843
+ const fastPathRefusal = isSingleStep ? fastPathRefusalReason(input) : null;
4844
+ if (fastPathRefusal) ctx.logger.info("synthetic bench: cached fast path declined", { meta: {
4845
+ reason: fastPathRefusal,
4846
+ node: benchNode
4847
+ } });
4699
4848
  const sharedFrame = {
4700
4849
  data: cached.data,
4701
4850
  format: cached.format,
@@ -4715,11 +4864,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
4715
4864
  height: cached.height,
4716
4865
  format: cached.format
4717
4866
  }, pin)).frameId;
4718
- await api.pipelineExecutor.runPipeline.mutate({
4867
+ await api.pipelineExecutor.runPipeline.mutate({ ...buildBenchRunPipelineInput({
4719
4868
  steps: stepsToRun,
4720
4869
  frame: sharedFrame,
4721
4870
  ...input.engine ? { engine: input.engine } : {}
4722
- }, pin);
4871
+ }) }, pin);
4723
4872
  const warmupCount = input.warmup ?? 1;
4724
4873
  for (let w = 0; w < warmupCount; w++) await api.pipelineExecutor.inferCached.mutate({
4725
4874
  stepId: rootStep.addonId,
@@ -4812,16 +4961,20 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
4812
4961
  }
4813
4962
  ctx.logger.info("synthetic bench: using full runPipeline path", { meta: {
4814
4963
  steps: enabledSteps.length,
4815
- simulatePipeline: !!input.simulatePipeline
4964
+ simulatePipeline: !!input.simulatePipeline,
4965
+ deviceKey: input.deviceKey ?? null,
4966
+ plane: input.plane ?? null
4816
4967
  } });
4817
4968
  let _callCount = 0;
4818
4969
  const callOnce = async () => {
4819
4970
  const t0 = performance.now();
4820
- const result = await api.pipelineExecutor.runPipeline.mutate({
4971
+ const result = await api.pipelineExecutor.runPipeline.mutate({ ...buildBenchRunPipelineInput({
4821
4972
  steps: stepsToRun,
4822
4973
  frame: sharedFrame,
4823
- ...input.engine ? { engine: input.engine } : {}
4824
- }, pin);
4974
+ ...input.engine ? { engine: input.engine } : {},
4975
+ ...input.deviceKey ? { deviceKey: input.deviceKey } : {},
4976
+ ...input.plane ? { plane: input.plane } : {}
4977
+ }) }, pin);
4825
4978
  const wallMs = performance.now() - t0;
4826
4979
  const n = ++_callCount;
4827
4980
  if (n <= 20) ctx.logger.info("bench call trace", { meta: {
@@ -6198,6 +6351,18 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
6198
6351
  * blocked by a missing gate. `label` distinguishes detect vs motion forks in
6199
6352
  * the logs; the device id is already tagged on `log`.
6200
6353
  */
6354
+ /**
6355
+ * The node's live-decode-worker admission, bound to one camera.
6356
+ *
6357
+ * `undefined` when the ceiling is not configured (pre-`onInitialize`), which
6358
+ * `startGuardedSessionDecode` reads as "no ceiling" — the historical
6359
+ * behaviour. Never a queue: see {@link LiveDecodeWorkerAdmission}.
6360
+ */
6361
+ buildLiveWorkerAdmit(deviceId, label) {
6362
+ const admission = this.liveWorkers;
6363
+ if (!admission) return void 0;
6364
+ return () => admission.admit(deviceId, label);
6365
+ }
6201
6366
  buildDecodeSpawnAcquire(log, label) {
6202
6367
  return async () => {
6203
6368
  const gate = this.spawnGate;
@@ -6225,6 +6390,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
6225
6390
  const requestedAt = Date.now();
6226
6391
  const teardown = startGuardedSessionDecode({
6227
6392
  logger: log,
6393
+ admitWorker: this.buildLiveWorkerAdmit(config.deviceId, "detect"),
6228
6394
  acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:detect:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config), true),
6229
6395
  startPump: (source) => {
6230
6396
  const dialMs = Date.now() - requestedAt;
@@ -6297,6 +6463,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
6297
6463
  if (!runner) return () => {};
6298
6464
  return startGuardedSessionDecode({
6299
6465
  logger: log,
6466
+ admitWorker: this.buildLiveWorkerAdmit(config.deviceId, "motion"),
6300
6467
  acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.motionStreamId, `session-decode:motion:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config)),
6301
6468
  startPump: (source) => {
6302
6469
  log.info("session-decode: motion routed through per-session decode worker (gray)", { meta: {
@@ -6853,6 +7020,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
6853
7020
  }, OCCUPANCY_BURST_SAFETY_TIMEOUT_MS);
6854
7021
  teardown = startGuardedSessionDecode({
6855
7022
  logger: log,
7023
+ admitWorker: this.buildLiveWorkerAdmit(deviceId, "occupancy"),
6856
7024
  acquire: () => this.acquireSessionDecodeRestream(api, config.deviceId, config.detectionStreamId, `session-decode:occupancy:${config.deviceId}`, resolveSessionDecodeHostname(config, process.env["CAMSTACK_HUB_URL"]), resolveSessionDecodeOwnerNodeId(config)),
6857
7025
  startPump: (source) => startSessionDecodePump({
6858
7026
  fork: this.forkDecodeWorkerWithLease,
@@ -7026,6 +7194,17 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7026
7194
  step: 1,
7027
7195
  default: DEFAULT_CONFIG.maxConcurrentDecodeSpawns,
7028
7196
  showValue: true
7197
+ },
7198
+ {
7199
+ type: "slider",
7200
+ key: "maxLiveDecodeWorkers",
7201
+ label: "Max live decode workers",
7202
+ description: "Hard ceiling on decode workers ALIVE at once on this node (~320 MB of RAM each). Unlike the spawn gate above, which only limits how fast workers START, this limits how many EXIST. A camera that does not fit is REFUSED and logged, then retried automatically as soon as a slot frees — it is never queued, because a queued live session would decode a moment that has already passed. Raise it if the node has RAM to spare; lower it if the host is memory-constrained.",
7203
+ min: 1,
7204
+ max: 32,
7205
+ step: 1,
7206
+ default: DEFAULT_CONFIG.maxLiveDecodeWorkers,
7207
+ showValue: true
7029
7208
  }
7030
7209
  ]
7031
7210
  }] });
@@ -7033,12 +7212,14 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7033
7212
  async onConfigChanged() {
7034
7213
  this.runner?.updateLimits(this.config);
7035
7214
  this.spawnGate?.resize(Math.max(1, this.config.maxConcurrentDecodeSpawns));
7215
+ this.liveWorkers?.resize(this.config.maxLiveDecodeWorkers);
7036
7216
  this.ctx.logger.info("pipeline-runner tuning updated", { meta: {
7037
7217
  maxQueueDepth: this.config.maxQueueDepth,
7038
7218
  maxConcurrentInferences: this.config.maxConcurrentInferences,
7039
7219
  targetLoadPercent: this.config.targetLoadPercent,
7040
7220
  minThrottledFps: this.config.minThrottledFps,
7041
- maxConcurrentDecodeSpawns: this.config.maxConcurrentDecodeSpawns
7221
+ maxConcurrentDecodeSpawns: this.config.maxConcurrentDecodeSpawns,
7222
+ maxLiveDecodeWorkers: this.config.maxLiveDecodeWorkers
7042
7223
  } });
7043
7224
  }
7044
7225
  };