@camstack/addon-pipeline 1.2.188 → 1.2.189
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.
|
@@ -301,6 +301,16 @@ var DEFAULT_NODE_DECODE_BUDGET_MB = 8192;
|
|
|
301
301
|
* `native-lease-config.ts` on why "unset" includes a knob sitting at its
|
|
302
302
|
* default); anything absent falls back to what the worker ships with.
|
|
303
303
|
*/
|
|
304
|
+
/**
|
|
305
|
+
* How many frames of one camera the runner may have in inference at once:
|
|
306
|
+
* the worker's hold minus one, never below one. The hold evicts oldest-first
|
|
307
|
+
* when a newer marked frame lands, so the (hold)th outstanding ref is the one
|
|
308
|
+
* that makes the first unreadable. See `runner.ts` `maxInflightPerCamera`.
|
|
309
|
+
*/
|
|
310
|
+
function maxInflightPerCamera(override) {
|
|
311
|
+
const holdFrames = override.holdFrames ?? require_dist.DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
|
|
312
|
+
return Math.max(1, holdFrames - 1);
|
|
313
|
+
}
|
|
304
314
|
function decodeWorkerFootprintMb(override) {
|
|
305
315
|
const budgetMb = override.budgetMb ?? require_dist.DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
306
316
|
const holdFrames = override.holdFrames ?? require_dist.DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
|
|
@@ -4520,6 +4530,7 @@ var PipelineRunner = class {
|
|
|
4520
4530
|
detectionQueue,
|
|
4521
4531
|
inferenceTimes: [],
|
|
4522
4532
|
shedFrames: 0,
|
|
4533
|
+
inflight: 0,
|
|
4523
4534
|
processedCount: 0,
|
|
4524
4535
|
startTime: Date.now(),
|
|
4525
4536
|
phase: initialPhase,
|
|
@@ -4626,8 +4637,23 @@ var PipelineRunner = class {
|
|
|
4626
4637
|
* time so the pump can copy the pixels while the frame is still reserved.
|
|
4627
4638
|
*/
|
|
4628
4639
|
detectionBackpressure(deviceId) {
|
|
4629
|
-
|
|
4630
|
-
|
|
4640
|
+
const state = this.cameras.get(deviceId);
|
|
4641
|
+
if (!state) return true;
|
|
4642
|
+
return this.semaphore.available <= 0 || state.inflight >= this.maxInflightPerCamera();
|
|
4643
|
+
}
|
|
4644
|
+
/** The per-camera in-flight cap in force (config, else the shipped hold minus one). */
|
|
4645
|
+
maxInflightPerCamera() {
|
|
4646
|
+
return Math.max(1, this.config.maxInflightPerCamera ?? require_dist.DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames - 1);
|
|
4647
|
+
}
|
|
4648
|
+
/**
|
|
4649
|
+
* Follow a resized native-frame hold. Running inferences are untouched; the
|
|
4650
|
+
* next pick is measured against the new cap.
|
|
4651
|
+
*/
|
|
4652
|
+
setMaxInflightPerCamera(cap) {
|
|
4653
|
+
this.config = {
|
|
4654
|
+
...this.config,
|
|
4655
|
+
maxInflightPerCamera: cap
|
|
4656
|
+
};
|
|
4631
4657
|
}
|
|
4632
4658
|
enqueueReservedDetectionFrame(deviceId, frame, handle) {
|
|
4633
4659
|
const state = this.cameras.get(deviceId);
|
|
@@ -4912,6 +4938,7 @@ var PipelineRunner = class {
|
|
|
4912
4938
|
async processWithSemaphore(deviceId, entry, frameInput, state, streamType) {
|
|
4913
4939
|
const pickedAt = Date.now();
|
|
4914
4940
|
const { frame, handle } = entry;
|
|
4941
|
+
state.inflight += 1;
|
|
4915
4942
|
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
4916
4943
|
const rawRelease = await this.semaphore.acquire();
|
|
4917
4944
|
let released = false;
|
|
@@ -4953,6 +4980,7 @@ var PipelineRunner = class {
|
|
|
4953
4980
|
} catch {
|
|
4954
4981
|
frameReleaseReason = "error";
|
|
4955
4982
|
} finally {
|
|
4983
|
+
state.inflight -= 1;
|
|
4956
4984
|
release();
|
|
4957
4985
|
try {
|
|
4958
4986
|
if (frameReleaseReason === "success" && handle && isContinuousRetainEnabled()) await this.config.hydrateRetainedFrame?.(handle);
|
|
@@ -5052,7 +5080,7 @@ var PipelineRunner = class {
|
|
|
5052
5080
|
} catch {}
|
|
5053
5081
|
}
|
|
5054
5082
|
pickNextDetectionFrame() {
|
|
5055
|
-
for (const [deviceId, state] of this.cameras) if (state.registration.detectionMode === "always-on" && state.detectionQueue.size > 0) return {
|
|
5083
|
+
for (const [deviceId, state] of this.cameras) if (state.registration.detectionMode === "always-on" && state.detectionQueue.size > 0 && state.inflight < this.maxInflightPerCamera()) return {
|
|
5056
5084
|
deviceId,
|
|
5057
5085
|
entry: state.detectionQueue.dequeue(),
|
|
5058
5086
|
state
|
|
@@ -5065,7 +5093,7 @@ var PipelineRunner = class {
|
|
|
5065
5093
|
if (!deviceId) continue;
|
|
5066
5094
|
const state = this.cameras.get(deviceId);
|
|
5067
5095
|
if (!state) continue;
|
|
5068
|
-
if (state.phase === "active" && state.detectionQueue.size > 0) {
|
|
5096
|
+
if (state.phase === "active" && state.detectionQueue.size > 0 && state.inflight < this.maxInflightPerCamera()) {
|
|
5069
5097
|
this.defaultRoundRobinIndex = (idx + 1) % this.defaultRoundRobinKeys.length;
|
|
5070
5098
|
const entry = state.detectionQueue.dequeue();
|
|
5071
5099
|
if (!entry) continue;
|
|
@@ -5795,6 +5823,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
5795
5823
|
this.runner = new PipelineRunner({
|
|
5796
5824
|
maxQueueDepth: this.config.maxQueueDepth,
|
|
5797
5825
|
maxConcurrentInferences: this.config.maxConcurrentInferences,
|
|
5826
|
+
maxInflightPerCamera: maxInflightPerCamera(this.nativeLease.current()),
|
|
5798
5827
|
targetLoadPercent: this.config.targetLoadPercent,
|
|
5799
5828
|
minThrottledFps: this.config.minThrottledFps,
|
|
5800
5829
|
processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
|
|
@@ -7045,6 +7074,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7045
7074
|
async refreshNativeLeaseSettings() {
|
|
7046
7075
|
await this.nativeLease.refresh(this.ctxIfReady?.api);
|
|
7047
7076
|
this.liveWorkers?.resizeFootprint(decodeWorkerFootprintMb(this.nativeLease.current()));
|
|
7077
|
+
this.runner?.setMaxInflightPerCamera(maxInflightPerCamera(this.nativeLease.current()));
|
|
7048
7078
|
await this.nodeLimits?.refresh(this.ctxIfReady?.api);
|
|
7049
7079
|
this.applyNodeDecodeLimits("lease-refresh");
|
|
7050
7080
|
}
|
|
@@ -295,6 +295,16 @@ var DEFAULT_NODE_DECODE_BUDGET_MB = 8192;
|
|
|
295
295
|
* `native-lease-config.ts` on why "unset" includes a knob sitting at its
|
|
296
296
|
* default); anything absent falls back to what the worker ships with.
|
|
297
297
|
*/
|
|
298
|
+
/**
|
|
299
|
+
* How many frames of one camera the runner may have in inference at once:
|
|
300
|
+
* the worker's hold minus one, never below one. The hold evicts oldest-first
|
|
301
|
+
* when a newer marked frame lands, so the (hold)th outstanding ref is the one
|
|
302
|
+
* that makes the first unreadable. See `runner.ts` `maxInflightPerCamera`.
|
|
303
|
+
*/
|
|
304
|
+
function maxInflightPerCamera(override) {
|
|
305
|
+
const holdFrames = override.holdFrames ?? DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
|
|
306
|
+
return Math.max(1, holdFrames - 1);
|
|
307
|
+
}
|
|
298
308
|
function decodeWorkerFootprintMb(override) {
|
|
299
309
|
const budgetMb = override.budgetMb ?? DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
300
310
|
const holdFrames = override.holdFrames ?? DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
|
|
@@ -4513,6 +4523,7 @@ var PipelineRunner = class {
|
|
|
4513
4523
|
detectionQueue,
|
|
4514
4524
|
inferenceTimes: [],
|
|
4515
4525
|
shedFrames: 0,
|
|
4526
|
+
inflight: 0,
|
|
4516
4527
|
processedCount: 0,
|
|
4517
4528
|
startTime: Date.now(),
|
|
4518
4529
|
phase: initialPhase,
|
|
@@ -4619,8 +4630,23 @@ var PipelineRunner = class {
|
|
|
4619
4630
|
* time so the pump can copy the pixels while the frame is still reserved.
|
|
4620
4631
|
*/
|
|
4621
4632
|
detectionBackpressure(deviceId) {
|
|
4622
|
-
|
|
4623
|
-
|
|
4633
|
+
const state = this.cameras.get(deviceId);
|
|
4634
|
+
if (!state) return true;
|
|
4635
|
+
return this.semaphore.available <= 0 || state.inflight >= this.maxInflightPerCamera();
|
|
4636
|
+
}
|
|
4637
|
+
/** The per-camera in-flight cap in force (config, else the shipped hold minus one). */
|
|
4638
|
+
maxInflightPerCamera() {
|
|
4639
|
+
return Math.max(1, this.config.maxInflightPerCamera ?? DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames - 1);
|
|
4640
|
+
}
|
|
4641
|
+
/**
|
|
4642
|
+
* Follow a resized native-frame hold. Running inferences are untouched; the
|
|
4643
|
+
* next pick is measured against the new cap.
|
|
4644
|
+
*/
|
|
4645
|
+
setMaxInflightPerCamera(cap) {
|
|
4646
|
+
this.config = {
|
|
4647
|
+
...this.config,
|
|
4648
|
+
maxInflightPerCamera: cap
|
|
4649
|
+
};
|
|
4624
4650
|
}
|
|
4625
4651
|
enqueueReservedDetectionFrame(deviceId, frame, handle) {
|
|
4626
4652
|
const state = this.cameras.get(deviceId);
|
|
@@ -4905,6 +4931,7 @@ var PipelineRunner = class {
|
|
|
4905
4931
|
async processWithSemaphore(deviceId, entry, frameInput, state, streamType) {
|
|
4906
4932
|
const pickedAt = Date.now();
|
|
4907
4933
|
const { frame, handle } = entry;
|
|
4934
|
+
state.inflight += 1;
|
|
4908
4935
|
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
4909
4936
|
const rawRelease = await this.semaphore.acquire();
|
|
4910
4937
|
let released = false;
|
|
@@ -4946,6 +4973,7 @@ var PipelineRunner = class {
|
|
|
4946
4973
|
} catch {
|
|
4947
4974
|
frameReleaseReason = "error";
|
|
4948
4975
|
} finally {
|
|
4976
|
+
state.inflight -= 1;
|
|
4949
4977
|
release();
|
|
4950
4978
|
try {
|
|
4951
4979
|
if (frameReleaseReason === "success" && handle && isContinuousRetainEnabled()) await this.config.hydrateRetainedFrame?.(handle);
|
|
@@ -5045,7 +5073,7 @@ var PipelineRunner = class {
|
|
|
5045
5073
|
} catch {}
|
|
5046
5074
|
}
|
|
5047
5075
|
pickNextDetectionFrame() {
|
|
5048
|
-
for (const [deviceId, state] of this.cameras) if (state.registration.detectionMode === "always-on" && state.detectionQueue.size > 0) return {
|
|
5076
|
+
for (const [deviceId, state] of this.cameras) if (state.registration.detectionMode === "always-on" && state.detectionQueue.size > 0 && state.inflight < this.maxInflightPerCamera()) return {
|
|
5049
5077
|
deviceId,
|
|
5050
5078
|
entry: state.detectionQueue.dequeue(),
|
|
5051
5079
|
state
|
|
@@ -5058,7 +5086,7 @@ var PipelineRunner = class {
|
|
|
5058
5086
|
if (!deviceId) continue;
|
|
5059
5087
|
const state = this.cameras.get(deviceId);
|
|
5060
5088
|
if (!state) continue;
|
|
5061
|
-
if (state.phase === "active" && state.detectionQueue.size > 0) {
|
|
5089
|
+
if (state.phase === "active" && state.detectionQueue.size > 0 && state.inflight < this.maxInflightPerCamera()) {
|
|
5062
5090
|
this.defaultRoundRobinIndex = (idx + 1) % this.defaultRoundRobinKeys.length;
|
|
5063
5091
|
const entry = state.detectionQueue.dequeue();
|
|
5064
5092
|
if (!entry) continue;
|
|
@@ -5788,6 +5816,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
5788
5816
|
this.runner = new PipelineRunner({
|
|
5789
5817
|
maxQueueDepth: this.config.maxQueueDepth,
|
|
5790
5818
|
maxConcurrentInferences: this.config.maxConcurrentInferences,
|
|
5819
|
+
maxInflightPerCamera: maxInflightPerCamera(this.nativeLease.current()),
|
|
5791
5820
|
targetLoadPercent: this.config.targetLoadPercent,
|
|
5792
5821
|
minThrottledFps: this.config.minThrottledFps,
|
|
5793
5822
|
processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
|
|
@@ -7038,6 +7067,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
7038
7067
|
async refreshNativeLeaseSettings() {
|
|
7039
7068
|
await this.nativeLease.refresh(this.ctxIfReady?.api);
|
|
7040
7069
|
this.liveWorkers?.resizeFootprint(decodeWorkerFootprintMb(this.nativeLease.current()));
|
|
7070
|
+
this.runner?.setMaxInflightPerCamera(maxInflightPerCamera(this.nativeLease.current()));
|
|
7041
7071
|
await this.nodeLimits?.refresh(this.ctxIfReady?.api);
|
|
7042
7072
|
this.applyNodeDecodeLimits("lease-refresh");
|
|
7043
7073
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.189",
|
|
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",
|