@camstack/addon-pipeline 1.2.106 → 1.2.115
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/{addon-utils-BEzVJ0xg.js → addon-utils-C0ht2KPO.js} +51 -15
- package/dist/{addon-utils-A2S9D7pu.mjs → addon-utils-CgBCF-mE.mjs} +50 -14
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +3 -3
- package/dist/detection-pipeline/index.js +198 -38
- package/dist/detection-pipeline/index.mjs +197 -37
- package/dist/{dist-BSeR_hVv.js → dist-BqIbYW1A.js} +290 -17
- package/dist/{dist-lITI1Bn5.mjs → dist-DGQkkWc6.mjs} +273 -18
- package/dist/{event-loop-stall-monitor-0VvjTRDP.mjs → event-loop-stall-monitor-D5jK5v4Z.mjs} +223 -2
- package/dist/{event-loop-stall-monitor-BNsp9k8r.js → event-loop-stall-monitor-ylMGpz48.js} +234 -1
- package/dist/{lazy-sharp-CBohM8D8.js → lazy-sharp-cOsy7l_P.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +915 -107
- package/dist/pipeline-runner/index.mjs +914 -106
- package/dist/{process-memory-C33u3tQz.js → process-memory-45juuHpN.js} +1 -1
- package/dist/{process-memory-CEKVKfwg.mjs → process-memory-BtT7WfbJ.mjs} +1 -1
- package/dist/recorder/index.js +187 -47
- package/dist/recorder/index.mjs +187 -47
- package/dist/session-decode/decode-worker-child.js +30 -10
- package/dist/session-decode/decode-worker-child.mjs +29 -9
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-w2XLD3uI.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C_SFFNGT.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B1LDFHY9.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-DsIi4G5Q.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAMyzCWw.mjs} +1 -1
- package/dist/stream-broker/{hostInit-B-h24cDy.mjs → hostInit-CG28NvlV.mjs} +3 -3
- package/dist/stream-broker/index.js +279 -198
- package/dist/stream-broker/index.mjs +279 -198
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-CiKfal2P.js → worker-protocol-CIrqGbpG.js} +6 -3
- package/dist/{worker-protocol-BDiL8TBm.mjs → worker-protocol-DyocTIx_.mjs} +6 -3
- package/package.json +1 -1
- package/python/inference_pool.py +4 -1
- package/python/test_inference_pool_preprocess.py +87 -16
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DvZWLWk1.mjs +0 -26
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { At as nodePin, Bt as literal, C as RATE_CONTROL_TIGHT, Ct as DeviceFeature, Dt as isEvent, E as RingBuffer, Ft as _enum, Gt as union, H as egressTranscodeSharingKey, Ht as object, It as array, J as maskUrlCredentials, Kt as EventCategory, Lt as boolean, M as batteryCapability, Mt as parseProfileBrokerId, N as cameraStreamsCapability, O as addonWidgetsSourceCapability, Ot as makeProfileBrokerId, P as createHwAccelCache, R as deriveBatteryPresence, Rt as discriminatedUnion, S as RATE_CONTROL_RELAXED, St as CAM_PROFILE_ORDER, Tt as createEvent, U as egressTransportFromRequest, Ut as record, Vt as number, Wt as string, _t as Fmp4BoxSplitter, bt as isSoftwareDecode, ft as streamBrokerCapability, gt as AUDIO_PRESETS, h as EncodeProfileSchema, ht as errMsg, i as BatteryStatusSchema, kt as makeSourceBrokerId, mt as webrtcSessionCapability, st as resolveEgressDecodeHwAccel, vt as buildFfmpegArgs$1, wt as DeviceType, xt as BaseAddon, yt as invocationFromEncodeProfile } from "../dist-DGQkkWc6.mjs";
|
|
3
3
|
import { t as parseRestreamPath } from "../restream-intent-B4BXZra7.mjs";
|
|
4
4
|
import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
|
|
5
5
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DMolfhsP.mjs";
|
|
@@ -23626,7 +23626,7 @@ var MonotonicClock = class {
|
|
|
23626
23626
|
this.rate = rate;
|
|
23627
23627
|
}
|
|
23628
23628
|
};
|
|
23629
|
-
var SCRUB_FRAGMENT_CACHE_MAX_BYTES =
|
|
23629
|
+
var SCRUB_FRAGMENT_CACHE_MAX_BYTES = 48 * 1024 * 1024;
|
|
23630
23630
|
var SCRUB_FRAGMENT_CACHE_OTHER_MAX_BYTES = 2 * 1024 * 1024;
|
|
23631
23631
|
/** Demuxed bytes an entry holds — what the byte bound actually counts. */
|
|
23632
23632
|
function fragmentBytes(frag) {
|
|
@@ -24048,14 +24048,22 @@ var SCRUB_WHOLE_SEGMENT_MAX_BYTES = 15e5;
|
|
|
24048
24048
|
* drag starts on.
|
|
24049
24049
|
*
|
|
24050
24050
|
* Asymmetric because the gesture is: a drag that begins at the live edge — or
|
|
24051
|
-
* at a notification's moment — is looking for what ALREADY happened.
|
|
24052
|
-
* against
|
|
24053
|
-
*
|
|
24051
|
+
* at a notification's moment — is looking for what ALREADY happened. 15 min
|
|
24052
|
+
* back against 5 min forward fills the 48 MB window where the finger will
|
|
24053
|
+
* actually move, read sequentially in the background while the first ticks
|
|
24054
|
+
* still pay their own demand-read.
|
|
24054
24055
|
*/
|
|
24055
|
-
var SCRUB_WARM_BACK_MS =
|
|
24056
|
-
var SCRUB_WARM_FWD_MS =
|
|
24057
|
-
/**
|
|
24058
|
-
var
|
|
24056
|
+
var SCRUB_WARM_BACK_MS = 15 * 6e4;
|
|
24057
|
+
var SCRUB_WARM_FWD_MS = 5 * 6e4;
|
|
24058
|
+
/** Symmetric span used after a jump: scrub around the landing, both ways. */
|
|
24059
|
+
var SCRUB_WARM_AROUND_MS = 15 * 6e4;
|
|
24060
|
+
/**
|
|
24061
|
+
* Re-centre the fill when a tick lands this far from the window we are filling.
|
|
24062
|
+
* A slow drag stays inside this; a jump to 8am does not.
|
|
24063
|
+
*/
|
|
24064
|
+
var SCRUB_RECENTER_MS = 12e4;
|
|
24065
|
+
/** Hard bound on warm reads, derived from the session RAM budget. */
|
|
24066
|
+
var SCRUB_WARM_MAX_SEGMENTS = Math.max(1, Math.floor(SCRUB_FRAGMENT_CACHE_MAX_BYTES / 2e5));
|
|
24059
24067
|
/**
|
|
24060
24068
|
* Median recent read time at or above which the feeder STOPS speculating.
|
|
24061
24069
|
*
|
|
@@ -24309,6 +24317,16 @@ var RecordedFeeder = class {
|
|
|
24309
24317
|
scrubReadMsWindow = [];
|
|
24310
24318
|
/** Warm-up runs once per drag, from the playhead the finger went down on. */
|
|
24311
24319
|
scrubWarmStarted = false;
|
|
24320
|
+
/** Centre of the fill currently in flight (or last completed). */
|
|
24321
|
+
scrubWarmCentreMs = null;
|
|
24322
|
+
/** Bumped to abort a fill without aborting the drag tick (`scrubToken`). */
|
|
24323
|
+
scrubWarmGen = 0;
|
|
24324
|
+
/** Tick demand-read in flight: the fill waits so the finger wins the spindle. */
|
|
24325
|
+
scrubTickReadInFlight = false;
|
|
24326
|
+
/** One fill walker per drag — a fling retargets it, it does not spawn another. */
|
|
24327
|
+
scrubFillLoopActive = false;
|
|
24328
|
+
scrubWarmBackMs = SCRUB_WARM_BACK_MS;
|
|
24329
|
+
scrubWarmFwdMs = SCRUB_WARM_FWD_MS;
|
|
24312
24330
|
constructor(deps) {
|
|
24313
24331
|
this.deps = deps;
|
|
24314
24332
|
this.audioClock = new MonotonicClock(deps.audioClockRateHz ?? 8e3);
|
|
@@ -24430,6 +24448,7 @@ var RecordedFeeder = class {
|
|
|
24430
24448
|
let prefixFed = false;
|
|
24431
24449
|
let gopFirst = false;
|
|
24432
24450
|
let stateAnnounced = false;
|
|
24451
|
+
let reusedWhole = null;
|
|
24433
24452
|
if (this.deps.readGop) {
|
|
24434
24453
|
const cached = this.scrubCache.covering(this.deps.profile, landMs);
|
|
24435
24454
|
let fragment;
|
|
@@ -24439,6 +24458,10 @@ var RecordedFeeder = class {
|
|
|
24439
24458
|
if (fragmentLoad === null) return;
|
|
24440
24459
|
fragment = fragmentLoad.seg;
|
|
24441
24460
|
this.scrubCache.put(this.deps.profile, fragment);
|
|
24461
|
+
if (fragmentLoad.wholeSegmentRead && fragmentLoad.rawBytes) reusedWhole = {
|
|
24462
|
+
video: fragment.aus,
|
|
24463
|
+
bytes: fragmentLoad.rawBytes
|
|
24464
|
+
};
|
|
24442
24465
|
}
|
|
24443
24466
|
if (this.disposed || token !== this.seekToken) return;
|
|
24444
24467
|
const fragmentLoc = {
|
|
@@ -24456,14 +24479,23 @@ var RecordedFeeder = class {
|
|
|
24456
24479
|
this.startPositionReports();
|
|
24457
24480
|
stateAnnounced = true;
|
|
24458
24481
|
}
|
|
24459
|
-
|
|
24460
|
-
|
|
24461
|
-
this.
|
|
24462
|
-
|
|
24463
|
-
|
|
24464
|
-
|
|
24465
|
-
|
|
24466
|
-
|
|
24482
|
+
let loaded;
|
|
24483
|
+
if (reusedWhole) {
|
|
24484
|
+
const audio = this.rate !== 0 && this.deps.demuxAudio && this.resolveAudioPush() !== void 0 ? await this.withTimeout(this.deps.demuxAudio(reusedWhole.bytes)).catch(() => []) : [];
|
|
24485
|
+
loaded = {
|
|
24486
|
+
video: reusedWhole.video,
|
|
24487
|
+
audio
|
|
24488
|
+
};
|
|
24489
|
+
} else {
|
|
24490
|
+
const fullLoad = this.loadAus(loc.startMs, this.rate !== 0, this.deps.readGop ? void 0 : (video) => {
|
|
24491
|
+
if (this.disposed || token !== this.seekToken) return;
|
|
24492
|
+
this.feedPrefix(loc, video, landMs);
|
|
24493
|
+
prefixFed = true;
|
|
24494
|
+
firstFrameMs = performance.now() - seekStartedAt;
|
|
24495
|
+
});
|
|
24496
|
+
fullLoad.catch(() => {});
|
|
24497
|
+
loaded = await fullLoad;
|
|
24498
|
+
}
|
|
24467
24499
|
loadMs = performance.now() - loadStartedAt;
|
|
24468
24500
|
if (this.disposed || token !== this.seekToken || loaded === null) return;
|
|
24469
24501
|
if (!prefixFed) this.feedPrefix(loc, loaded.video, landMs);
|
|
@@ -24625,38 +24657,92 @@ var RecordedFeeder = class {
|
|
|
24625
24657
|
* back and {@link SCRUB_WARM_FWD_MS} forward from the entry playhead, one
|
|
24626
24658
|
* segment at a time, backward first.
|
|
24627
24659
|
*
|
|
24628
|
-
*
|
|
24629
|
-
*
|
|
24660
|
+
* Fire-and-forget, never on the tick's path — a tick that arrives against a
|
|
24661
|
+
* cold cache pays its own read exactly as before. A later jump more than
|
|
24662
|
+
* {@link SCRUB_RECENTER_MS} from this centre aborts the fill (via
|
|
24663
|
+
* {@link scrubWarmGen}, not the drag token) and starts a new window.
|
|
24664
|
+
*
|
|
24630
24665
|
* It walks segment to segment through `locate` rather than assuming a segment
|
|
24631
24666
|
* length, so a recorder configured with a different `segmentSeconds` warms the
|
|
24632
24667
|
* same SPAN rather than the same count.
|
|
24633
24668
|
*/
|
|
24634
|
-
startScrubWarm(centreMs,
|
|
24635
|
-
if (this.scrubWarmStarted) return;
|
|
24669
|
+
startScrubWarm(centreMs, dragGen, opts) {
|
|
24670
|
+
if (this.scrubWarmStarted && !opts?.force) return;
|
|
24636
24671
|
this.scrubWarmStarted = true;
|
|
24637
|
-
this.
|
|
24672
|
+
this.scrubWarmGen += 1;
|
|
24673
|
+
this.scrubWarmCentreMs = centreMs;
|
|
24674
|
+
this.scrubWarmBackMs = opts?.backMs ?? SCRUB_WARM_BACK_MS;
|
|
24675
|
+
this.scrubWarmFwdMs = opts?.fwdMs ?? SCRUB_WARM_FWD_MS;
|
|
24676
|
+
this.deps.onLog?.("scrub-feeder:warm-start", {
|
|
24677
|
+
centreMs,
|
|
24678
|
+
fillGen: this.scrubWarmGen
|
|
24679
|
+
});
|
|
24680
|
+
if (this.scrubFillLoopActive) return;
|
|
24681
|
+
this.scrubFillLoopActive = true;
|
|
24682
|
+
this.runScrubFillLoop(dragGen);
|
|
24683
|
+
}
|
|
24684
|
+
/**
|
|
24685
|
+
* A single sequential walk. Recentre bumps {@link scrubWarmGen} and this loop
|
|
24686
|
+
* restarts around the new cursor — it must never `void` a second walker, or a
|
|
24687
|
+
* coarse fling stacks one USB walk per tick and play/scrub starve (1.2.113:
|
|
24688
|
+
* 16 walkers, seek `loadMs=16652`, drags with `ticks=0`).
|
|
24689
|
+
*/
|
|
24690
|
+
async runScrubFillLoop(dragGen) {
|
|
24691
|
+
let lastWorked = -1;
|
|
24692
|
+
try {
|
|
24693
|
+
while (!this.disposed && this.scrubbing && dragGen === this.scrubToken) {
|
|
24694
|
+
const gen = this.scrubWarmGen;
|
|
24695
|
+
const centre = this.scrubWarmCentreMs;
|
|
24696
|
+
if (centre === null) return;
|
|
24697
|
+
await this.warmScrubWindow(centre, dragGen, gen, this.scrubWarmBackMs, this.scrubWarmFwdMs);
|
|
24698
|
+
lastWorked = gen;
|
|
24699
|
+
if (gen === this.scrubWarmGen) return;
|
|
24700
|
+
}
|
|
24701
|
+
} finally {
|
|
24702
|
+
this.scrubFillLoopActive = false;
|
|
24703
|
+
if (!this.disposed && this.scrubbing && dragGen === this.scrubToken && this.scrubWarmCentreMs !== null && this.scrubWarmGen !== lastWorked) {
|
|
24704
|
+
this.scrubFillLoopActive = true;
|
|
24705
|
+
this.runScrubFillLoop(dragGen);
|
|
24706
|
+
}
|
|
24707
|
+
}
|
|
24708
|
+
}
|
|
24709
|
+
/**
|
|
24710
|
+
* Abort the current fill and start a new ±15 min window around `centreMs`.
|
|
24711
|
+
* Does not bump `scrubToken` — the in-flight tick must still land.
|
|
24712
|
+
*/
|
|
24713
|
+
recenterScrubWarm(centreMs) {
|
|
24714
|
+
if (this.deps.profile !== SCRUB_WHOLE_SEGMENT_PROFILE) return;
|
|
24715
|
+
this.startScrubWarm(centreMs, this.scrubToken, {
|
|
24716
|
+
force: true,
|
|
24717
|
+
backMs: SCRUB_WARM_AROUND_MS,
|
|
24718
|
+
fwdMs: SCRUB_WARM_AROUND_MS
|
|
24719
|
+
});
|
|
24720
|
+
}
|
|
24721
|
+
maybeRecenterScrubWarm(targetMs) {
|
|
24722
|
+
if (this.deps.profile !== SCRUB_WHOLE_SEGMENT_PROFILE) return;
|
|
24723
|
+
const centre = this.scrubWarmCentreMs;
|
|
24724
|
+
if (centre === null) return;
|
|
24725
|
+
if (Math.abs(targetMs - centre) <= SCRUB_RECENTER_MS) return;
|
|
24726
|
+
this.recenterScrubWarm(targetMs);
|
|
24638
24727
|
}
|
|
24639
|
-
async warmScrubWindow(centreMs,
|
|
24640
|
-
const spent = await this.warmScrubDirection(centreMs, -1,
|
|
24641
|
-
await this.warmScrubDirection(centreMs, 1,
|
|
24728
|
+
async warmScrubWindow(centreMs, dragGen, fillGen, backMs, fwdMs) {
|
|
24729
|
+
const spent = await this.warmScrubDirection(centreMs, -1, backMs, 0, dragGen, fillGen);
|
|
24730
|
+
await this.warmScrubDirection(centreMs, 1, fwdMs, spent, dragGen, fillGen);
|
|
24642
24731
|
}
|
|
24643
24732
|
/**
|
|
24644
24733
|
* Walk `dir` from `fromMs` until `spanMs` of timeline is warm (or the shared
|
|
24645
24734
|
* bound is hit), reading each segment that is not already cached. Returns the
|
|
24646
24735
|
* running total of segments read, so the two directions share one budget.
|
|
24647
24736
|
*/
|
|
24648
|
-
async warmScrubDirection(fromMs, dir, spanMs, spent,
|
|
24737
|
+
async warmScrubDirection(fromMs, dir, spanMs, spent, dragGen, fillGen) {
|
|
24649
24738
|
let read = spent;
|
|
24650
24739
|
let probeMs = fromMs;
|
|
24651
24740
|
for (let i = 0; i < SCRUB_WARM_MAX_SEGMENTS; i++) {
|
|
24652
|
-
if (this.disposed || !this.scrubbing ||
|
|
24741
|
+
if (this.disposed || !this.scrubbing || dragGen !== this.scrubToken) return read;
|
|
24742
|
+
if (fillGen !== this.scrubWarmGen) return read;
|
|
24653
24743
|
if (read >= SCRUB_WARM_MAX_SEGMENTS) return read;
|
|
24654
24744
|
if (Math.abs(probeMs - fromMs) > spanMs) return read;
|
|
24655
|
-
|
|
24656
|
-
this.scrubStats?.recordPrefetchSuppressed();
|
|
24657
|
-
return read;
|
|
24658
|
-
}
|
|
24659
|
-
const seg = await this.warmOneScrubSegment(probeMs, gen);
|
|
24745
|
+
const seg = await this.warmOneScrubSegment(probeMs, dragGen, fillGen);
|
|
24660
24746
|
if (seg === null) return read;
|
|
24661
24747
|
if (seg.fetched) read += 1;
|
|
24662
24748
|
probeMs = dir === -1 ? seg.startMs - 1 : seg.startMs + seg.durMs + 1;
|
|
@@ -24670,7 +24756,7 @@ var RecordedFeeder = class {
|
|
|
24670
24756
|
* so the walk can step to its neighbour, or null when the walk must stop (a
|
|
24671
24757
|
* gap, a failure, or teardown).
|
|
24672
24758
|
*/
|
|
24673
|
-
async warmOneScrubSegment(probeMs,
|
|
24759
|
+
async warmOneScrubSegment(probeMs, dragGen, fillGen) {
|
|
24674
24760
|
try {
|
|
24675
24761
|
const warm = this.scrubCache.covering(this.deps.profile, probeMs);
|
|
24676
24762
|
if (warm) return {
|
|
@@ -24678,16 +24764,20 @@ var RecordedFeeder = class {
|
|
|
24678
24764
|
durMs: warm.durMs,
|
|
24679
24765
|
fetched: false
|
|
24680
24766
|
};
|
|
24767
|
+
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24681
24768
|
const loc = await this.withTimeout(this.deps.locate({
|
|
24682
24769
|
deviceId: this.deps.deviceId,
|
|
24683
24770
|
profile: this.deps.profile,
|
|
24684
24771
|
epochMs: probeMs
|
|
24685
24772
|
}));
|
|
24686
|
-
if (this.disposed || !this.scrubbing ||
|
|
24773
|
+
if (this.disposed || !this.scrubbing || dragGen !== this.scrubToken) return null;
|
|
24774
|
+
if (fillGen !== this.scrubWarmGen) return null;
|
|
24687
24775
|
if (loc.kind !== "segment") return null;
|
|
24776
|
+
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24688
24777
|
const load = await this.loadScrubFragment(loc, probeMs);
|
|
24689
24778
|
if (load !== null) this.scrubStats?.recordPrefetchRead(load.readBytes);
|
|
24690
|
-
if (this.disposed || !this.scrubbing ||
|
|
24779
|
+
if (this.disposed || !this.scrubbing || dragGen !== this.scrubToken || load === null) return null;
|
|
24780
|
+
if (fillGen !== this.scrubWarmGen) return null;
|
|
24691
24781
|
this.scrubCache.put(this.deps.profile, load.seg);
|
|
24692
24782
|
this.scrubPrefetched.add(load.seg.startMs);
|
|
24693
24783
|
return {
|
|
@@ -24699,6 +24789,14 @@ var RecordedFeeder = class {
|
|
|
24699
24789
|
return null;
|
|
24700
24790
|
}
|
|
24701
24791
|
}
|
|
24792
|
+
/** Yield the spindle to an in-flight finger tick. Returns false if this fill died. */
|
|
24793
|
+
async waitForTickReadIdle(fillGen) {
|
|
24794
|
+
while (this.scrubTickReadInFlight) {
|
|
24795
|
+
await new Promise((r) => setTimeout(r, 4));
|
|
24796
|
+
if (this.disposed || !this.scrubbing || fillGen !== this.scrubWarmGen) return false;
|
|
24797
|
+
}
|
|
24798
|
+
return true;
|
|
24799
|
+
}
|
|
24702
24800
|
/**
|
|
24703
24801
|
* EXPERIMENTAL feeder-scrub: register a drag target. Records the newest target
|
|
24704
24802
|
* and, if no tick cycle is live, starts a leading-edge tick. A burst of moves
|
|
@@ -24862,63 +24960,69 @@ var RecordedFeeder = class {
|
|
|
24862
24960
|
}
|
|
24863
24961
|
}
|
|
24864
24962
|
if (!seg) {
|
|
24865
|
-
|
|
24866
|
-
|
|
24867
|
-
|
|
24868
|
-
|
|
24869
|
-
|
|
24870
|
-
}));
|
|
24871
|
-
locateCalls += 1;
|
|
24872
|
-
locateMs = performance.now() - locateStartMs;
|
|
24873
|
-
this.maybeLogSlowLocate(target, locateMs);
|
|
24874
|
-
if (this.disposed || !this.scrubbing || gen !== this.scrubToken) return;
|
|
24875
|
-
if (loc.kind === "gap") {
|
|
24876
|
-
const decision = resolveSnapTarget(loc, target);
|
|
24877
|
-
if (decision.kind !== "snap") {
|
|
24878
|
-
if (!this.scrubNoFootageLogged) {
|
|
24879
|
-
this.scrubNoFootageLogged = true;
|
|
24880
|
-
if (decision.kind === "too-far") this.deps.onLog?.("scrub-feeder:snap-too-far", {
|
|
24881
|
-
epochMs: target,
|
|
24882
|
-
nearestMs: decision.snapMs,
|
|
24883
|
-
distanceMs: decision.distanceMs
|
|
24884
|
-
});
|
|
24885
|
-
else this.deps.onLog?.("scrub-feeder:no-footage", { epochMs: target });
|
|
24886
|
-
}
|
|
24887
|
-
return;
|
|
24888
|
-
}
|
|
24889
|
-
const snapStartMs = performance.now();
|
|
24890
|
-
const snapped = await this.withTimeout(this.deps.locate({
|
|
24963
|
+
this.scrubTickReadInFlight = true;
|
|
24964
|
+
try {
|
|
24965
|
+
this.maybeRecenterScrubWarm(target);
|
|
24966
|
+
const locateStartMs = performance.now();
|
|
24967
|
+
let loc = await this.withTimeout(this.deps.locate({
|
|
24891
24968
|
deviceId: this.deps.deviceId,
|
|
24892
24969
|
profile: this.deps.profile,
|
|
24893
|
-
epochMs:
|
|
24970
|
+
epochMs: target
|
|
24894
24971
|
}));
|
|
24895
24972
|
locateCalls += 1;
|
|
24896
|
-
locateMs =
|
|
24973
|
+
locateMs = performance.now() - locateStartMs;
|
|
24974
|
+
this.maybeLogSlowLocate(target, locateMs);
|
|
24897
24975
|
if (this.disposed || !this.scrubbing || gen !== this.scrubToken) return;
|
|
24898
|
-
if (
|
|
24899
|
-
|
|
24900
|
-
|
|
24901
|
-
this.
|
|
24976
|
+
if (loc.kind === "gap") {
|
|
24977
|
+
const decision = resolveSnapTarget(loc, target);
|
|
24978
|
+
if (decision.kind !== "snap") {
|
|
24979
|
+
if (!this.scrubNoFootageLogged) {
|
|
24980
|
+
this.scrubNoFootageLogged = true;
|
|
24981
|
+
if (decision.kind === "too-far") this.deps.onLog?.("scrub-feeder:snap-too-far", {
|
|
24982
|
+
epochMs: target,
|
|
24983
|
+
nearestMs: decision.snapMs,
|
|
24984
|
+
distanceMs: decision.distanceMs
|
|
24985
|
+
});
|
|
24986
|
+
else this.deps.onLog?.("scrub-feeder:no-footage", { epochMs: target });
|
|
24987
|
+
}
|
|
24988
|
+
return;
|
|
24902
24989
|
}
|
|
24903
|
-
|
|
24990
|
+
const snapStartMs = performance.now();
|
|
24991
|
+
const snapped = await this.withTimeout(this.deps.locate({
|
|
24992
|
+
deviceId: this.deps.deviceId,
|
|
24993
|
+
profile: this.deps.profile,
|
|
24994
|
+
epochMs: decision.snapMs
|
|
24995
|
+
}));
|
|
24996
|
+
locateCalls += 1;
|
|
24997
|
+
locateMs = (locateMs ?? 0) + (performance.now() - snapStartMs);
|
|
24998
|
+
if (this.disposed || !this.scrubbing || gen !== this.scrubToken) return;
|
|
24999
|
+
if (snapped.kind !== "segment") {
|
|
25000
|
+
if (!this.scrubNoFootageLogged) {
|
|
25001
|
+
this.scrubNoFootageLogged = true;
|
|
25002
|
+
this.deps.onLog?.("scrub-feeder:no-footage", { epochMs: target });
|
|
25003
|
+
}
|
|
25004
|
+
return;
|
|
25005
|
+
}
|
|
25006
|
+
this.deps.onLog?.("scrub-feeder:snapped", {
|
|
25007
|
+
requestedMs: target,
|
|
25008
|
+
snappedMs: decision.snapMs
|
|
25009
|
+
});
|
|
25010
|
+
loc = snapped;
|
|
25011
|
+
effTarget = decision.snapMs;
|
|
24904
25012
|
}
|
|
24905
|
-
this.deps.
|
|
24906
|
-
|
|
24907
|
-
|
|
24908
|
-
|
|
24909
|
-
|
|
24910
|
-
|
|
24911
|
-
|
|
24912
|
-
|
|
24913
|
-
|
|
24914
|
-
|
|
24915
|
-
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
readBytes = load.readBytes;
|
|
24919
|
-
demuxMs = load.demuxMs;
|
|
24920
|
-
wholeSegmentRead = load.wholeSegmentRead;
|
|
24921
|
-
this.scrubCache.put(this.deps.profile, seg);
|
|
25013
|
+
seg = this.scrubCache.covering(this.deps.profile, effTarget);
|
|
25014
|
+
if (!seg) {
|
|
25015
|
+
const load = await this.loadScrubFragment(loc, effTarget);
|
|
25016
|
+
if (this.disposed || !this.scrubbing || gen !== this.scrubToken || load === null) return;
|
|
25017
|
+
seg = load.seg;
|
|
25018
|
+
readMs = load.readMs;
|
|
25019
|
+
readBytes = load.readBytes;
|
|
25020
|
+
demuxMs = load.demuxMs;
|
|
25021
|
+
wholeSegmentRead = load.wholeSegmentRead;
|
|
25022
|
+
this.scrubCache.put(this.deps.profile, seg);
|
|
25023
|
+
}
|
|
25024
|
+
} finally {
|
|
25025
|
+
this.scrubTickReadInFlight = false;
|
|
24922
25026
|
}
|
|
24923
25027
|
}
|
|
24924
25028
|
this.scrubSegment = seg;
|
|
@@ -25127,7 +25231,8 @@ var RecordedFeeder = class {
|
|
|
25127
25231
|
readMs: readEndMs - readStartMs,
|
|
25128
25232
|
readBytes: bytes.length,
|
|
25129
25233
|
demuxMs: performance.now() - readEndMs,
|
|
25130
|
-
wholeSegmentRead: true
|
|
25234
|
+
wholeSegmentRead: true,
|
|
25235
|
+
rawBytes: bytes
|
|
25131
25236
|
};
|
|
25132
25237
|
}
|
|
25133
25238
|
/**
|
|
@@ -25339,6 +25444,7 @@ var RecordedFeeder = class {
|
|
|
25339
25444
|
this.scrubStats?.recordPrefetchSuppressed();
|
|
25340
25445
|
return;
|
|
25341
25446
|
}
|
|
25447
|
+
if (this.scrubFillLoopActive) return;
|
|
25342
25448
|
const aheadMs = Math.max(Math.abs(stepMs), seg.durMs + 1);
|
|
25343
25449
|
const horizons = Math.abs(stepMs) > seg.durMs ? SCRUB_PREFETCH_MAX_INFLIGHT : 1;
|
|
25344
25450
|
for (let horizon = 1; horizon <= horizons; horizon++) {
|
|
@@ -25347,6 +25453,7 @@ var RecordedFeeder = class {
|
|
|
25347
25453
|
if (probeMs < 0) continue;
|
|
25348
25454
|
if (this.scrubPrefetchProbes.has(probeMs)) continue;
|
|
25349
25455
|
if (this.scrubCache.covering(this.deps.profile, probeMs)) continue;
|
|
25456
|
+
if (this.scrubWarmCentreMs !== null && Math.abs(probeMs - this.scrubWarmCentreMs) > SCRUB_WARM_AROUND_MS) continue;
|
|
25350
25457
|
this.scrubPrefetch(probeMs, gen);
|
|
25351
25458
|
}
|
|
25352
25459
|
}
|
|
@@ -25969,7 +26076,7 @@ var TimelineSession = class {
|
|
|
25969
26076
|
*
|
|
25970
26077
|
* 1. The scrub is pinned to `low` by the client, and every D163 pre-cache
|
|
25971
26078
|
* behaviour is gated on `deps.profile === 'low'` by exact string equality
|
|
25972
|
-
* — the
|
|
26079
|
+
* — the ±15 min entry warm, the whole-segment read, and the 48 MB
|
|
25973
26080
|
* fragment budget (2 MB for anything else). `profileSearchOrder` puts
|
|
25974
26081
|
* `high` BEFORE `low`, so a momentary `low` gap used to hand the drag a
|
|
25975
26082
|
* `high` feeder and silently turn all three off. The client cannot repair
|
|
@@ -33702,6 +33809,70 @@ function preferLiveAdaptiveCandidates(candidates) {
|
|
|
33702
33809
|
return natives.length > 0 ? natives : [...candidates];
|
|
33703
33810
|
}
|
|
33704
33811
|
//#endregion
|
|
33812
|
+
//#region src/stream-broker/webrtc/adaptive-tier.ts
|
|
33813
|
+
var RANK = {
|
|
33814
|
+
low: 1,
|
|
33815
|
+
mid: 2,
|
|
33816
|
+
high: 3
|
|
33817
|
+
};
|
|
33818
|
+
var QUALITY_FIRST = [
|
|
33819
|
+
"high",
|
|
33820
|
+
"mid",
|
|
33821
|
+
"low"
|
|
33822
|
+
];
|
|
33823
|
+
var SMALLEST_FIRST = [
|
|
33824
|
+
"low",
|
|
33825
|
+
"mid",
|
|
33826
|
+
"high"
|
|
33827
|
+
];
|
|
33828
|
+
function isTier(value) {
|
|
33829
|
+
return value === "high" || value === "mid" || value === "low";
|
|
33830
|
+
}
|
|
33831
|
+
function firstAvailable(order, available) {
|
|
33832
|
+
const set = new Set(available);
|
|
33833
|
+
return order.find((t) => set.has(t));
|
|
33834
|
+
}
|
|
33835
|
+
function capTo(tier, available) {
|
|
33836
|
+
const max = RANK[tier];
|
|
33837
|
+
return firstAvailable(QUALITY_FIRST.filter((t) => RANK[t] <= max), available);
|
|
33838
|
+
}
|
|
33839
|
+
function pickAdaptiveTier(input) {
|
|
33840
|
+
const available = input.available.filter((t) => t === "high" || t === "mid" || t === "low");
|
|
33841
|
+
const fallback = firstAvailable(QUALITY_FIRST, available) ?? "high";
|
|
33842
|
+
if (isTier(input.prefersTier)) {
|
|
33843
|
+
const exact = available.includes(input.prefersTier) ? input.prefersTier : void 0;
|
|
33844
|
+
if (exact) return {
|
|
33845
|
+
tier: exact,
|
|
33846
|
+
reason: "prefers-tier"
|
|
33847
|
+
};
|
|
33848
|
+
return {
|
|
33849
|
+
tier: firstAvailable(SMALLEST_FIRST, available) ?? fallback,
|
|
33850
|
+
reason: "prefers-tier-fallback"
|
|
33851
|
+
};
|
|
33852
|
+
}
|
|
33853
|
+
let tier = fallback;
|
|
33854
|
+
let reason = "default-high";
|
|
33855
|
+
if (input.downlinkMbps !== void 0 && Number.isFinite(input.downlinkMbps) && input.downlinkMbps >= 0) {
|
|
33856
|
+
tier = capTo(input.downlinkMbps >= 8 ? "high" : input.downlinkMbps >= 2.5 ? "mid" : "low", available) ?? fallback;
|
|
33857
|
+
reason = "downlink";
|
|
33858
|
+
}
|
|
33859
|
+
const width = input.viewportWidth;
|
|
33860
|
+
const height = input.viewportHeight;
|
|
33861
|
+
if (width !== void 0 && height !== void 0 && width > 0 && height > 0) {
|
|
33862
|
+
if (Math.max(width, height) < 240) {
|
|
33863
|
+
const tiled = capTo("low", available) ?? fallback;
|
|
33864
|
+
if (RANK[tiled] < RANK[tier]) return {
|
|
33865
|
+
tier: tiled,
|
|
33866
|
+
reason: "viewport-ceiling"
|
|
33867
|
+
};
|
|
33868
|
+
}
|
|
33869
|
+
}
|
|
33870
|
+
return {
|
|
33871
|
+
tier,
|
|
33872
|
+
reason
|
|
33873
|
+
};
|
|
33874
|
+
}
|
|
33875
|
+
//#endregion
|
|
33705
33876
|
//#region src/stream-broker/webrtc/broker-webrtc-server.ts
|
|
33706
33877
|
/**
|
|
33707
33878
|
* Broker-integrated WebRTC server.
|
|
@@ -33817,36 +33988,12 @@ function deriveH264ProfileLevelId(sdpParameterSets, preBuffer) {
|
|
|
33817
33988
|
if (profileLevelId) return profileLevelId;
|
|
33818
33989
|
}
|
|
33819
33990
|
}
|
|
33820
|
-
|
|
33821
|
-
|
|
33822
|
-
|
|
33823
|
-
|
|
33824
|
-
|
|
33825
|
-
|
|
33826
|
-
mid: {
|
|
33827
|
-
pixels: 1280 * 720,
|
|
33828
|
-
bitrateKbps: 1200
|
|
33829
|
-
},
|
|
33830
|
-
low: {
|
|
33831
|
-
pixels: 640 * 360,
|
|
33832
|
-
bitrateKbps: 400
|
|
33833
|
-
}
|
|
33834
|
-
};
|
|
33835
|
-
var TIER_PREFERENCE = [
|
|
33836
|
-
"high",
|
|
33837
|
-
"mid",
|
|
33838
|
-
"low"
|
|
33839
|
-
];
|
|
33840
|
-
/** Smallest-keyframe-first tier order. Used as the fallback when an explicit
|
|
33841
|
-
* `prefersTier` (e.g. the hub's `remote`-viewer bias to `low`) has no exact
|
|
33842
|
-
* assigned slot: prefer the SMALLEST available tier rather than the
|
|
33843
|
-
* quality-first default, so a remote-biased request never silently lands back
|
|
33844
|
-
* on `high`. */
|
|
33845
|
-
var SMALLEST_FIRST_TIER_PREFERENCE = [
|
|
33846
|
-
"low",
|
|
33847
|
-
"mid",
|
|
33848
|
-
"high"
|
|
33849
|
-
];
|
|
33991
|
+
function adaptivePickLogLine(reason) {
|
|
33992
|
+
if (reason === "prefers-tier") return "Adaptive source selected (prefersTier exact)";
|
|
33993
|
+
if (reason === "prefers-tier-fallback") return "Adaptive source selected (prefersTier fallback → smallest available)";
|
|
33994
|
+
if (reason === "default-high") return "Adaptive source selected (no client hints — tier preference)";
|
|
33995
|
+
return "Adaptive source selected";
|
|
33996
|
+
}
|
|
33850
33997
|
/** Push-queue overflow purges are aggregated and reported at most this often. */
|
|
33851
33998
|
var FRAME_DROP_LOG_INTERVAL_MS = 3e4;
|
|
33852
33999
|
/**
|
|
@@ -33887,20 +34034,6 @@ function reduceProfileForTranscodeDown(base) {
|
|
|
33887
34034
|
}
|
|
33888
34035
|
};
|
|
33889
34036
|
}
|
|
33890
|
-
function scoreBroker(tier, broker, hints) {
|
|
33891
|
-
const stats = broker.getStats();
|
|
33892
|
-
const fallback = tier ? LABEL_DEFAULTS[tier] : void 0;
|
|
33893
|
-
const bitrateKbps = stats.bitrateKbps > 0 ? stats.bitrateKbps : fallback?.bitrateKbps ?? 2e3;
|
|
33894
|
-
const srcPixels = fallback?.pixels ?? 1920 * 1080;
|
|
33895
|
-
const targetPixels = (hints.viewportWidth ?? 1920) * (hints.viewportHeight ?? 1080);
|
|
33896
|
-
const pixelDistance = Math.abs(srcPixels - targetPixels) / 1e6;
|
|
33897
|
-
let bandwidthPenalty = 0;
|
|
33898
|
-
if (hints.downlinkMbps && hints.downlinkMbps > 0 && bitrateKbps > 0) {
|
|
33899
|
-
const availableKbps = hints.downlinkMbps * 1e3 * .6;
|
|
33900
|
-
if (bitrateKbps > availableKbps) bandwidthPenalty = (bitrateKbps - availableKbps) / availableKbps * 100;
|
|
33901
|
-
}
|
|
33902
|
-
return pixelDistance + bandwidthPenalty;
|
|
33903
|
-
}
|
|
33904
34037
|
var BrokerWebrtcServer = class {
|
|
33905
34038
|
logger;
|
|
33906
34039
|
getIceServersFn;
|
|
@@ -35032,78 +35165,26 @@ var BrokerWebrtcServer = class {
|
|
|
35032
35165
|
if (pool.length === 1) return pool[0].brokerId;
|
|
35033
35166
|
const adaptiveDeviceIdTag = Number(deviceKey.split("/")[0]);
|
|
35034
35167
|
const deviceIdTag = Number.isFinite(adaptiveDeviceIdTag) ? { tags: { deviceId: adaptiveDeviceIdTag } } : {};
|
|
35035
|
-
|
|
35036
|
-
|
|
35037
|
-
|
|
35038
|
-
|
|
35039
|
-
|
|
35040
|
-
|
|
35041
|
-
|
|
35042
|
-
|
|
35043
|
-
|
|
35044
|
-
|
|
35045
|
-
}
|
|
35046
|
-
});
|
|
35047
|
-
return explicit.brokerId;
|
|
35048
|
-
}
|
|
35049
|
-
for (const tier of SMALLEST_FIRST_TIER_PREFERENCE) {
|
|
35050
|
-
const match = pool.find((c) => c.tier === tier);
|
|
35051
|
-
if (match) {
|
|
35052
|
-
this.logger.info("Adaptive source selected (prefersTier fallback → smallest available)", {
|
|
35053
|
-
...deviceIdTag,
|
|
35054
|
-
meta: {
|
|
35055
|
-
deviceKey,
|
|
35056
|
-
selected: match.brokerId,
|
|
35057
|
-
tier: match.tier,
|
|
35058
|
-
prefersTier: hints.prefersTier
|
|
35059
|
-
}
|
|
35060
|
-
});
|
|
35061
|
-
return match.brokerId;
|
|
35062
|
-
}
|
|
35063
|
-
}
|
|
35064
|
-
}
|
|
35065
|
-
if (!(hints.viewportWidth !== void 0 || hints.viewportHeight !== void 0 || hints.downlinkMbps !== void 0)) {
|
|
35066
|
-
for (const tier of TIER_PREFERENCE) {
|
|
35067
|
-
const match = pool.find((c) => c.tier === tier);
|
|
35068
|
-
if (match) {
|
|
35069
|
-
this.logger.info("Adaptive source selected (no client hints — tier preference)", {
|
|
35070
|
-
...deviceIdTag,
|
|
35071
|
-
meta: {
|
|
35072
|
-
deviceKey,
|
|
35073
|
-
selected: match.brokerId,
|
|
35074
|
-
tier: match.tier
|
|
35075
|
-
}
|
|
35076
|
-
});
|
|
35077
|
-
return match.brokerId;
|
|
35078
|
-
}
|
|
35079
|
-
}
|
|
35080
|
-
return pool[0].brokerId;
|
|
35081
|
-
}
|
|
35082
|
-
let bestId = pool[0].brokerId;
|
|
35083
|
-
let bestTier = pool[0].tier;
|
|
35084
|
-
let bestScore = Infinity;
|
|
35085
|
-
for (const c of pool) {
|
|
35086
|
-
const score = scoreBroker(c.tier, c.broker, hints);
|
|
35087
|
-
const cRank = c.tier ? TIER_PREFERENCE.indexOf(c.tier) : TIER_PREFERENCE.length;
|
|
35088
|
-
const bestRank = bestTier ? TIER_PREFERENCE.indexOf(bestTier) : TIER_PREFERENCE.length;
|
|
35089
|
-
if (score < bestScore || score === bestScore && cRank < bestRank) {
|
|
35090
|
-
bestId = c.brokerId;
|
|
35091
|
-
bestTier = c.tier;
|
|
35092
|
-
bestScore = score;
|
|
35093
|
-
}
|
|
35094
|
-
}
|
|
35095
|
-
const adaptiveDeviceId = Number(deviceKey.split("/")[0]);
|
|
35096
|
-
this.logger.debug("Adaptive source selected", {
|
|
35097
|
-
...Number.isFinite(adaptiveDeviceId) ? { tags: { deviceId: adaptiveDeviceId } } : {},
|
|
35168
|
+
const pick = pickAdaptiveTier({
|
|
35169
|
+
available: pool.map((c) => c.tier),
|
|
35170
|
+
prefersTier: hints.prefersTier,
|
|
35171
|
+
downlinkMbps: hints.downlinkMbps,
|
|
35172
|
+
viewportWidth: hints.viewportWidth,
|
|
35173
|
+
viewportHeight: hints.viewportHeight
|
|
35174
|
+
});
|
|
35175
|
+
const selected = pool.find((c) => c.tier === pick.tier) ?? pool[0];
|
|
35176
|
+
this.logger.info(adaptivePickLogLine(pick.reason), {
|
|
35177
|
+
...deviceIdTag,
|
|
35098
35178
|
meta: {
|
|
35099
35179
|
deviceKey,
|
|
35100
|
-
selected:
|
|
35101
|
-
tier:
|
|
35102
|
-
|
|
35180
|
+
selected: selected.brokerId,
|
|
35181
|
+
tier: selected.tier,
|
|
35182
|
+
reason: pick.reason,
|
|
35183
|
+
prefersTier: hints.prefersTier,
|
|
35103
35184
|
hints
|
|
35104
35185
|
}
|
|
35105
35186
|
});
|
|
35106
|
-
return
|
|
35187
|
+
return selected.brokerId;
|
|
35107
35188
|
}
|
|
35108
35189
|
async resolveIceServers() {
|
|
35109
35190
|
if (this.getIceServersFn) try {
|
|
@@ -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-C_SFFNGT.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|