@camstack/addon-pipeline 1.2.213 → 1.2.214
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-B-PccVsO.mjs → default-detection-model-C2vYSX3O.mjs} +1 -1
- package/dist/{default-detection-model-BZTjgdod.js → default-detection-model-f0PImZnh.js} +1 -1
- package/dist/detection-pipeline/index.js +5 -5
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-CyfP2nFh.js → dist-DoHcimdu.js} +42 -1
- package/dist/{dist-CsI90TVi.mjs → dist-DttqKvsQ.mjs} +42 -1
- package/dist/{lazy-sharp-BawPy_a9.js → lazy-sharp-BeeQGCUu.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-CkBqyM-0.js → node-6uF72wri.js} +1 -1
- package/dist/{node-6Pb_t1Fm.mjs → node-NtM7__8q.mjs} +1 -1
- package/dist/pipeline-runner/index.js +213 -24
- package/dist/pipeline-runner/index.mjs +212 -23
- package/dist/{process-memory-B8yoOaEl.js → process-memory-BRdyl5fE.js} +1 -1
- package/dist/{process-memory-B_-QzMHn.mjs → process-memory-CddpCDWL.mjs} +1 -1
- package/dist/recorder/index.js +4 -2
- package/dist/recorder/index.mjs +4 -2
- package/dist/{segment-demux-js-oOpXzk2J.js → segment-demux-js-CkJPdyDH.js} +1 -1
- package/dist/{segment-demux-js-CBvM6zIM.mjs → segment-demux-js-CsFtPVzH.mjs} +1 -1
- package/dist/session-decode/decode-worker-child.js +319 -8
- package/dist/session-decode/decode-worker-child.mjs +318 -7
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Cf0O5S13.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbBa4iV-.mjs} +3 -3
- 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-3eT4CIOk.mjs → hostInit-CagSUJH7.mjs} +3 -3
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-DNqdtZzZ.mjs → worker-protocol-BKwkLZWT.mjs} +5 -2
- package/dist/{worker-protocol-CYopQrkK.js → worker-protocol-BWO2WNjw.js} +5 -2
- package/package.json +1 -1
|
@@ -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-BKwkLZWT.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
|
/**
|
|
@@ -207,7 +207,9 @@ function toReply(held) {
|
|
|
207
207
|
frameId: held.frameId,
|
|
208
208
|
timestamp: held.timestamp,
|
|
209
209
|
width: held.width,
|
|
210
|
-
height: held.height
|
|
210
|
+
height: held.height,
|
|
211
|
+
capturedAt: held.capturedAt,
|
|
212
|
+
preRoll: held.preRoll
|
|
211
213
|
};
|
|
212
214
|
}
|
|
213
215
|
/**
|
|
@@ -251,7 +253,7 @@ var FrameSlot = class {
|
|
|
251
253
|
* immediately and reserving the frame); otherwise returns `null` and the
|
|
252
254
|
* frame simply waits in the slot for the next `pull`.
|
|
253
255
|
*/
|
|
254
|
-
publish(frame, timestamp, minIntervalMs) {
|
|
256
|
+
publish(frame, timestamp, minIntervalMs, meta) {
|
|
255
257
|
if (minIntervalMs > 0 && this.lastPublishedAt !== null && timestamp - this.lastPublishedAt < minIntervalMs) {
|
|
256
258
|
this.throttledCount++;
|
|
257
259
|
frame.free();
|
|
@@ -265,7 +267,9 @@ var FrameSlot = class {
|
|
|
265
267
|
frame,
|
|
266
268
|
timestamp,
|
|
267
269
|
width: frame.width,
|
|
268
|
-
height: frame.height
|
|
270
|
+
height: frame.height,
|
|
271
|
+
capturedAt: meta?.capturedAt ?? timestamp,
|
|
272
|
+
preRoll: meta?.preRoll ?? false
|
|
269
273
|
};
|
|
270
274
|
if (previous) this.supersededCount++;
|
|
271
275
|
previous?.frame.free();
|
|
@@ -318,6 +322,153 @@ var FrameSlot = class {
|
|
|
318
322
|
}
|
|
319
323
|
};
|
|
320
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
|
|
321
472
|
//#region src/session-decode/lease-activity-gate.ts
|
|
322
473
|
/**
|
|
323
474
|
* Demand-driven gate for the native-frame lease capture (see
|
|
@@ -1522,6 +1673,18 @@ var REDIAL_MS = 3e3;
|
|
|
1522
1673
|
/** How often the worker emits its `framesDecoded/framesSkipped/deliveredFps` line. */
|
|
1523
1674
|
var METRICS_INTERVAL_MS = 1e4;
|
|
1524
1675
|
/**
|
|
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
|
+
/**
|
|
1525
1688
|
* Hard cap on the native-frame retention ring (native-res crop feature). Kept
|
|
1526
1689
|
* DELIBERATELY TINY — its only job is to cover the detection→crop latency
|
|
1527
1690
|
* (typically 1-4 frames), NOT the full shm ring depth. On the HW path every
|
|
@@ -2155,6 +2318,23 @@ var DecodeWorkerChild = class {
|
|
|
2155
2318
|
sessionFormat = "rgb";
|
|
2156
2319
|
minIntervalMs = 0;
|
|
2157
2320
|
/**
|
|
2321
|
+
* Session media clock, anchored on the FIRST decoded frame. Null until that
|
|
2322
|
+
* frame arrives, or forever on a source whose pts cannot be read — in which
|
|
2323
|
+
* case every frame falls back to the wall clock, exactly as before.
|
|
2324
|
+
*/
|
|
2325
|
+
mediaClock = null;
|
|
2326
|
+
/** History this dial asked the broker for; `0` ⇒ there is no burst. */
|
|
2327
|
+
preRollMs = 0;
|
|
2328
|
+
/** Frames the catch-up burst may send to inference. */
|
|
2329
|
+
preRollMaxFrames = 0;
|
|
2330
|
+
/** The live catch-up burst, or null once it has ended (or never started). */
|
|
2331
|
+
preRollBurst = null;
|
|
2332
|
+
/** Whether the burst's report has already been sent — it is sent exactly once. */
|
|
2333
|
+
preRollReported = false;
|
|
2334
|
+
/** Media wall clock of the OLDEST and NEWEST frames the burst admitted. */
|
|
2335
|
+
preRollFirstMediaWall = null;
|
|
2336
|
+
preRollLastMediaWall = null;
|
|
2337
|
+
/**
|
|
2158
2338
|
* The latest-wins decoded-frame slot + reserved-for-`toBuffer` holder +
|
|
2159
2339
|
* pending-pull flag — extracted to `FrameSlot` (Epic C P2 Task 2 / I2) so
|
|
2160
2340
|
* the reserve-on-pull invariant (`e3c78a9b`) is unit-tested without
|
|
@@ -2216,6 +2396,7 @@ var DecodeWorkerChild = class {
|
|
|
2216
2396
|
this.metricsTimer = null;
|
|
2217
2397
|
}
|
|
2218
2398
|
if (this.started) this.logMetrics(true);
|
|
2399
|
+
this.endPreRollBurst();
|
|
2219
2400
|
this.abortController?.abort();
|
|
2220
2401
|
this.frames.free();
|
|
2221
2402
|
this.closeInput();
|
|
@@ -2275,6 +2456,8 @@ var DecodeWorkerChild = class {
|
|
|
2275
2456
|
this.emitStderr(`decode-worker-child: ${formatNativeLeaseKnobs(NATIVE_LEASE_KNOBS)}\n`, "info");
|
|
2276
2457
|
this.sessionFormat = opts.format ?? "rgb";
|
|
2277
2458
|
this.minIntervalMs = opts.fps && opts.fps > 0 ? 1e3 / opts.fps : 0;
|
|
2459
|
+
this.preRollMs = opts.preRollMs && opts.preRollMs > 0 ? opts.preRollMs : 0;
|
|
2460
|
+
this.preRollMaxFrames = opts.preRollMaxFrames && opts.preRollMaxFrames > 0 ? opts.preRollMaxFrames : 0;
|
|
2278
2461
|
this.lastMetricsAt = Date.now();
|
|
2279
2462
|
this.metricsTimer = setInterval(() => this.logMetrics(false), METRICS_INTERVAL_MS);
|
|
2280
2463
|
this.metricsTimer.unref?.();
|
|
@@ -3107,14 +3290,140 @@ var DecodeWorkerChild = class {
|
|
|
3107
3290
|
}
|
|
3108
3291
|
this.publishFrame(frame);
|
|
3109
3292
|
}
|
|
3110
|
-
/**
|
|
3293
|
+
/**
|
|
3294
|
+
* Publish to the latest-wins slot; deliver immediately if a `pull` is pending.
|
|
3295
|
+
*
|
|
3296
|
+
* ## Where the pre-roll used to die
|
|
3297
|
+
*
|
|
3298
|
+
* This method used to be one line: `this.frames.publish(frame, Date.now(),
|
|
3299
|
+
* this.minIntervalMs)`. `FrameSlot.publish` throttles by comparing that
|
|
3300
|
+
* `Date.now()` against the previous publish's, and during a catch-up burst
|
|
3301
|
+
* that comparison is meaningless — the restreamer replays ~4 s of retained
|
|
3302
|
+
* media and the decoder chews through all of it inside a few hundred
|
|
3303
|
+
* milliseconds of WALL clock. So the throttle admitted one or two frames of
|
|
3304
|
+
* the whole window and `frame.free()`d the rest, and the two frames on which
|
|
3305
|
+
* device 617's cyclist scored above the vehicle floor (0.5669 and 0.5366,
|
|
3306
|
+
* 1.68 s before the live track was born) were freed here, having been
|
|
3307
|
+
* received, decoded and paid for in full.
|
|
3308
|
+
*
|
|
3309
|
+
* The fix is not to weaken the throttle. A live frame still paces on wall
|
|
3310
|
+
* clock, unchanged. A HISTORY frame paces on its own MEDIA clock instead,
|
|
3311
|
+
* under an explicit budget, because media time is the only clock on which
|
|
3312
|
+
* "one frame per 333 ms of footage" means what it says.
|
|
3313
|
+
*/
|
|
3111
3314
|
publishFrame(frame) {
|
|
3112
|
-
const
|
|
3315
|
+
const now = Date.now();
|
|
3316
|
+
const mediaWall = this.mediaWallClockOfFrame(frame, now);
|
|
3317
|
+
const verdict = this.offerToPreRollBurst(mediaWall, now);
|
|
3318
|
+
if (verdict === "dropped") {
|
|
3319
|
+
this.framesSkipped++;
|
|
3320
|
+
frame.free();
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
const preRoll = verdict === "pre-roll";
|
|
3324
|
+
const reply = this.frames.publish(frame, preRoll ? mediaWall : now, preRoll ? 0 : this.minIntervalMs, {
|
|
3325
|
+
capturedAt: mediaWall,
|
|
3326
|
+
preRoll
|
|
3327
|
+
});
|
|
3113
3328
|
if (reply) {
|
|
3114
3329
|
this.pendingPull = false;
|
|
3115
3330
|
this.deliverFrame(reply);
|
|
3116
3331
|
}
|
|
3117
3332
|
}
|
|
3333
|
+
/**
|
|
3334
|
+
* The frame's own moment, from its pts — or the wall clock when the source
|
|
3335
|
+
* cannot say.
|
|
3336
|
+
*
|
|
3337
|
+
* The media clock is anchored on the FIRST frame of the session: that frame
|
|
3338
|
+
* is `preRollMs` old by construction, because the pre-roll is what the dial
|
|
3339
|
+
* asked for and the restreamer serves it before anything live. A source whose
|
|
3340
|
+
* pts cannot be converted (no time base, a zero denominator) gets no clock at
|
|
3341
|
+
* all and therefore no burst — a fabricated media time would date a whole
|
|
3342
|
+
* burst of detections at one wrong instant, which is worse than not inferring
|
|
3343
|
+
* them at all.
|
|
3344
|
+
*/
|
|
3345
|
+
mediaWallClockOfFrame(frame, now) {
|
|
3346
|
+
const ptsMs = this.frameMediaPtsMs(frame);
|
|
3347
|
+
if (ptsMs === null) return now;
|
|
3348
|
+
if (!this.mediaClock) {
|
|
3349
|
+
this.mediaClock = anchorMediaClock({
|
|
3350
|
+
firstPtsMs: ptsMs,
|
|
3351
|
+
preRollMs: this.preRollMs,
|
|
3352
|
+
nowWall: now
|
|
3353
|
+
});
|
|
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
|
+
}
|
|
3368
|
+
return mediaWallClockOf(this.mediaClock, ptsMs, now);
|
|
3369
|
+
}
|
|
3370
|
+
/** This frame's pts in ms, or `null` when the source cannot express one. */
|
|
3371
|
+
frameMediaPtsMs(frame) {
|
|
3372
|
+
const pts = frame.pts;
|
|
3373
|
+
const timeBase = frame.timeBase;
|
|
3374
|
+
if (typeof pts !== "bigint") return null;
|
|
3375
|
+
if (typeof timeBase !== "object" || timeBase === null) return null;
|
|
3376
|
+
const num = Reflect.get(timeBase, "num");
|
|
3377
|
+
const den = Reflect.get(timeBase, "den");
|
|
3378
|
+
if (typeof num !== "number" || typeof den !== "number") return null;
|
|
3379
|
+
return ptsToMs(pts, {
|
|
3380
|
+
num,
|
|
3381
|
+
den
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* Ask the burst whether this frame is history worth inferring.
|
|
3386
|
+
*
|
|
3387
|
+
* `'live'` — no burst, or the burst has ended. The ordinary path, untouched.
|
|
3388
|
+
* `'pre-roll'` — inside the budget; delivered marked, to the runner's lane.
|
|
3389
|
+
* `'dropped'` — history the budget refused. Freed by the caller and counted.
|
|
3390
|
+
*/
|
|
3391
|
+
offerToPreRollBurst(mediaWall, now) {
|
|
3392
|
+
const burst = this.preRollBurst;
|
|
3393
|
+
if (!burst) return "live";
|
|
3394
|
+
const verdict = burst.offer(mediaWall, now);
|
|
3395
|
+
if (verdict.admitted) {
|
|
3396
|
+
if (this.preRollFirstMediaWall === null) this.preRollFirstMediaWall = mediaWall;
|
|
3397
|
+
this.preRollLastMediaWall = mediaWall;
|
|
3398
|
+
return "pre-roll";
|
|
3399
|
+
}
|
|
3400
|
+
if (burst.finished) {
|
|
3401
|
+
this.endPreRollBurst();
|
|
3402
|
+
return verdict.reason === "live-edge" ? "live" : "dropped";
|
|
3403
|
+
}
|
|
3404
|
+
return "dropped";
|
|
3405
|
+
}
|
|
3406
|
+
/** Close the burst and send its accounting — exactly once per session. */
|
|
3407
|
+
endPreRollBurst() {
|
|
3408
|
+
const burst = this.preRollBurst;
|
|
3409
|
+
this.preRollBurst = null;
|
|
3410
|
+
if (!burst || this.preRollReported) return;
|
|
3411
|
+
this.preRollReported = true;
|
|
3412
|
+
burst.finish();
|
|
3413
|
+
const report = burst.report();
|
|
3414
|
+
const first = this.preRollFirstMediaWall;
|
|
3415
|
+
const last = this.preRollLastMediaWall;
|
|
3416
|
+
this.send({
|
|
3417
|
+
kind: "preRollReport",
|
|
3418
|
+
offered: report.offered,
|
|
3419
|
+
inferred: report.inferred,
|
|
3420
|
+
dropped: report.dropped,
|
|
3421
|
+
droppedByReason: report.droppedByReason,
|
|
3422
|
+
strideMs: report.strideMs,
|
|
3423
|
+
subsampled: report.subsampled,
|
|
3424
|
+
spanMs: first !== null && last !== null ? Math.round(last - first) : 0
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3118
3427
|
/** Send a `frame` reply to the parent and count it toward `deliveredFps`. */
|
|
3119
3428
|
deliverFrame(reply) {
|
|
3120
3429
|
this.framesDelivered++;
|
|
@@ -3171,7 +3480,9 @@ var DecodeWorkerChild = class {
|
|
|
3171
3480
|
timestamp: reply.timestamp,
|
|
3172
3481
|
width: reply.width,
|
|
3173
3482
|
height: reply.height,
|
|
3174
|
-
format: this.sessionFormat
|
|
3483
|
+
format: this.sessionFormat,
|
|
3484
|
+
capturedAt: reply.capturedAt,
|
|
3485
|
+
...reply.preRoll ? { preRoll: true } : {}
|
|
3175
3486
|
};
|
|
3176
3487
|
}
|
|
3177
3488
|
/**
|
|
@@ -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.92",
|
|
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.172",
|
|
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.140",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_segment_demux_js = require("../segment-demux-js-
|
|
1
|
+
const require_segment_demux_js = require("../segment-demux-js-CkJPdyDH.js");
|
|
2
2
|
let node_worker_threads = require("node:worker_threads");
|
|
3
3
|
//#region src/stream-broker/recorded/demux-worker-child.ts
|
|
4
4
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as demuxSegmentFullJs } from "../segment-demux-js-
|
|
1
|
+
import { t as demuxSegmentFullJs } from "../segment-demux-js-CsFtPVzH.mjs";
|
|
2
2
|
import { parentPort } from "node:worker_threads";
|
|
3
3
|
//#region src/stream-broker/recorded/demux-worker-child.ts
|
|
4
4
|
/**
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.172",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.92",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.140",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -2,12 +2,12 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_node = require("../node-
|
|
5
|
+
const require_dist = require("../dist-DoHcimdu.js");
|
|
6
|
+
const require_node = require("../node-6uF72wri.js");
|
|
7
7
|
const require_restream_intent = require("../restream-intent-B1Difcnq.js");
|
|
8
8
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
9
9
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
|
10
|
-
const require_segment_demux_js = require("../segment-demux-js-
|
|
10
|
+
const require_segment_demux_js = require("../segment-demux-js-CkJPdyDH.js");
|
|
11
11
|
let node_os = require("node:os");
|
|
12
12
|
node_os = require_dist.__toESM(node_os);
|
|
13
13
|
let node_crypto = require("node:crypto");
|
|
@@ -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-
|
|
3
|
-
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-
|
|
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-DttqKvsQ.mjs";
|
|
3
|
+
import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-NtM7__8q.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-
|
|
7
|
+
import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-CsFtPVzH.mjs";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import * as os from "node:os";
|
|
10
10
|
import { networkInterfaces, tmpdir } from "node:os";
|
|
@@ -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-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbBa4iV-.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as NativeLeaseSettingsSchema, p as DEFAULT_NATIVE_LEASE_SETTINGS, y as NativeLeaseAdmissionSchema } from "./dist-
|
|
1
|
+
import { b as NativeLeaseSettingsSchema, p as DEFAULT_NATIVE_LEASE_SETTINGS, y as NativeLeaseAdmissionSchema } from "./dist-DttqKvsQ.mjs";
|
|
2
2
|
//#region src/session-decode/native-lease-config.ts
|
|
3
3
|
/**
|
|
4
4
|
* Resolution of the native-frame hold / tile knobs for one decode worker.
|
|
@@ -225,6 +225,8 @@ function isVideoFrameGenOptions(value) {
|
|
|
225
225
|
if (!isRecord(value)) return false;
|
|
226
226
|
if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
|
|
227
227
|
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
228
|
+
if (value.preRollMs !== void 0 && !isFiniteNumber(value.preRollMs)) return false;
|
|
229
|
+
if (value.preRollMaxFrames !== void 0 && !isFiniteNumber(value.preRollMaxFrames)) return false;
|
|
228
230
|
return true;
|
|
229
231
|
}
|
|
230
232
|
/** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
|
|
@@ -246,13 +248,14 @@ function isWorkerRequest(value) {
|
|
|
246
248
|
function isWorkerReply(value) {
|
|
247
249
|
if (!isRecord(value)) return false;
|
|
248
250
|
switch (value.kind) {
|
|
249
|
-
case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format);
|
|
251
|
+
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");
|
|
250
252
|
case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
|
|
251
253
|
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");
|
|
252
254
|
case "nativeCropMiss": return isFiniteNumber(value.requestId) && (value.reason === void 0 || typeof value.reason === "string");
|
|
253
255
|
case "ended": return true;
|
|
254
256
|
case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
|
|
255
257
|
case "log": return isWorkerLogLevel(value.level) && typeof value.message === "string";
|
|
258
|
+
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);
|
|
256
259
|
case "dialTiming": return isFiniteNumber(value.hwContextMs) && isFiniteNumber(value.demuxerOpenMs) && isFiniteNumber(value.decoderCreateMs) && isFiniteNumber(value.firstDecodeMs) && typeof value.streamInfoProbed === "boolean";
|
|
257
260
|
default: return false;
|
|
258
261
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-DoHcimdu.js");
|
|
2
2
|
//#region src/session-decode/native-lease-config.ts
|
|
3
3
|
/**
|
|
4
4
|
* Resolution of the native-frame hold / tile knobs for one decode worker.
|
|
@@ -225,6 +225,8 @@ function isVideoFrameGenOptions(value) {
|
|
|
225
225
|
if (!isRecord(value)) return false;
|
|
226
226
|
if (value.fps !== void 0 && !isFiniteNumber(value.fps)) return false;
|
|
227
227
|
if (value.format !== void 0 && !isWorkerFrameFormat(value.format)) return false;
|
|
228
|
+
if (value.preRollMs !== void 0 && !isFiniteNumber(value.preRollMs)) return false;
|
|
229
|
+
if (value.preRollMaxFrames !== void 0 && !isFiniteNumber(value.preRollMaxFrames)) return false;
|
|
228
230
|
return true;
|
|
229
231
|
}
|
|
230
232
|
/** Discriminated-union guard for {@link WorkerRequest} — no `as` casts. */
|
|
@@ -246,13 +248,14 @@ function isWorkerRequest(value) {
|
|
|
246
248
|
function isWorkerReply(value) {
|
|
247
249
|
if (!isRecord(value)) return false;
|
|
248
250
|
switch (value.kind) {
|
|
249
|
-
case "frame": return isFiniteNumber(value.frameId) && isFiniteNumber(value.timestamp) && isFiniteNumber(value.width) && isFiniteNumber(value.height) && isWorkerFrameFormat(value.format);
|
|
251
|
+
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");
|
|
250
252
|
case "buffer": return isFiniteNumber(value.frameId) && value.bytes instanceof Uint8Array;
|
|
251
253
|
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");
|
|
252
254
|
case "nativeCropMiss": return isFiniteNumber(value.requestId) && (value.reason === void 0 || typeof value.reason === "string");
|
|
253
255
|
case "ended": return true;
|
|
254
256
|
case "error": return typeof value.message === "string" && (value.frameId === void 0 || isFiniteNumber(value.frameId));
|
|
255
257
|
case "log": return isWorkerLogLevel(value.level) && typeof value.message === "string";
|
|
258
|
+
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);
|
|
256
259
|
case "dialTiming": return isFiniteNumber(value.hwContextMs) && isFiniteNumber(value.demuxerOpenMs) && isFiniteNumber(value.decoderCreateMs) && isFiniteNumber(value.firstDecodeMs) && typeof value.streamInfoProbed === "boolean";
|
|
257
260
|
default: return false;
|
|
258
261
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.214",
|
|
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",
|