@camstack/addon-pipeline 1.2.214 → 1.2.217
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 +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-C2vYSX3O.mjs → default-detection-model-BwNEV8Wl.mjs} +1 -1
- package/dist/{default-detection-model-f0PImZnh.js → default-detection-model-DzzuINe-.js} +1 -1
- package/dist/detection-pipeline/index.js +6 -7
- package/dist/detection-pipeline/index.mjs +5 -6
- package/dist/{dist-DoHcimdu.js → dist-D7hxiBnC.js} +237 -16
- package/dist/{dist-DttqKvsQ.mjs → dist-DgeXZfwn.mjs} +237 -16
- package/dist/{lazy-sharp-BeeQGCUu.js → lazy-sharp-BSkbiuz5.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-NtM7__8q.mjs → node-C9GcBrwk.mjs} +1 -1
- package/dist/{node-6uF72wri.js → node-DDhmPcOx.js} +1 -1
- package/dist/pipeline-runner/index.js +541 -26
- package/dist/pipeline-runner/index.mjs +539 -24
- package/dist/{process-memory-CddpCDWL.mjs → process-memory-Dw3CGS3D.mjs} +1 -1
- package/dist/{process-memory-BRdyl5fE.js → process-memory-ENqmiiis.js} +1 -1
- package/dist/recorder/index.js +158 -47
- package/dist/recorder/index.mjs +158 -47
- package/dist/{segment-demux-js-CsFtPVzH.mjs → segment-demux-js-BGbKlotF.mjs} +1 -1
- package/dist/{segment-demux-js-CkJPdyDH.js → segment-demux-js-sLs8mbZQ.js} +1 -1
- package/dist/session-decode/decode-worker-child.js +61 -180
- package/dist/session-decode/decode-worker-child.mjs +60 -179
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbBa4iV-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BULotDCb.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-yJ2CyixO.mjs +26 -0
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Uxkwkgy5.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DICVQkNi.mjs} +1 -1
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-CagSUJH7.mjs → hostInit-_RQxF72h.mjs} +3 -3
- package/dist/stream-broker/index.js +20 -21
- package/dist/stream-broker/index.mjs +20 -21
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BWO2WNjw.js → worker-protocol-DGql9R4g.js} +1 -1
- package/dist/{worker-protocol-BKwkLZWT.mjs → worker-protocol-SzmaLNnP.mjs} +1 -1
- package/package.json +1 -1
- package/dist/event-loop-stall-monitor-DXLNBMkY.mjs +0 -138
- package/dist/event-loop-stall-monitor-DaUBcb13.js +0 -149
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-0MF-KL1v.mjs +0 -26
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
3
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
2
|
+
const require_worker_protocol = require("../worker-protocol-DGql9R4g.js");
|
|
3
|
+
const require_lazy_sharp = require("../lazy-sharp-BSkbiuz5.js");
|
|
4
4
|
//#region src/session-decode/color-conversion.ts
|
|
5
5
|
/**
|
|
6
6
|
* Explicit YUV→RGB colorspace/range resolution for the session-decode worker.
|
|
@@ -323,153 +323,6 @@ var FrameSlot = class {
|
|
|
323
323
|
}
|
|
324
324
|
};
|
|
325
325
|
//#endregion
|
|
326
|
-
//#region src/session-decode/media-clock.ts
|
|
327
|
-
/**
|
|
328
|
-
* Convert a container pts to milliseconds.
|
|
329
|
-
*
|
|
330
|
-
* Returns `null` — never a fabricated `0` — for a time base that cannot
|
|
331
|
-
* describe a duration. A zero here would place every frame of the session at
|
|
332
|
-
* the anchor, i.e. a whole burst of detections sharing one instant, which is
|
|
333
|
-
* exactly the kind of plausible-looking wrong timestamp this module exists to
|
|
334
|
-
* refuse. The caller degrades to the wall clock instead.
|
|
335
|
-
*/
|
|
336
|
-
function ptsToMs(pts, timeBase) {
|
|
337
|
-
if (!Number.isFinite(timeBase.num) || !Number.isFinite(timeBase.den)) return null;
|
|
338
|
-
if (timeBase.num <= 0 || timeBase.den <= 0) return null;
|
|
339
|
-
return Number(pts) * timeBase.num * 1e3 / timeBase.den;
|
|
340
|
-
}
|
|
341
|
-
/** Anchor the session's media clock on its first decoded frame. */
|
|
342
|
-
function anchorMediaClock(input) {
|
|
343
|
-
const preRoll = Number.isFinite(input.preRollMs) && input.preRollMs > 0 ? input.preRollMs : 0;
|
|
344
|
-
return {
|
|
345
|
-
firstPtsMs: input.firstPtsMs,
|
|
346
|
-
wallAtFirstPts: input.nowWall - preRoll
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* The wall clock at which the frame with `ptsMs` was shot.
|
|
351
|
-
*
|
|
352
|
-
* Clamped to `nowWall`: a container whose pts runs past the anchor (a clock
|
|
353
|
-
* jump, a source whose pts is not wall-clock-shaped, a re-dial that reset it)
|
|
354
|
-
* must never produce a track that starts in the FUTURE. A future `firstSeen`
|
|
355
|
-
* sorts a track ahead of everything, and no later pass can detect that it is
|
|
356
|
-
* wrong. Clamping loses precision on a pathological source and keeps the
|
|
357
|
-
* ordering property every consumer relies on.
|
|
358
|
-
*/
|
|
359
|
-
function mediaWallClockOf(anchor, ptsMs, nowWall) {
|
|
360
|
-
const shotAt = anchor.wallAtFirstPts + (ptsMs - anchor.firstPtsMs);
|
|
361
|
-
return shotAt > nowWall ? nowWall : shotAt;
|
|
362
|
-
}
|
|
363
|
-
//#endregion
|
|
364
|
-
//#region src/session-decode/pre-roll-burst.ts
|
|
365
|
-
var ADMITTED = { admitted: true };
|
|
366
|
-
/**
|
|
367
|
-
* Choose the media stride for one burst.
|
|
368
|
-
*
|
|
369
|
-
* `plannedFrames` is what the stride actually yields, so a caller can log the
|
|
370
|
-
* intent next to the outcome — a budget that is never reached and a budget that
|
|
371
|
-
* is always hit are different problems and used to read the same.
|
|
372
|
-
*/
|
|
373
|
-
function planPreRollBurst(input) {
|
|
374
|
-
const span = Number.isFinite(input.spanMs) && input.spanMs > 0 ? input.spanMs : 0;
|
|
375
|
-
const fps = Number.isFinite(input.detectionFps) && input.detectionFps > 0 ? input.detectionFps : 1;
|
|
376
|
-
const maxFrames = Number.isFinite(input.maxFrames) && input.maxFrames > 0 ? input.maxFrames : 0;
|
|
377
|
-
if (span === 0 || maxFrames === 0) return {
|
|
378
|
-
strideMs: Number.POSITIVE_INFINITY,
|
|
379
|
-
plannedFrames: 0,
|
|
380
|
-
subsampled: false
|
|
381
|
-
};
|
|
382
|
-
const cadenceMs = 1e3 / fps;
|
|
383
|
-
const budgetStrideMs = span / maxFrames;
|
|
384
|
-
const subsampled = budgetStrideMs > cadenceMs;
|
|
385
|
-
const strideMs = subsampled ? budgetStrideMs : cadenceMs;
|
|
386
|
-
return {
|
|
387
|
-
strideMs,
|
|
388
|
-
plannedFrames: Math.min(maxFrames, Math.max(1, Math.floor(span / strideMs))),
|
|
389
|
-
subsampled
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* The live, per-session budget. Stateful by necessity — a stride is a decision
|
|
394
|
-
* about the PREVIOUS admission — and deliberately free of any clock of its own:
|
|
395
|
-
* both times are passed in, so a test can drive a catch-up burst without
|
|
396
|
-
* pretending 4 s of media takes 4 s to decode.
|
|
397
|
-
*/
|
|
398
|
-
var PreRollBurst = class {
|
|
399
|
-
budget;
|
|
400
|
-
plan;
|
|
401
|
-
startedAtWall;
|
|
402
|
-
liveEdgeWall;
|
|
403
|
-
lastAdmittedMediaWall = null;
|
|
404
|
-
offered = 0;
|
|
405
|
-
inferred = 0;
|
|
406
|
-
done = false;
|
|
407
|
-
drops = {
|
|
408
|
-
stride: 0,
|
|
409
|
-
"budget-frames": 0,
|
|
410
|
-
"budget-wall-ms": 0,
|
|
411
|
-
"live-edge": 0
|
|
412
|
-
};
|
|
413
|
-
constructor(input) {
|
|
414
|
-
this.budget = input.budget;
|
|
415
|
-
this.plan = input.plan;
|
|
416
|
-
this.startedAtWall = input.startedAtWall;
|
|
417
|
-
this.liveEdgeWall = input.liveEdgeWall;
|
|
418
|
-
}
|
|
419
|
-
/** True once the burst has ended — by budget, or by reaching the live edge. */
|
|
420
|
-
get finished() {
|
|
421
|
-
return this.done;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Offer one decoded history frame.
|
|
425
|
-
*
|
|
426
|
-
* `mediaWall` is the frame's OWN moment (see `media-clock.ts`); `nowWall` is
|
|
427
|
-
* the wall clock of the offer. The two are different by construction during
|
|
428
|
-
* catch-up, which is the whole reason this class exists.
|
|
429
|
-
*/
|
|
430
|
-
offer(mediaWall, nowWall) {
|
|
431
|
-
this.offered++;
|
|
432
|
-
if (this.done) return this.drop("budget-frames");
|
|
433
|
-
if (mediaWall >= this.liveEdgeWall) {
|
|
434
|
-
this.done = true;
|
|
435
|
-
return this.drop("live-edge");
|
|
436
|
-
}
|
|
437
|
-
if (nowWall - this.startedAtWall >= this.budget.maxWallMs) {
|
|
438
|
-
this.done = true;
|
|
439
|
-
return this.drop("budget-wall-ms");
|
|
440
|
-
}
|
|
441
|
-
if (this.inferred >= this.budget.maxFrames) {
|
|
442
|
-
this.done = true;
|
|
443
|
-
return this.drop("budget-frames");
|
|
444
|
-
}
|
|
445
|
-
if (this.lastAdmittedMediaWall !== null && mediaWall - this.lastAdmittedMediaWall < this.plan.strideMs) return this.drop("stride");
|
|
446
|
-
this.lastAdmittedMediaWall = mediaWall;
|
|
447
|
-
this.inferred++;
|
|
448
|
-
return ADMITTED;
|
|
449
|
-
}
|
|
450
|
-
/** End the burst without offering a frame (session torn down, decode ended). */
|
|
451
|
-
finish() {
|
|
452
|
-
this.done = true;
|
|
453
|
-
}
|
|
454
|
-
report() {
|
|
455
|
-
return {
|
|
456
|
-
offered: this.offered,
|
|
457
|
-
inferred: this.inferred,
|
|
458
|
-
dropped: this.offered - this.inferred,
|
|
459
|
-
droppedByReason: { ...this.drops },
|
|
460
|
-
strideMs: Math.round(this.plan.strideMs),
|
|
461
|
-
subsampled: this.plan.subsampled
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
drop(reason) {
|
|
465
|
-
this.drops[reason]++;
|
|
466
|
-
return {
|
|
467
|
-
admitted: false,
|
|
468
|
-
reason
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
//#endregion
|
|
473
326
|
//#region src/session-decode/lease-activity-gate.ts
|
|
474
327
|
/**
|
|
475
328
|
* Demand-driven gate for the native-frame lease capture (see
|
|
@@ -654,6 +507,56 @@ var LeaseAdmissionLedger = class {
|
|
|
654
507
|
}
|
|
655
508
|
};
|
|
656
509
|
//#endregion
|
|
510
|
+
//#region src/session-decode/media-clock.ts
|
|
511
|
+
/**
|
|
512
|
+
* Convert a container pts to milliseconds.
|
|
513
|
+
*
|
|
514
|
+
* Returns `null` — never a fabricated `0` — for a time base that cannot
|
|
515
|
+
* describe a duration. A zero here would place every frame of the session at
|
|
516
|
+
* the anchor, i.e. a whole burst of detections sharing one instant, which is
|
|
517
|
+
* exactly the kind of plausible-looking wrong timestamp this module exists to
|
|
518
|
+
* refuse. The caller degrades to the wall clock instead.
|
|
519
|
+
*/
|
|
520
|
+
function ptsToMs(pts, timeBase) {
|
|
521
|
+
if (!Number.isFinite(timeBase.num) || !Number.isFinite(timeBase.den)) return null;
|
|
522
|
+
if (timeBase.num <= 0 || timeBase.den <= 0) return null;
|
|
523
|
+
return Number(pts) * timeBase.num * 1e3 / timeBase.den;
|
|
524
|
+
}
|
|
525
|
+
/** Anchor the session's media clock on its first decoded frame. */
|
|
526
|
+
function anchorMediaClock(input) {
|
|
527
|
+
const preRoll = Number.isFinite(input.preRollMs) && input.preRollMs > 0 ? input.preRollMs : 0;
|
|
528
|
+
return {
|
|
529
|
+
firstPtsMs: input.firstPtsMs,
|
|
530
|
+
wallAtFirstPts: input.nowWall - preRoll
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* The wall clock at which the frame with `ptsMs` was shot.
|
|
535
|
+
*
|
|
536
|
+
* Clamped to `nowWall`: a container whose pts runs past the anchor (a clock
|
|
537
|
+
* jump, a source whose pts is not wall-clock-shaped, a re-dial that reset it)
|
|
538
|
+
* must never produce a track that starts in the FUTURE. A future `firstSeen`
|
|
539
|
+
* sorts a track ahead of everything, and no later pass can detect that it is
|
|
540
|
+
* wrong. Clamping loses precision on a pathological source and keeps the
|
|
541
|
+
* ordering property every consumer relies on.
|
|
542
|
+
*/
|
|
543
|
+
function mediaWallClockOf(anchor, ptsMs, nowWall) {
|
|
544
|
+
const shotAt = anchor.wallAtFirstPts + (ptsMs - anchor.firstPtsMs);
|
|
545
|
+
return shotAt > nowWall ? nowWall : shotAt;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* The anchor for a frame of dial `dialSeq`: the current one while the dial is
|
|
549
|
+
* the same, a fresh one — anchored on THIS frame — the moment the dial changes.
|
|
550
|
+
* Pure; the caller keeps the returned value and passes it back next frame.
|
|
551
|
+
*/
|
|
552
|
+
function mediaClockForDial(current, dialSeq, input) {
|
|
553
|
+
if (current !== null && current.dialSeq === dialSeq) return current;
|
|
554
|
+
return {
|
|
555
|
+
dialSeq,
|
|
556
|
+
anchor: anchorMediaClock(input)
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
//#endregion
|
|
657
560
|
//#region src/session-decode/native-frame-ring.ts
|
|
658
561
|
/**
|
|
659
562
|
* A hard-capped, insertion-ordered map from worker frameId → retained frame.
|
|
@@ -1674,18 +1577,6 @@ var REDIAL_MS = 3e3;
|
|
|
1674
1577
|
/** How often the worker emits its `framesDecoded/framesSkipped/deliveredFps` line. */
|
|
1675
1578
|
var METRICS_INTERVAL_MS = 1e4;
|
|
1676
1579
|
/**
|
|
1677
|
-
* Wall-clock ceiling on one pre-roll catch-up burst.
|
|
1678
|
-
*
|
|
1679
|
-
* A burst is a race against the live edge it is trying to get ahead of. Two
|
|
1680
|
-
* seconds in, the decoder has either caught up (the burst ended on its own) or
|
|
1681
|
-
* something is wrong — a cold GPU, a node under load, a camera whose GOP is
|
|
1682
|
-
* longer than its history — and continuing to spend inference on footage that
|
|
1683
|
-
* is now three or four seconds stale buys an operator nothing while a live
|
|
1684
|
-
* frame waits. The frame budget bounds the WORK; this bounds the WINDOW in
|
|
1685
|
-
* which that work is still worth doing.
|
|
1686
|
-
*/
|
|
1687
|
-
var PRE_ROLL_BURST_MAX_WALL_MS = 2e3;
|
|
1688
|
-
/**
|
|
1689
1580
|
* Hard cap on the native-frame retention ring (native-res crop feature). Kept
|
|
1690
1581
|
* DELIBERATELY TINY — its only job is to cover the detection→crop latency
|
|
1691
1582
|
* (typically 1-4 frames), NOT the full shm ring depth. On the HW path every
|
|
@@ -2257,7 +2148,7 @@ var DecodeWorkerChild = class {
|
|
|
2257
2148
|
lastColorProvenanceKeyByPath = /* @__PURE__ */ new Map();
|
|
2258
2149
|
/** Per-frame color provenance logging (opt-in; the transition marker is always on). */
|
|
2259
2150
|
colorDebug = process.env["CAMSTACK_DECODE_COLOR_DEBUG"] === "1";
|
|
2260
|
-
/**
|
|
2151
|
+
/** Throughput counters (plain integers, no per-frame allocation). */
|
|
2261
2152
|
framesDecoded = 0;
|
|
2262
2153
|
/** Releases that found a held frame / that found nothing to release. */
|
|
2263
2154
|
leaseReleased = 0;
|
|
@@ -2323,6 +2214,7 @@ var DecodeWorkerChild = class {
|
|
|
2323
2214
|
* frame arrives, or forever on a source whose pts cannot be read — in which
|
|
2324
2215
|
* case every frame falls back to the wall clock, exactly as before.
|
|
2325
2216
|
*/
|
|
2217
|
+
/** Anchored per DIAL (see `mediaClockForDial`): a re-dial is a new pts epoch. */
|
|
2326
2218
|
mediaClock = null;
|
|
2327
2219
|
/** History this dial asked the broker for; `0` ⇒ there is no burst. */
|
|
2328
2220
|
preRollMs = 0;
|
|
@@ -2338,7 +2230,7 @@ var DecodeWorkerChild = class {
|
|
|
2338
2230
|
/**
|
|
2339
2231
|
* The latest-wins decoded-frame slot + reserved-for-`toBuffer` holder +
|
|
2340
2232
|
* pending-pull flag — extracted to `FrameSlot` (Epic C P2 Task 2 / I2) so
|
|
2341
|
-
* the reserve-on-pull invariant
|
|
2233
|
+
* the reserve-on-pull invariant is unit-tested without
|
|
2342
2234
|
* node-av. See that file's doc comment for the full semantics.
|
|
2343
2235
|
*/
|
|
2344
2236
|
frames = new FrameSlot({ retainSuperseded: (frameId, frame) => this.captureNativeFrame(frameId, frame) });
|
|
@@ -3346,27 +3238,16 @@ var DecodeWorkerChild = class {
|
|
|
3346
3238
|
mediaWallClockOfFrame(frame, now) {
|
|
3347
3239
|
const ptsMs = this.frameMediaPtsMs(frame);
|
|
3348
3240
|
if (ptsMs === null) return now;
|
|
3349
|
-
if (
|
|
3350
|
-
|
|
3241
|
+
if (this.mediaClock === null || this.mediaClock.dialSeq !== this.dialSeq) {
|
|
3242
|
+
const servedPreRollMs = 0;
|
|
3243
|
+
if (this.preRollMs > 0) this.emitStderr(`[decode] pre-roll requested ${this.preRollMs}ms but the SERVED span does not reach this worker — burst suppressed, clock anchored at now (D411 follow-up)`);
|
|
3244
|
+
this.mediaClock = mediaClockForDial(this.mediaClock, this.dialSeq, {
|
|
3351
3245
|
firstPtsMs: ptsMs,
|
|
3352
|
-
preRollMs:
|
|
3246
|
+
preRollMs: servedPreRollMs,
|
|
3353
3247
|
nowWall: now
|
|
3354
3248
|
});
|
|
3355
|
-
if (this.preRollMs > 0 && this.preRollMaxFrames > 0) this.preRollBurst = new PreRollBurst({
|
|
3356
|
-
budget: {
|
|
3357
|
-
maxFrames: this.preRollMaxFrames,
|
|
3358
|
-
maxWallMs: PRE_ROLL_BURST_MAX_WALL_MS
|
|
3359
|
-
},
|
|
3360
|
-
plan: planPreRollBurst({
|
|
3361
|
-
spanMs: this.preRollMs,
|
|
3362
|
-
detectionFps: this.minIntervalMs > 0 ? 1e3 / this.minIntervalMs : 1,
|
|
3363
|
-
maxFrames: this.preRollMaxFrames
|
|
3364
|
-
}),
|
|
3365
|
-
startedAtWall: now,
|
|
3366
|
-
liveEdgeWall: now
|
|
3367
|
-
});
|
|
3368
3249
|
}
|
|
3369
|
-
return mediaWallClockOf(this.mediaClock, ptsMs, now);
|
|
3250
|
+
return mediaWallClockOf(this.mediaClock.anchor, ptsMs, now);
|
|
3370
3251
|
}
|
|
3371
3252
|
/** This frame's pts in ms, or `null` when the source cannot express one. */
|
|
3372
3253
|
frameMediaPtsMs(frame) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as formatNativeLeaseKnobs, n as isWorkerRequest, o as resolveNativeLeaseKnobs, r as logLevelForLine } from "../worker-protocol-
|
|
1
|
+
import { i as formatNativeLeaseKnobs, n as isWorkerRequest, o as resolveNativeLeaseKnobs, r as logLevelForLine } from "../worker-protocol-SzmaLNnP.mjs";
|
|
2
2
|
import { n as setSharpWarnSink, r as hostExternalEntryUrls, t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
3
3
|
//#region src/session-decode/color-conversion.ts
|
|
4
4
|
/**
|
|
@@ -322,153 +322,6 @@ var FrameSlot = class {
|
|
|
322
322
|
}
|
|
323
323
|
};
|
|
324
324
|
//#endregion
|
|
325
|
-
//#region src/session-decode/media-clock.ts
|
|
326
|
-
/**
|
|
327
|
-
* Convert a container pts to milliseconds.
|
|
328
|
-
*
|
|
329
|
-
* Returns `null` — never a fabricated `0` — for a time base that cannot
|
|
330
|
-
* describe a duration. A zero here would place every frame of the session at
|
|
331
|
-
* the anchor, i.e. a whole burst of detections sharing one instant, which is
|
|
332
|
-
* exactly the kind of plausible-looking wrong timestamp this module exists to
|
|
333
|
-
* refuse. The caller degrades to the wall clock instead.
|
|
334
|
-
*/
|
|
335
|
-
function ptsToMs(pts, timeBase) {
|
|
336
|
-
if (!Number.isFinite(timeBase.num) || !Number.isFinite(timeBase.den)) return null;
|
|
337
|
-
if (timeBase.num <= 0 || timeBase.den <= 0) return null;
|
|
338
|
-
return Number(pts) * timeBase.num * 1e3 / timeBase.den;
|
|
339
|
-
}
|
|
340
|
-
/** Anchor the session's media clock on its first decoded frame. */
|
|
341
|
-
function anchorMediaClock(input) {
|
|
342
|
-
const preRoll = Number.isFinite(input.preRollMs) && input.preRollMs > 0 ? input.preRollMs : 0;
|
|
343
|
-
return {
|
|
344
|
-
firstPtsMs: input.firstPtsMs,
|
|
345
|
-
wallAtFirstPts: input.nowWall - preRoll
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* The wall clock at which the frame with `ptsMs` was shot.
|
|
350
|
-
*
|
|
351
|
-
* Clamped to `nowWall`: a container whose pts runs past the anchor (a clock
|
|
352
|
-
* jump, a source whose pts is not wall-clock-shaped, a re-dial that reset it)
|
|
353
|
-
* must never produce a track that starts in the FUTURE. A future `firstSeen`
|
|
354
|
-
* sorts a track ahead of everything, and no later pass can detect that it is
|
|
355
|
-
* wrong. Clamping loses precision on a pathological source and keeps the
|
|
356
|
-
* ordering property every consumer relies on.
|
|
357
|
-
*/
|
|
358
|
-
function mediaWallClockOf(anchor, ptsMs, nowWall) {
|
|
359
|
-
const shotAt = anchor.wallAtFirstPts + (ptsMs - anchor.firstPtsMs);
|
|
360
|
-
return shotAt > nowWall ? nowWall : shotAt;
|
|
361
|
-
}
|
|
362
|
-
//#endregion
|
|
363
|
-
//#region src/session-decode/pre-roll-burst.ts
|
|
364
|
-
var ADMITTED = { admitted: true };
|
|
365
|
-
/**
|
|
366
|
-
* Choose the media stride for one burst.
|
|
367
|
-
*
|
|
368
|
-
* `plannedFrames` is what the stride actually yields, so a caller can log the
|
|
369
|
-
* intent next to the outcome — a budget that is never reached and a budget that
|
|
370
|
-
* is always hit are different problems and used to read the same.
|
|
371
|
-
*/
|
|
372
|
-
function planPreRollBurst(input) {
|
|
373
|
-
const span = Number.isFinite(input.spanMs) && input.spanMs > 0 ? input.spanMs : 0;
|
|
374
|
-
const fps = Number.isFinite(input.detectionFps) && input.detectionFps > 0 ? input.detectionFps : 1;
|
|
375
|
-
const maxFrames = Number.isFinite(input.maxFrames) && input.maxFrames > 0 ? input.maxFrames : 0;
|
|
376
|
-
if (span === 0 || maxFrames === 0) return {
|
|
377
|
-
strideMs: Number.POSITIVE_INFINITY,
|
|
378
|
-
plannedFrames: 0,
|
|
379
|
-
subsampled: false
|
|
380
|
-
};
|
|
381
|
-
const cadenceMs = 1e3 / fps;
|
|
382
|
-
const budgetStrideMs = span / maxFrames;
|
|
383
|
-
const subsampled = budgetStrideMs > cadenceMs;
|
|
384
|
-
const strideMs = subsampled ? budgetStrideMs : cadenceMs;
|
|
385
|
-
return {
|
|
386
|
-
strideMs,
|
|
387
|
-
plannedFrames: Math.min(maxFrames, Math.max(1, Math.floor(span / strideMs))),
|
|
388
|
-
subsampled
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* The live, per-session budget. Stateful by necessity — a stride is a decision
|
|
393
|
-
* about the PREVIOUS admission — and deliberately free of any clock of its own:
|
|
394
|
-
* both times are passed in, so a test can drive a catch-up burst without
|
|
395
|
-
* pretending 4 s of media takes 4 s to decode.
|
|
396
|
-
*/
|
|
397
|
-
var PreRollBurst = class {
|
|
398
|
-
budget;
|
|
399
|
-
plan;
|
|
400
|
-
startedAtWall;
|
|
401
|
-
liveEdgeWall;
|
|
402
|
-
lastAdmittedMediaWall = null;
|
|
403
|
-
offered = 0;
|
|
404
|
-
inferred = 0;
|
|
405
|
-
done = false;
|
|
406
|
-
drops = {
|
|
407
|
-
stride: 0,
|
|
408
|
-
"budget-frames": 0,
|
|
409
|
-
"budget-wall-ms": 0,
|
|
410
|
-
"live-edge": 0
|
|
411
|
-
};
|
|
412
|
-
constructor(input) {
|
|
413
|
-
this.budget = input.budget;
|
|
414
|
-
this.plan = input.plan;
|
|
415
|
-
this.startedAtWall = input.startedAtWall;
|
|
416
|
-
this.liveEdgeWall = input.liveEdgeWall;
|
|
417
|
-
}
|
|
418
|
-
/** True once the burst has ended — by budget, or by reaching the live edge. */
|
|
419
|
-
get finished() {
|
|
420
|
-
return this.done;
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Offer one decoded history frame.
|
|
424
|
-
*
|
|
425
|
-
* `mediaWall` is the frame's OWN moment (see `media-clock.ts`); `nowWall` is
|
|
426
|
-
* the wall clock of the offer. The two are different by construction during
|
|
427
|
-
* catch-up, which is the whole reason this class exists.
|
|
428
|
-
*/
|
|
429
|
-
offer(mediaWall, nowWall) {
|
|
430
|
-
this.offered++;
|
|
431
|
-
if (this.done) return this.drop("budget-frames");
|
|
432
|
-
if (mediaWall >= this.liveEdgeWall) {
|
|
433
|
-
this.done = true;
|
|
434
|
-
return this.drop("live-edge");
|
|
435
|
-
}
|
|
436
|
-
if (nowWall - this.startedAtWall >= this.budget.maxWallMs) {
|
|
437
|
-
this.done = true;
|
|
438
|
-
return this.drop("budget-wall-ms");
|
|
439
|
-
}
|
|
440
|
-
if (this.inferred >= this.budget.maxFrames) {
|
|
441
|
-
this.done = true;
|
|
442
|
-
return this.drop("budget-frames");
|
|
443
|
-
}
|
|
444
|
-
if (this.lastAdmittedMediaWall !== null && mediaWall - this.lastAdmittedMediaWall < this.plan.strideMs) return this.drop("stride");
|
|
445
|
-
this.lastAdmittedMediaWall = mediaWall;
|
|
446
|
-
this.inferred++;
|
|
447
|
-
return ADMITTED;
|
|
448
|
-
}
|
|
449
|
-
/** End the burst without offering a frame (session torn down, decode ended). */
|
|
450
|
-
finish() {
|
|
451
|
-
this.done = true;
|
|
452
|
-
}
|
|
453
|
-
report() {
|
|
454
|
-
return {
|
|
455
|
-
offered: this.offered,
|
|
456
|
-
inferred: this.inferred,
|
|
457
|
-
dropped: this.offered - this.inferred,
|
|
458
|
-
droppedByReason: { ...this.drops },
|
|
459
|
-
strideMs: Math.round(this.plan.strideMs),
|
|
460
|
-
subsampled: this.plan.subsampled
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
drop(reason) {
|
|
464
|
-
this.drops[reason]++;
|
|
465
|
-
return {
|
|
466
|
-
admitted: false,
|
|
467
|
-
reason
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
//#endregion
|
|
472
325
|
//#region src/session-decode/lease-activity-gate.ts
|
|
473
326
|
/**
|
|
474
327
|
* Demand-driven gate for the native-frame lease capture (see
|
|
@@ -653,6 +506,56 @@ var LeaseAdmissionLedger = class {
|
|
|
653
506
|
}
|
|
654
507
|
};
|
|
655
508
|
//#endregion
|
|
509
|
+
//#region src/session-decode/media-clock.ts
|
|
510
|
+
/**
|
|
511
|
+
* Convert a container pts to milliseconds.
|
|
512
|
+
*
|
|
513
|
+
* Returns `null` — never a fabricated `0` — for a time base that cannot
|
|
514
|
+
* describe a duration. A zero here would place every frame of the session at
|
|
515
|
+
* the anchor, i.e. a whole burst of detections sharing one instant, which is
|
|
516
|
+
* exactly the kind of plausible-looking wrong timestamp this module exists to
|
|
517
|
+
* refuse. The caller degrades to the wall clock instead.
|
|
518
|
+
*/
|
|
519
|
+
function ptsToMs(pts, timeBase) {
|
|
520
|
+
if (!Number.isFinite(timeBase.num) || !Number.isFinite(timeBase.den)) return null;
|
|
521
|
+
if (timeBase.num <= 0 || timeBase.den <= 0) return null;
|
|
522
|
+
return Number(pts) * timeBase.num * 1e3 / timeBase.den;
|
|
523
|
+
}
|
|
524
|
+
/** Anchor the session's media clock on its first decoded frame. */
|
|
525
|
+
function anchorMediaClock(input) {
|
|
526
|
+
const preRoll = Number.isFinite(input.preRollMs) && input.preRollMs > 0 ? input.preRollMs : 0;
|
|
527
|
+
return {
|
|
528
|
+
firstPtsMs: input.firstPtsMs,
|
|
529
|
+
wallAtFirstPts: input.nowWall - preRoll
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* The wall clock at which the frame with `ptsMs` was shot.
|
|
534
|
+
*
|
|
535
|
+
* Clamped to `nowWall`: a container whose pts runs past the anchor (a clock
|
|
536
|
+
* jump, a source whose pts is not wall-clock-shaped, a re-dial that reset it)
|
|
537
|
+
* must never produce a track that starts in the FUTURE. A future `firstSeen`
|
|
538
|
+
* sorts a track ahead of everything, and no later pass can detect that it is
|
|
539
|
+
* wrong. Clamping loses precision on a pathological source and keeps the
|
|
540
|
+
* ordering property every consumer relies on.
|
|
541
|
+
*/
|
|
542
|
+
function mediaWallClockOf(anchor, ptsMs, nowWall) {
|
|
543
|
+
const shotAt = anchor.wallAtFirstPts + (ptsMs - anchor.firstPtsMs);
|
|
544
|
+
return shotAt > nowWall ? nowWall : shotAt;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* The anchor for a frame of dial `dialSeq`: the current one while the dial is
|
|
548
|
+
* the same, a fresh one — anchored on THIS frame — the moment the dial changes.
|
|
549
|
+
* Pure; the caller keeps the returned value and passes it back next frame.
|
|
550
|
+
*/
|
|
551
|
+
function mediaClockForDial(current, dialSeq, input) {
|
|
552
|
+
if (current !== null && current.dialSeq === dialSeq) return current;
|
|
553
|
+
return {
|
|
554
|
+
dialSeq,
|
|
555
|
+
anchor: anchorMediaClock(input)
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
656
559
|
//#region src/session-decode/native-frame-ring.ts
|
|
657
560
|
/**
|
|
658
561
|
* A hard-capped, insertion-ordered map from worker frameId → retained frame.
|
|
@@ -1673,18 +1576,6 @@ var REDIAL_MS = 3e3;
|
|
|
1673
1576
|
/** How often the worker emits its `framesDecoded/framesSkipped/deliveredFps` line. */
|
|
1674
1577
|
var METRICS_INTERVAL_MS = 1e4;
|
|
1675
1578
|
/**
|
|
1676
|
-
* Wall-clock ceiling on one pre-roll catch-up burst.
|
|
1677
|
-
*
|
|
1678
|
-
* A burst is a race against the live edge it is trying to get ahead of. Two
|
|
1679
|
-
* seconds in, the decoder has either caught up (the burst ended on its own) or
|
|
1680
|
-
* something is wrong — a cold GPU, a node under load, a camera whose GOP is
|
|
1681
|
-
* longer than its history — and continuing to spend inference on footage that
|
|
1682
|
-
* is now three or four seconds stale buys an operator nothing while a live
|
|
1683
|
-
* frame waits. The frame budget bounds the WORK; this bounds the WINDOW in
|
|
1684
|
-
* which that work is still worth doing.
|
|
1685
|
-
*/
|
|
1686
|
-
var PRE_ROLL_BURST_MAX_WALL_MS = 2e3;
|
|
1687
|
-
/**
|
|
1688
1579
|
* Hard cap on the native-frame retention ring (native-res crop feature). Kept
|
|
1689
1580
|
* DELIBERATELY TINY — its only job is to cover the detection→crop latency
|
|
1690
1581
|
* (typically 1-4 frames), NOT the full shm ring depth. On the HW path every
|
|
@@ -2256,7 +2147,7 @@ var DecodeWorkerChild = class {
|
|
|
2256
2147
|
lastColorProvenanceKeyByPath = /* @__PURE__ */ new Map();
|
|
2257
2148
|
/** Per-frame color provenance logging (opt-in; the transition marker is always on). */
|
|
2258
2149
|
colorDebug = process.env["CAMSTACK_DECODE_COLOR_DEBUG"] === "1";
|
|
2259
|
-
/**
|
|
2150
|
+
/** Throughput counters (plain integers, no per-frame allocation). */
|
|
2260
2151
|
framesDecoded = 0;
|
|
2261
2152
|
/** Releases that found a held frame / that found nothing to release. */
|
|
2262
2153
|
leaseReleased = 0;
|
|
@@ -2322,6 +2213,7 @@ var DecodeWorkerChild = class {
|
|
|
2322
2213
|
* frame arrives, or forever on a source whose pts cannot be read — in which
|
|
2323
2214
|
* case every frame falls back to the wall clock, exactly as before.
|
|
2324
2215
|
*/
|
|
2216
|
+
/** Anchored per DIAL (see `mediaClockForDial`): a re-dial is a new pts epoch. */
|
|
2325
2217
|
mediaClock = null;
|
|
2326
2218
|
/** History this dial asked the broker for; `0` ⇒ there is no burst. */
|
|
2327
2219
|
preRollMs = 0;
|
|
@@ -2337,7 +2229,7 @@ var DecodeWorkerChild = class {
|
|
|
2337
2229
|
/**
|
|
2338
2230
|
* The latest-wins decoded-frame slot + reserved-for-`toBuffer` holder +
|
|
2339
2231
|
* pending-pull flag — extracted to `FrameSlot` (Epic C P2 Task 2 / I2) so
|
|
2340
|
-
* the reserve-on-pull invariant
|
|
2232
|
+
* the reserve-on-pull invariant is unit-tested without
|
|
2341
2233
|
* node-av. See that file's doc comment for the full semantics.
|
|
2342
2234
|
*/
|
|
2343
2235
|
frames = new FrameSlot({ retainSuperseded: (frameId, frame) => this.captureNativeFrame(frameId, frame) });
|
|
@@ -3345,27 +3237,16 @@ var DecodeWorkerChild = class {
|
|
|
3345
3237
|
mediaWallClockOfFrame(frame, now) {
|
|
3346
3238
|
const ptsMs = this.frameMediaPtsMs(frame);
|
|
3347
3239
|
if (ptsMs === null) return now;
|
|
3348
|
-
if (
|
|
3349
|
-
|
|
3240
|
+
if (this.mediaClock === null || this.mediaClock.dialSeq !== this.dialSeq) {
|
|
3241
|
+
const servedPreRollMs = 0;
|
|
3242
|
+
if (this.preRollMs > 0) this.emitStderr(`[decode] pre-roll requested ${this.preRollMs}ms but the SERVED span does not reach this worker — burst suppressed, clock anchored at now (D411 follow-up)`);
|
|
3243
|
+
this.mediaClock = mediaClockForDial(this.mediaClock, this.dialSeq, {
|
|
3350
3244
|
firstPtsMs: ptsMs,
|
|
3351
|
-
preRollMs:
|
|
3245
|
+
preRollMs: servedPreRollMs,
|
|
3352
3246
|
nowWall: now
|
|
3353
3247
|
});
|
|
3354
|
-
if (this.preRollMs > 0 && this.preRollMaxFrames > 0) this.preRollBurst = new PreRollBurst({
|
|
3355
|
-
budget: {
|
|
3356
|
-
maxFrames: this.preRollMaxFrames,
|
|
3357
|
-
maxWallMs: PRE_ROLL_BURST_MAX_WALL_MS
|
|
3358
|
-
},
|
|
3359
|
-
plan: planPreRollBurst({
|
|
3360
|
-
spanMs: this.preRollMs,
|
|
3361
|
-
detectionFps: this.minIntervalMs > 0 ? 1e3 / this.minIntervalMs : 1,
|
|
3362
|
-
maxFrames: this.preRollMaxFrames
|
|
3363
|
-
}),
|
|
3364
|
-
startedAtWall: now,
|
|
3365
|
-
liveEdgeWall: now
|
|
3366
|
-
});
|
|
3367
3248
|
}
|
|
3368
|
-
return mediaWallClockOf(this.mediaClock, ptsMs, now);
|
|
3249
|
+
return mediaWallClockOf(this.mediaClock.anchor, ptsMs, now);
|
|
3369
3250
|
}
|
|
3370
3251
|
/** This frame's pts in ms, or `null` when the source cannot express one. */
|
|
3371
3252
|
frameMediaPtsMs(frame) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DICVQkNi.mjs";
|
|
2
2
|
import { a as p, i as m, n as h, o as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
|
|
3
3
|
import { n as y, r as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
|
|
4
|
-
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-
|
|
4
|
+
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-yJ2CyixO.mjs";
|
|
5
5
|
import { n as C, t as w } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
|
|
6
6
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
7
7
|
var T = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), E = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), D = (e) => {
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.95",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_stream_broker_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.175",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_stream_broker_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.143",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|