@camstack/addon-pipeline 0.1.17 → 0.1.19
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.
- package/dist/audio-analyzer/index.js +8 -3
- package/dist/audio-analyzer/index.js.map +1 -1
- package/dist/audio-analyzer/index.mjs +8 -3
- package/dist/audio-analyzer/index.mjs.map +1 -1
- package/dist/audio-codec-nodeav/index.js +1 -1
- package/dist/audio-codec-nodeav/index.mjs +1 -1
- package/dist/decoder-nodeav/index.js +1 -1
- package/dist/decoder-nodeav/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +23 -20
- package/dist/detection-pipeline/index.js.map +1 -1
- package/dist/detection-pipeline/index.mjs +23 -20
- package/dist/detection-pipeline/index.mjs.map +1 -1
- package/dist/{index-p-6GfKOg.js → index-BbPPvoCx.js} +469 -57
- package/dist/index-BbPPvoCx.js.map +1 -0
- package/dist/{index-CVzLrojg.mjs → index-Bmlkm0Fd.mjs} +469 -57
- package/dist/index-Bmlkm0Fd.mjs.map +1 -0
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +132 -14
- package/dist/pipeline-runner/index.js.map +1 -1
- package/dist/pipeline-runner/index.mjs +133 -15
- package/dist/pipeline-runner/index.mjs.map +1 -1
- package/dist/stream-broker/@mf-types.zip +0 -0
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-NjF4kxzW.mjs +19 -0
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-BAv_5ISf.mjs +20 -0
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs-DAssX3h0.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs-BsB2G7oY.mjs} +2 -1
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-DFoJJhpt.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.mjs_commonjs-proxy-xrRiPUpA.mjs} +1 -1
- package/dist/stream-broker/{__mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-x7XMEeuJ.mjs → __mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-C0E2yCzO.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Sx8tgpFZ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CupRlwqG.mjs} +6 -6
- package/dist/stream-broker/{client-CZXrddDR.mjs → client-NPZqorv9.mjs} +2 -2
- package/dist/stream-broker/{hostInit-D0jPgChu.mjs → hostInit-Bh4w7o5_.mjs} +12 -12
- package/dist/stream-broker/{index-C0BzaWmB.mjs → index-2Qp8vT3w.mjs} +1 -1
- package/dist/stream-broker/{index-CZNxa0ad.mjs → index-BBcZvb5t.mjs} +1 -1
- package/dist/stream-broker/index-CIJue-4t.mjs +37880 -0
- package/dist/stream-broker/{index-BvV3RVTZ.mjs → index-Cc6QBqMk.mjs} +2 -2
- package/dist/stream-broker/{index-cYW01SNH.mjs → index-D_1p2K9B.mjs} +1 -1
- package/dist/stream-broker/{index-BCEx31Mh.mjs → index-Dy2V7VOm.mjs} +3808 -3277
- package/dist/stream-broker/{index-KtR7Pp0O.mjs → index-mX3Kgiv1.mjs} +1 -1
- package/dist/stream-broker/index.js +1565 -280
- package/dist/stream-broker/index.js.map +1 -1
- package/dist/stream-broker/index.mjs +1567 -281
- package/dist/stream-broker/index.mjs.map +1 -1
- package/dist/stream-broker/{jsx-runtime-B_evVsXl.mjs → jsx-runtime-lb0mH5st.mjs} +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/stream-broker/{schemas-ChN4Ih0h.mjs → schemas-ClCuS4qa.mjs} +151 -141
- package/package.json +1 -1
- package/dist/index-CVzLrojg.mjs.map +0 -1
- package/dist/index-p-6GfKOg.js.map +0 -1
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-d8PmLbO2.mjs +0 -19
- package/dist/stream-broker/__mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-B4l8Nb2y.mjs +0 -20
- package/dist/stream-broker/index-Kb4xa8FX.mjs +0 -36403
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { e as errMsg, o as object, s as string, _ as _enum, l as lazy, a as array, b as boolean, n as number, d as defineCustomActions, c as customAction, B as BaseAddon, E as EventCategory, p as pipelineRunnerCapability, f as createEvent } from "../index-
|
|
1
|
+
import { e as errMsg, o as object, s as string, _ as _enum, l as lazy, a as array, b as boolean, n as number, d as defineCustomActions, c as customAction, B as BaseAddon, E as EventCategory, p as pipelineRunnerCapability, f as createEvent } from "../index-Bmlkm0Fd.mjs";
|
|
2
2
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
3
3
|
class FrameQueue {
|
|
4
4
|
/**
|
|
@@ -113,9 +113,9 @@ class PipelineTimingSampler {
|
|
|
113
113
|
if (!this.detSamples.has(deviceId)) this.detSamples.set(deviceId, []);
|
|
114
114
|
this.detSamples.get(deviceId).push(s);
|
|
115
115
|
}
|
|
116
|
-
addMotionSample(deviceId, ms) {
|
|
116
|
+
addMotionSample(deviceId, ms, frameAge = -1) {
|
|
117
117
|
if (!this.motSamples.has(deviceId)) this.motSamples.set(deviceId, []);
|
|
118
|
-
this.motSamples.get(deviceId).push(ms);
|
|
118
|
+
this.motSamples.get(deviceId).push({ ms, frameAge });
|
|
119
119
|
}
|
|
120
120
|
addAudioSample(deviceId, s) {
|
|
121
121
|
if (!this.audioSamples.has(deviceId)) this.audioSamples.set(deviceId, []);
|
|
@@ -140,6 +140,7 @@ class PipelineTimingSampler {
|
|
|
140
140
|
if (det.length === 0) continue;
|
|
141
141
|
const e2e = det.map((s) => s.endToEnd);
|
|
142
142
|
const inf = det.map((s) => s.inference);
|
|
143
|
+
const frameAge = det.map((s) => s.frameAge).filter((v) => v >= 0);
|
|
143
144
|
const totalDet = det.reduce((s, d) => s + d.detections, 0);
|
|
144
145
|
this.log.info(
|
|
145
146
|
"pipeline stats",
|
|
@@ -148,7 +149,20 @@ class PipelineTimingSampler {
|
|
|
148
149
|
meta: {
|
|
149
150
|
frames: det.length,
|
|
150
151
|
intervalSec: REPORT_INTERVAL_MS / 1e3,
|
|
152
|
+
// enqueue → emit, in ms. Stage breakdown (avg) exposes WHERE the
|
|
153
|
+
// latency sits: queue backlog vs semaphore contention vs inference
|
|
154
|
+
// vs result-emit. inference is usually the floor (model chain).
|
|
151
155
|
e2e: { avg: avg(e2e), p95: p95(e2e), max: max(e2e) },
|
|
156
|
+
// Frame age (capture→inference-pick): if large, the analyzed frame is
|
|
157
|
+
// already stale (decoder/ring behind), which delays the overlay
|
|
158
|
+
// regardless of how fast the result is delivered.
|
|
159
|
+
frameAge: { avg: avg(frameAge), p95: p95(frameAge), max: max(frameAge) },
|
|
160
|
+
stagesMs: {
|
|
161
|
+
queueWait: avg(det.map((s) => s.queueWait)),
|
|
162
|
+
semaphoreWait: avg(det.map((s) => s.semaphoreWait)),
|
|
163
|
+
inference: avg(inf),
|
|
164
|
+
resultToEmit: avg(det.map((s) => s.resultToEmit))
|
|
165
|
+
},
|
|
152
166
|
inference: { avg: avg(inf), p95: p95(inf) },
|
|
153
167
|
detections: totalDet,
|
|
154
168
|
dropped,
|
|
@@ -161,6 +175,8 @@ class PipelineTimingSampler {
|
|
|
161
175
|
this.detSamples.clear();
|
|
162
176
|
for (const [deviceId, mot] of this.motSamples) {
|
|
163
177
|
if (mot.length === 0) continue;
|
|
178
|
+
const ms = mot.map((s) => s.ms);
|
|
179
|
+
const frameAge = mot.map((s) => s.frameAge).filter((v) => v >= 0);
|
|
164
180
|
this.log.info(
|
|
165
181
|
"motion stats",
|
|
166
182
|
{
|
|
@@ -168,10 +184,12 @@ class PipelineTimingSampler {
|
|
|
168
184
|
meta: {
|
|
169
185
|
frames: mot.length,
|
|
170
186
|
intervalSec: REPORT_INTERVAL_MS / 1e3,
|
|
171
|
-
avg: avg(
|
|
172
|
-
p95: p95(
|
|
173
|
-
max: max(
|
|
174
|
-
//
|
|
187
|
+
avg: avg(ms),
|
|
188
|
+
p95: p95(ms),
|
|
189
|
+
max: max(ms),
|
|
190
|
+
// Frame age at motion analysis (capture→analysis). Large = stale
|
|
191
|
+
// input frame (decoder/ring behind) → motion box lags real movement.
|
|
192
|
+
frameAge: { avg: avg(frameAge), p95: p95(frameAge), max: max(frameAge) }
|
|
175
193
|
}
|
|
176
194
|
}
|
|
177
195
|
);
|
|
@@ -517,8 +535,7 @@ class PipelineRunner {
|
|
|
517
535
|
async processWithSemaphore(deviceId, entry, frameInput, state, streamType) {
|
|
518
536
|
const pickedAt = Date.now();
|
|
519
537
|
const { frame, handle } = entry;
|
|
520
|
-
const
|
|
521
|
-
const enqueuedAt = frame._enqueuedAt ?? captureTs;
|
|
538
|
+
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
522
539
|
const release = await this.semaphore.acquire();
|
|
523
540
|
const semAcquiredAt = Date.now();
|
|
524
541
|
try {
|
|
@@ -533,13 +550,19 @@ class PipelineRunner {
|
|
|
533
550
|
if (result) {
|
|
534
551
|
await this.notifyCallbacks(deviceId, frame, result, streamType, handle);
|
|
535
552
|
const emittedAt = Date.now();
|
|
553
|
+
const capturedAt = frame.capturedAt;
|
|
536
554
|
this.timingSampler.addSample(deviceId, {
|
|
537
|
-
captureToEnqueue: enqueuedAt - captureTs,
|
|
538
555
|
queueWait: pickedAt - enqueuedAt,
|
|
539
556
|
semaphoreWait: semAcquiredAt - pickedAt,
|
|
540
557
|
inference: inferenceMs,
|
|
541
558
|
resultToEmit: emittedAt - inferDoneAt,
|
|
542
|
-
|
|
559
|
+
// capturedAt is the shm-ring commit wall-clock; pickedAt − capturedAt
|
|
560
|
+
// is how stale the frame was when inference started. <0/absent → unknown.
|
|
561
|
+
frameAge: typeof capturedAt === "number" && capturedAt > 0 ? pickedAt - capturedAt : -1,
|
|
562
|
+
// Wall-clock pipeline latency: enqueue → result emitted. (Capture →
|
|
563
|
+
// enqueue, i.e. RTSP decode + shm-ring write, is not measured here —
|
|
564
|
+
// the shm frame carries only a PTS, no wall-clock capture stamp.)
|
|
565
|
+
endToEnd: emittedAt - enqueuedAt,
|
|
543
566
|
detections: result.detections?.length ?? 0
|
|
544
567
|
});
|
|
545
568
|
}
|
|
@@ -730,6 +753,11 @@ const DEFAULT_CONFIG = {
|
|
|
730
753
|
targetLoadPercent: 80,
|
|
731
754
|
minThrottledFps: 1
|
|
732
755
|
};
|
|
756
|
+
function shouldStartOnboardAnalyzer(config) {
|
|
757
|
+
if (!config.onboardMotionDrivesAnalyzer) return false;
|
|
758
|
+
if (config.motionSources.includes("analyzer")) return false;
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
733
761
|
function toFrameInput(frame) {
|
|
734
762
|
return {
|
|
735
763
|
data: frame.data,
|
|
@@ -760,6 +788,20 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
760
788
|
* detach.
|
|
761
789
|
*/
|
|
762
790
|
lastMotionAt = /* @__PURE__ */ new Map();
|
|
791
|
+
/**
|
|
792
|
+
* Dynamic analyzer subscriptions opened on `MotionOnMotionChanged
|
|
793
|
+
* source:'onboard'` when `onboardMotionDrivesAnalyzer === true`. Each
|
|
794
|
+
* entry is the unsubscribe handle returned by `subscribeMotionFrames`.
|
|
795
|
+
* Cleared on teardown timer fire, detach, and shutdown.
|
|
796
|
+
*/
|
|
797
|
+
onboardAnalyzerSubs = /* @__PURE__ */ new Map();
|
|
798
|
+
/**
|
|
799
|
+
* Teardown timers that close the dynamic analyzer subscription after
|
|
800
|
+
* `motionCooldownMs` without a new motion event. Re-armed on every
|
|
801
|
+
* `MotionOnMotionChanged source:'onboard'` call so the sub stays open
|
|
802
|
+
* while motion persists.
|
|
803
|
+
*/
|
|
804
|
+
onboardAnalyzerTeardownTimers = /* @__PURE__ */ new Map();
|
|
763
805
|
/**
|
|
764
806
|
* Snapshot-equality cache for metrics-snapshot defer. The runner
|
|
765
807
|
* fires per-camera metrics every `METRICS_SNAPSHOT_INTERVAL_MS`;
|
|
@@ -824,6 +866,9 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
824
866
|
const attachment = this.attached.get(deviceId);
|
|
825
867
|
if (!attachment) return;
|
|
826
868
|
const source = data.source;
|
|
869
|
+
if (source === "onboard") {
|
|
870
|
+
void this.handleOnboardMotionAnalyzer(deviceId, data.detected);
|
|
871
|
+
}
|
|
827
872
|
if (!attachment.config.motionSources.includes(source)) return;
|
|
828
873
|
this.runner?.reportMotion(
|
|
829
874
|
deviceId,
|
|
@@ -868,6 +913,9 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
868
913
|
this.unsubMotionEvents = null;
|
|
869
914
|
}
|
|
870
915
|
this.lastAnalyzerDetected.clear();
|
|
916
|
+
for (const deviceId of [...this.onboardAnalyzerTeardownTimers.keys(), ...this.onboardAnalyzerSubs.keys()]) {
|
|
917
|
+
this.clearOnboardAnalyzer(deviceId);
|
|
918
|
+
}
|
|
871
919
|
if (this.runner) {
|
|
872
920
|
this.runner.stop();
|
|
873
921
|
this.runner = null;
|
|
@@ -1302,6 +1350,7 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
1302
1350
|
detachInternal(deviceId) {
|
|
1303
1351
|
const attachment = this.attached.get(deviceId);
|
|
1304
1352
|
if (!attachment) return;
|
|
1353
|
+
this.clearOnboardAnalyzer(deviceId);
|
|
1305
1354
|
attachment.motionUnsubscribe?.();
|
|
1306
1355
|
attachment.detectionUnsubscribe?.();
|
|
1307
1356
|
this.attached.delete(deviceId);
|
|
@@ -1310,6 +1359,70 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
1310
1359
|
this.runner?.unregisterCamera(deviceId);
|
|
1311
1360
|
this.ctx?.logger.info("detachCamera", { tags: { deviceId } });
|
|
1312
1361
|
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Synchronously cancel the teardown timer and call the unsubscribe
|
|
1364
|
+
* handle for the dynamic onboard analyzer, if one is open. Safe to
|
|
1365
|
+
* call when no subscription exists.
|
|
1366
|
+
*/
|
|
1367
|
+
clearOnboardAnalyzer(deviceId) {
|
|
1368
|
+
const timer = this.onboardAnalyzerTeardownTimers.get(deviceId);
|
|
1369
|
+
if (timer !== void 0) {
|
|
1370
|
+
clearTimeout(timer);
|
|
1371
|
+
this.onboardAnalyzerTeardownTimers.delete(deviceId);
|
|
1372
|
+
}
|
|
1373
|
+
const unsub = this.onboardAnalyzerSubs.get(deviceId);
|
|
1374
|
+
if (unsub !== void 0) {
|
|
1375
|
+
try {
|
|
1376
|
+
unsub();
|
|
1377
|
+
} catch {
|
|
1378
|
+
}
|
|
1379
|
+
this.onboardAnalyzerSubs.delete(deviceId);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Dynamic analyzer gate for onboard-motion cameras.
|
|
1384
|
+
*
|
|
1385
|
+
* Called from the `MotionOnMotionChanged` subscriber whenever
|
|
1386
|
+
* `source === 'onboard'`. Opens a `subscribeMotionFrames` subscription
|
|
1387
|
+
* the first time motion is detected (idempotent — a second `detected:true`
|
|
1388
|
+
* while the sub is already open is a no-op). Always re-arms the teardown
|
|
1389
|
+
* timer so the subscription stays open as long as motion events keep
|
|
1390
|
+
* arriving and tears down `motionCooldownMs` after the last event.
|
|
1391
|
+
*
|
|
1392
|
+
* No-op when:
|
|
1393
|
+
* - The camera is not currently attached.
|
|
1394
|
+
* - `shouldStartOnboardAnalyzer(config)` returns false (flag off or
|
|
1395
|
+
* `motionSources` already includes `'analyzer'`).
|
|
1396
|
+
*/
|
|
1397
|
+
async handleOnboardMotionAnalyzer(deviceId, detected) {
|
|
1398
|
+
const attachment = this.attached.get(deviceId);
|
|
1399
|
+
if (!attachment) return;
|
|
1400
|
+
const config = attachment.config;
|
|
1401
|
+
if (!shouldStartOnboardAnalyzer(config)) return;
|
|
1402
|
+
const log = this.ctx.logger.withTags({ deviceId });
|
|
1403
|
+
if (detected && !this.onboardAnalyzerSubs.has(deviceId)) {
|
|
1404
|
+
const unsub = await this.subscribeMotionFrames(config);
|
|
1405
|
+
if (unsub) {
|
|
1406
|
+
this.onboardAnalyzerSubs.set(deviceId, unsub);
|
|
1407
|
+
log.debug("onboard-analyzer: opened motion-frame subscription");
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
const existing = this.onboardAnalyzerTeardownTimers.get(deviceId);
|
|
1411
|
+
if (existing !== void 0) clearTimeout(existing);
|
|
1412
|
+
const cooldownMs = config.motionCooldownMs;
|
|
1413
|
+
const timer = setTimeout(() => {
|
|
1414
|
+
this.onboardAnalyzerTeardownTimers.delete(deviceId);
|
|
1415
|
+
const unsub = this.onboardAnalyzerSubs.get(deviceId);
|
|
1416
|
+
if (!unsub) return;
|
|
1417
|
+
try {
|
|
1418
|
+
unsub();
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1421
|
+
this.onboardAnalyzerSubs.delete(deviceId);
|
|
1422
|
+
log.debug("onboard-analyzer: closed after motion cooldown", { meta: { cooldownMs } });
|
|
1423
|
+
}, cooldownMs);
|
|
1424
|
+
this.onboardAnalyzerTeardownTimers.set(deviceId, timer);
|
|
1425
|
+
}
|
|
1313
1426
|
async getLocalLoad() {
|
|
1314
1427
|
const metrics = this.runner?.getMetrics() ?? { avgInferenceTimeMs: 0, queueDepth: 0 };
|
|
1315
1428
|
const allCameraMetrics = this.runner?.getAllCameraMetrics() ?? [];
|
|
@@ -1566,20 +1679,24 @@ class PipelineRunnerAddon extends BaseAddon {
|
|
|
1566
1679
|
zonesPayload
|
|
1567
1680
|
));
|
|
1568
1681
|
}
|
|
1569
|
-
|
|
1682
|
+
const capturedAt = frame.capturedAt;
|
|
1683
|
+
const motionFrameAge = typeof capturedAt === "number" && capturedAt > 0 ? motionStart - capturedAt : -1;
|
|
1684
|
+
runner.timingSampler.addMotionSample(deviceId, Date.now() - motionStart, motionFrameAge);
|
|
1570
1685
|
} catch (error) {
|
|
1571
1686
|
const msg = errMsg(error);
|
|
1572
1687
|
log.error("runMotionAnalysis failed", { meta: { error: msg } });
|
|
1573
1688
|
}
|
|
1574
1689
|
}
|
|
1575
|
-
emitInferenceResult(deviceId,
|
|
1690
|
+
emitInferenceResult(deviceId, frame, result, handle) {
|
|
1576
1691
|
const ctx = this.ctx;
|
|
1577
1692
|
if (!ctx?.eventBus) return;
|
|
1693
|
+
const capturedAt = frame.capturedAt;
|
|
1578
1694
|
const payload = {
|
|
1579
1695
|
deviceId,
|
|
1580
1696
|
frame: result,
|
|
1581
1697
|
nodeId: this.nodeId,
|
|
1582
|
-
frameHandle: handle
|
|
1698
|
+
frameHandle: handle,
|
|
1699
|
+
...typeof capturedAt === "number" && capturedAt > 0 ? { capturedAt } : {}
|
|
1583
1700
|
};
|
|
1584
1701
|
this.ctx.eventBus.emit(createEvent(
|
|
1585
1702
|
EventCategory.PipelineInferenceResult,
|
|
@@ -1720,6 +1837,7 @@ export {
|
|
|
1720
1837
|
PipelineTimingSampler,
|
|
1721
1838
|
Semaphore,
|
|
1722
1839
|
pipelineRunnerBenchActions as customActions,
|
|
1723
|
-
PipelineRunnerAddon as default
|
|
1840
|
+
PipelineRunnerAddon as default,
|
|
1841
|
+
shouldStartOnboardAnalyzer
|
|
1724
1842
|
};
|
|
1725
1843
|
//# sourceMappingURL=index.mjs.map
|