@camstack/addon-pipeline 1.2.122 → 1.2.124
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/recorder/index.js +1201 -880
- package/dist/recorder/index.mjs +1201 -880
- package/dist/stream-broker/index.js +83 -12
- package/dist/stream-broker/index.mjs +83 -12
- package/package.json +7 -12
|
@@ -24097,6 +24097,19 @@ var SCRUB_RECENTER_MS = 12e4;
|
|
|
24097
24097
|
/** Hard bound on warm reads, derived from the session RAM budget. */
|
|
24098
24098
|
var SCRUB_WARM_MAX_SEGMENTS = Math.max(1, Math.floor(SCRUB_FRAGMENT_CACHE_MAX_BYTES / 2e5));
|
|
24099
24099
|
/**
|
|
24100
|
+
* Pause between two IDLE warm reads — the fill that continues AFTER a seek or
|
|
24101
|
+
* a scrub commit, while no finger is down.
|
|
24102
|
+
*
|
|
24103
|
+
* Idle warm exists because the cold/warm ratio on the recordings spindle is
|
|
24104
|
+
* 19× (531 ms vs 28 ms, measured 2026-08-25) and the operator's think-time on
|
|
24105
|
+
* a landed frame is the one window where the spindle is otherwise free. But
|
|
24106
|
+
* that same spindle carries every camera's writes and the export/event reads:
|
|
24107
|
+
* an UNPACED background walk would be one more saturating reader. One segment
|
|
24108
|
+
* read per gap keeps the fill under ~1 read/s worst case — the window fills in
|
|
24109
|
+
* a couple of minutes and the drag that follows lands on RAM.
|
|
24110
|
+
*/
|
|
24111
|
+
var IDLE_WARM_READ_GAP_MS = 250;
|
|
24112
|
+
/**
|
|
24100
24113
|
* Median recent read time at or above which the feeder STOPS speculating.
|
|
24101
24114
|
*
|
|
24102
24115
|
* Speculation is a bet that spare I/O exists. When reads are this slow there is
|
|
@@ -24363,6 +24376,13 @@ var RecordedFeeder = class {
|
|
|
24363
24376
|
scrubReadMsWindow = [];
|
|
24364
24377
|
/** Warm-up runs once per drag, from the playhead the finger went down on. */
|
|
24365
24378
|
scrubWarmStarted = false;
|
|
24379
|
+
/**
|
|
24380
|
+
* True while the fill window belongs to IDLE mode — started by a seek (a
|
|
24381
|
+
* timeline tap or a scrub commit) rather than by a drag. An idle fill paces
|
|
24382
|
+
* itself ({@link IDLE_WARM_READ_GAP_MS}) and yields to any in-flight seek;
|
|
24383
|
+
* a drag's own warm (`beginScrub`) always takes the window over.
|
|
24384
|
+
*/
|
|
24385
|
+
idleWarm = false;
|
|
24366
24386
|
/** Centre of the fill currently in flight (or last completed). */
|
|
24367
24387
|
scrubWarmCentreMs = null;
|
|
24368
24388
|
/** Bumped to abort a fill without aborting the drag tick (`scrubToken`). */
|
|
@@ -24607,6 +24627,7 @@ var RecordedFeeder = class {
|
|
|
24607
24627
|
bytes: loaded.bytes,
|
|
24608
24628
|
loadAusMs: Math.round(loadAusMs)
|
|
24609
24629
|
});
|
|
24630
|
+
this.startIdleWarm(this.cursorMs);
|
|
24610
24631
|
return;
|
|
24611
24632
|
}
|
|
24612
24633
|
if (!stateAnnounced) {
|
|
@@ -24643,6 +24664,7 @@ var RecordedFeeder = class {
|
|
|
24643
24664
|
},
|
|
24644
24665
|
resumeAfterPtsMs
|
|
24645
24666
|
});
|
|
24667
|
+
this.startIdleWarm(this.cursorMs);
|
|
24646
24668
|
} catch (err) {
|
|
24647
24669
|
if (!this.disposed && token === this.seekToken) {
|
|
24648
24670
|
this.stopPositionReports();
|
|
@@ -24756,7 +24778,36 @@ var RecordedFeeder = class {
|
|
|
24756
24778
|
this.stopPositionReports();
|
|
24757
24779
|
this.scrubWarmStarted = false;
|
|
24758
24780
|
this.scrubReadMsWindow = [];
|
|
24759
|
-
if (this.cursorMs > 0 && this.deps.profile === SCRUB_WHOLE_SEGMENT_PROFILE)
|
|
24781
|
+
if (this.cursorMs > 0 && this.deps.profile === SCRUB_WHOLE_SEGMENT_PROFILE) {
|
|
24782
|
+
this.idleWarm = false;
|
|
24783
|
+
this.startScrubWarm(this.cursorMs, this.scrubToken, { force: true });
|
|
24784
|
+
}
|
|
24785
|
+
}
|
|
24786
|
+
/**
|
|
24787
|
+
* Start (or recentre) the IDLE fill: called after every completed seek —
|
|
24788
|
+
* a timeline tap or a scrub commit — with the landed cursor. The operator is
|
|
24789
|
+
* now LOOKING at that frame; the next gesture almost always starts near it,
|
|
24790
|
+
* and this is the one moment the spindle is not serving the finger. Bounds:
|
|
24791
|
+
* the same session cache budget as the drag warm (48 MB LRU — no new store),
|
|
24792
|
+
* the same {@link SCRUB_WARM_MAX_SEGMENTS} per-window cap, plus the idle
|
|
24793
|
+
* pacing gap. `low`-only for the same reason the drag warm is: a `high`
|
|
24794
|
+
* window would be hundreds of MB of reads for a profile no drag reads.
|
|
24795
|
+
*/
|
|
24796
|
+
startIdleWarm(centreMs) {
|
|
24797
|
+
if (this.disposed || this.scrubbing) return;
|
|
24798
|
+
if (centreMs <= 0 || this.deps.profile !== SCRUB_WHOLE_SEGMENT_PROFILE) return;
|
|
24799
|
+
this.idleWarm = true;
|
|
24800
|
+
this.startScrubWarm(centreMs, this.scrubToken, { force: true });
|
|
24801
|
+
}
|
|
24802
|
+
/**
|
|
24803
|
+
* Whether the fill loop generation `dragGen` may keep working. During a drag
|
|
24804
|
+
* only that drag's generation runs (a commit bumps `scrubToken` and the old
|
|
24805
|
+
* walker exits); outside a drag the loop runs exactly while idle warm is on.
|
|
24806
|
+
*/
|
|
24807
|
+
warmLoopAlive(dragGen) {
|
|
24808
|
+
if (this.disposed) return false;
|
|
24809
|
+
if (this.scrubbing) return dragGen === this.scrubToken;
|
|
24810
|
+
return this.idleWarm;
|
|
24760
24811
|
}
|
|
24761
24812
|
/**
|
|
24762
24813
|
* Warm the window the drag is about to move through: {@link SCRUB_WARM_BACK_MS}
|
|
@@ -24796,19 +24847,29 @@ var RecordedFeeder = class {
|
|
|
24796
24847
|
async runScrubFillLoop(dragGen) {
|
|
24797
24848
|
let lastWorked = -1;
|
|
24798
24849
|
try {
|
|
24799
|
-
while (
|
|
24850
|
+
while (this.warmLoopAlive(dragGen)) {
|
|
24800
24851
|
const gen = this.scrubWarmGen;
|
|
24801
24852
|
const centre = this.scrubWarmCentreMs;
|
|
24802
24853
|
if (centre === null) return;
|
|
24803
|
-
|
|
24854
|
+
const startedAt = performance.now();
|
|
24855
|
+
const fetched = await this.warmScrubWindow(centre, dragGen, gen, this.scrubWarmBackMs, this.scrubWarmFwdMs);
|
|
24804
24856
|
lastWorked = gen;
|
|
24805
|
-
if (gen === this.scrubWarmGen)
|
|
24857
|
+
if (gen === this.scrubWarmGen) {
|
|
24858
|
+
this.deps.onLog?.("scrub-feeder:warm-done", {
|
|
24859
|
+
fillGen: gen,
|
|
24860
|
+
idle: this.idleWarm && !this.scrubbing,
|
|
24861
|
+
fetched,
|
|
24862
|
+
ms: Math.round(performance.now() - startedAt)
|
|
24863
|
+
});
|
|
24864
|
+
if (!this.scrubbing) this.idleWarm = false;
|
|
24865
|
+
return;
|
|
24866
|
+
}
|
|
24806
24867
|
}
|
|
24807
24868
|
} finally {
|
|
24808
24869
|
this.scrubFillLoopActive = false;
|
|
24809
|
-
if (!this.disposed && this.scrubbing
|
|
24870
|
+
if (!this.disposed && (this.scrubbing || this.idleWarm) && this.scrubWarmCentreMs !== null && this.scrubWarmGen !== lastWorked) {
|
|
24810
24871
|
this.scrubFillLoopActive = true;
|
|
24811
|
-
this.runScrubFillLoop(
|
|
24872
|
+
this.runScrubFillLoop(this.scrubToken);
|
|
24812
24873
|
}
|
|
24813
24874
|
}
|
|
24814
24875
|
}
|
|
@@ -24833,7 +24894,7 @@ var RecordedFeeder = class {
|
|
|
24833
24894
|
}
|
|
24834
24895
|
async warmScrubWindow(centreMs, dragGen, fillGen, backMs, fwdMs) {
|
|
24835
24896
|
const spent = await this.warmScrubDirection(centreMs, -1, backMs, 0, dragGen, fillGen);
|
|
24836
|
-
|
|
24897
|
+
return this.warmScrubDirection(centreMs, 1, fwdMs, spent, dragGen, fillGen);
|
|
24837
24898
|
}
|
|
24838
24899
|
/**
|
|
24839
24900
|
* Walk `dir` from `fromMs` until `spanMs` of timeline is warm (or the shared
|
|
@@ -24844,7 +24905,7 @@ var RecordedFeeder = class {
|
|
|
24844
24905
|
let read = spent;
|
|
24845
24906
|
let probeMs = fromMs;
|
|
24846
24907
|
for (let i = 0; i < SCRUB_WARM_MAX_SEGMENTS; i++) {
|
|
24847
|
-
if (
|
|
24908
|
+
if (!this.warmLoopAlive(dragGen)) return read;
|
|
24848
24909
|
if (fillGen !== this.scrubWarmGen) return read;
|
|
24849
24910
|
if (read >= SCRUB_WARM_MAX_SEGMENTS) return read;
|
|
24850
24911
|
if (Math.abs(probeMs - fromMs) > spanMs) return read;
|
|
@@ -24870,19 +24931,27 @@ var RecordedFeeder = class {
|
|
|
24870
24931
|
durMs: warm.durMs,
|
|
24871
24932
|
fetched: false
|
|
24872
24933
|
};
|
|
24934
|
+
if (this.idleWarm && !this.scrubbing) {
|
|
24935
|
+
await new Promise((r) => setTimeout(r, this.deps.idleWarmGapMs ?? IDLE_WARM_READ_GAP_MS));
|
|
24936
|
+
while (this.inFlightSeekEpochMs !== null) {
|
|
24937
|
+
if (!this.warmLoopAlive(dragGen) || fillGen !== this.scrubWarmGen) return null;
|
|
24938
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
24939
|
+
}
|
|
24940
|
+
if (!this.warmLoopAlive(dragGen) || fillGen !== this.scrubWarmGen) return null;
|
|
24941
|
+
}
|
|
24873
24942
|
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24874
24943
|
const loc = await this.withTimeout(this.deps.locate({
|
|
24875
24944
|
deviceId: this.deps.deviceId,
|
|
24876
24945
|
profile: this.deps.profile,
|
|
24877
24946
|
epochMs: probeMs
|
|
24878
24947
|
}));
|
|
24879
|
-
if (
|
|
24948
|
+
if (!this.warmLoopAlive(dragGen)) return null;
|
|
24880
24949
|
if (fillGen !== this.scrubWarmGen) return null;
|
|
24881
24950
|
if (loc.kind !== "segment") return null;
|
|
24882
24951
|
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24883
24952
|
const load = await this.loadScrubFragment(loc, probeMs);
|
|
24884
|
-
if (load !== null) this.scrubStats?.recordPrefetchRead(load.readBytes);
|
|
24885
|
-
if (
|
|
24953
|
+
if (load !== null && this.scrubbing) this.scrubStats?.recordPrefetchRead(load.readBytes);
|
|
24954
|
+
if (!this.warmLoopAlive(dragGen) || load === null) return null;
|
|
24886
24955
|
if (fillGen !== this.scrubWarmGen) return null;
|
|
24887
24956
|
this.scrubCache.put(this.deps.profile, load.seg);
|
|
24888
24957
|
this.scrubPrefetched.add(load.seg.startMs);
|
|
@@ -24899,7 +24968,8 @@ var RecordedFeeder = class {
|
|
|
24899
24968
|
async waitForTickReadIdle(fillGen) {
|
|
24900
24969
|
while (this.scrubTickReadInFlight) {
|
|
24901
24970
|
await new Promise((r) => setTimeout(r, 4));
|
|
24902
|
-
if (this.disposed ||
|
|
24971
|
+
if (this.disposed || fillGen !== this.scrubWarmGen) return false;
|
|
24972
|
+
if (!this.scrubbing && !this.idleWarm) return false;
|
|
24903
24973
|
}
|
|
24904
24974
|
return true;
|
|
24905
24975
|
}
|
|
@@ -26085,6 +26155,7 @@ var RecordedFeeder = class {
|
|
|
26085
26155
|
this.seekToken++;
|
|
26086
26156
|
this.advanceToken++;
|
|
26087
26157
|
this.scrubbing = false;
|
|
26158
|
+
this.idleWarm = false;
|
|
26088
26159
|
this.scrubToken++;
|
|
26089
26160
|
this.scrubSegment = null;
|
|
26090
26161
|
this.scrubPrevTarget = null;
|
|
@@ -24092,6 +24092,19 @@ var SCRUB_RECENTER_MS = 12e4;
|
|
|
24092
24092
|
/** Hard bound on warm reads, derived from the session RAM budget. */
|
|
24093
24093
|
var SCRUB_WARM_MAX_SEGMENTS = Math.max(1, Math.floor(SCRUB_FRAGMENT_CACHE_MAX_BYTES / 2e5));
|
|
24094
24094
|
/**
|
|
24095
|
+
* Pause between two IDLE warm reads — the fill that continues AFTER a seek or
|
|
24096
|
+
* a scrub commit, while no finger is down.
|
|
24097
|
+
*
|
|
24098
|
+
* Idle warm exists because the cold/warm ratio on the recordings spindle is
|
|
24099
|
+
* 19× (531 ms vs 28 ms, measured 2026-08-25) and the operator's think-time on
|
|
24100
|
+
* a landed frame is the one window where the spindle is otherwise free. But
|
|
24101
|
+
* that same spindle carries every camera's writes and the export/event reads:
|
|
24102
|
+
* an UNPACED background walk would be one more saturating reader. One segment
|
|
24103
|
+
* read per gap keeps the fill under ~1 read/s worst case — the window fills in
|
|
24104
|
+
* a couple of minutes and the drag that follows lands on RAM.
|
|
24105
|
+
*/
|
|
24106
|
+
var IDLE_WARM_READ_GAP_MS = 250;
|
|
24107
|
+
/**
|
|
24095
24108
|
* Median recent read time at or above which the feeder STOPS speculating.
|
|
24096
24109
|
*
|
|
24097
24110
|
* Speculation is a bet that spare I/O exists. When reads are this slow there is
|
|
@@ -24358,6 +24371,13 @@ var RecordedFeeder = class {
|
|
|
24358
24371
|
scrubReadMsWindow = [];
|
|
24359
24372
|
/** Warm-up runs once per drag, from the playhead the finger went down on. */
|
|
24360
24373
|
scrubWarmStarted = false;
|
|
24374
|
+
/**
|
|
24375
|
+
* True while the fill window belongs to IDLE mode — started by a seek (a
|
|
24376
|
+
* timeline tap or a scrub commit) rather than by a drag. An idle fill paces
|
|
24377
|
+
* itself ({@link IDLE_WARM_READ_GAP_MS}) and yields to any in-flight seek;
|
|
24378
|
+
* a drag's own warm (`beginScrub`) always takes the window over.
|
|
24379
|
+
*/
|
|
24380
|
+
idleWarm = false;
|
|
24361
24381
|
/** Centre of the fill currently in flight (or last completed). */
|
|
24362
24382
|
scrubWarmCentreMs = null;
|
|
24363
24383
|
/** Bumped to abort a fill without aborting the drag tick (`scrubToken`). */
|
|
@@ -24602,6 +24622,7 @@ var RecordedFeeder = class {
|
|
|
24602
24622
|
bytes: loaded.bytes,
|
|
24603
24623
|
loadAusMs: Math.round(loadAusMs)
|
|
24604
24624
|
});
|
|
24625
|
+
this.startIdleWarm(this.cursorMs);
|
|
24605
24626
|
return;
|
|
24606
24627
|
}
|
|
24607
24628
|
if (!stateAnnounced) {
|
|
@@ -24638,6 +24659,7 @@ var RecordedFeeder = class {
|
|
|
24638
24659
|
},
|
|
24639
24660
|
resumeAfterPtsMs
|
|
24640
24661
|
});
|
|
24662
|
+
this.startIdleWarm(this.cursorMs);
|
|
24641
24663
|
} catch (err) {
|
|
24642
24664
|
if (!this.disposed && token === this.seekToken) {
|
|
24643
24665
|
this.stopPositionReports();
|
|
@@ -24751,7 +24773,36 @@ var RecordedFeeder = class {
|
|
|
24751
24773
|
this.stopPositionReports();
|
|
24752
24774
|
this.scrubWarmStarted = false;
|
|
24753
24775
|
this.scrubReadMsWindow = [];
|
|
24754
|
-
if (this.cursorMs > 0 && this.deps.profile === SCRUB_WHOLE_SEGMENT_PROFILE)
|
|
24776
|
+
if (this.cursorMs > 0 && this.deps.profile === SCRUB_WHOLE_SEGMENT_PROFILE) {
|
|
24777
|
+
this.idleWarm = false;
|
|
24778
|
+
this.startScrubWarm(this.cursorMs, this.scrubToken, { force: true });
|
|
24779
|
+
}
|
|
24780
|
+
}
|
|
24781
|
+
/**
|
|
24782
|
+
* Start (or recentre) the IDLE fill: called after every completed seek —
|
|
24783
|
+
* a timeline tap or a scrub commit — with the landed cursor. The operator is
|
|
24784
|
+
* now LOOKING at that frame; the next gesture almost always starts near it,
|
|
24785
|
+
* and this is the one moment the spindle is not serving the finger. Bounds:
|
|
24786
|
+
* the same session cache budget as the drag warm (48 MB LRU — no new store),
|
|
24787
|
+
* the same {@link SCRUB_WARM_MAX_SEGMENTS} per-window cap, plus the idle
|
|
24788
|
+
* pacing gap. `low`-only for the same reason the drag warm is: a `high`
|
|
24789
|
+
* window would be hundreds of MB of reads for a profile no drag reads.
|
|
24790
|
+
*/
|
|
24791
|
+
startIdleWarm(centreMs) {
|
|
24792
|
+
if (this.disposed || this.scrubbing) return;
|
|
24793
|
+
if (centreMs <= 0 || this.deps.profile !== SCRUB_WHOLE_SEGMENT_PROFILE) return;
|
|
24794
|
+
this.idleWarm = true;
|
|
24795
|
+
this.startScrubWarm(centreMs, this.scrubToken, { force: true });
|
|
24796
|
+
}
|
|
24797
|
+
/**
|
|
24798
|
+
* Whether the fill loop generation `dragGen` may keep working. During a drag
|
|
24799
|
+
* only that drag's generation runs (a commit bumps `scrubToken` and the old
|
|
24800
|
+
* walker exits); outside a drag the loop runs exactly while idle warm is on.
|
|
24801
|
+
*/
|
|
24802
|
+
warmLoopAlive(dragGen) {
|
|
24803
|
+
if (this.disposed) return false;
|
|
24804
|
+
if (this.scrubbing) return dragGen === this.scrubToken;
|
|
24805
|
+
return this.idleWarm;
|
|
24755
24806
|
}
|
|
24756
24807
|
/**
|
|
24757
24808
|
* Warm the window the drag is about to move through: {@link SCRUB_WARM_BACK_MS}
|
|
@@ -24791,19 +24842,29 @@ var RecordedFeeder = class {
|
|
|
24791
24842
|
async runScrubFillLoop(dragGen) {
|
|
24792
24843
|
let lastWorked = -1;
|
|
24793
24844
|
try {
|
|
24794
|
-
while (
|
|
24845
|
+
while (this.warmLoopAlive(dragGen)) {
|
|
24795
24846
|
const gen = this.scrubWarmGen;
|
|
24796
24847
|
const centre = this.scrubWarmCentreMs;
|
|
24797
24848
|
if (centre === null) return;
|
|
24798
|
-
|
|
24849
|
+
const startedAt = performance.now();
|
|
24850
|
+
const fetched = await this.warmScrubWindow(centre, dragGen, gen, this.scrubWarmBackMs, this.scrubWarmFwdMs);
|
|
24799
24851
|
lastWorked = gen;
|
|
24800
|
-
if (gen === this.scrubWarmGen)
|
|
24852
|
+
if (gen === this.scrubWarmGen) {
|
|
24853
|
+
this.deps.onLog?.("scrub-feeder:warm-done", {
|
|
24854
|
+
fillGen: gen,
|
|
24855
|
+
idle: this.idleWarm && !this.scrubbing,
|
|
24856
|
+
fetched,
|
|
24857
|
+
ms: Math.round(performance.now() - startedAt)
|
|
24858
|
+
});
|
|
24859
|
+
if (!this.scrubbing) this.idleWarm = false;
|
|
24860
|
+
return;
|
|
24861
|
+
}
|
|
24801
24862
|
}
|
|
24802
24863
|
} finally {
|
|
24803
24864
|
this.scrubFillLoopActive = false;
|
|
24804
|
-
if (!this.disposed && this.scrubbing
|
|
24865
|
+
if (!this.disposed && (this.scrubbing || this.idleWarm) && this.scrubWarmCentreMs !== null && this.scrubWarmGen !== lastWorked) {
|
|
24805
24866
|
this.scrubFillLoopActive = true;
|
|
24806
|
-
this.runScrubFillLoop(
|
|
24867
|
+
this.runScrubFillLoop(this.scrubToken);
|
|
24807
24868
|
}
|
|
24808
24869
|
}
|
|
24809
24870
|
}
|
|
@@ -24828,7 +24889,7 @@ var RecordedFeeder = class {
|
|
|
24828
24889
|
}
|
|
24829
24890
|
async warmScrubWindow(centreMs, dragGen, fillGen, backMs, fwdMs) {
|
|
24830
24891
|
const spent = await this.warmScrubDirection(centreMs, -1, backMs, 0, dragGen, fillGen);
|
|
24831
|
-
|
|
24892
|
+
return this.warmScrubDirection(centreMs, 1, fwdMs, spent, dragGen, fillGen);
|
|
24832
24893
|
}
|
|
24833
24894
|
/**
|
|
24834
24895
|
* Walk `dir` from `fromMs` until `spanMs` of timeline is warm (or the shared
|
|
@@ -24839,7 +24900,7 @@ var RecordedFeeder = class {
|
|
|
24839
24900
|
let read = spent;
|
|
24840
24901
|
let probeMs = fromMs;
|
|
24841
24902
|
for (let i = 0; i < SCRUB_WARM_MAX_SEGMENTS; i++) {
|
|
24842
|
-
if (
|
|
24903
|
+
if (!this.warmLoopAlive(dragGen)) return read;
|
|
24843
24904
|
if (fillGen !== this.scrubWarmGen) return read;
|
|
24844
24905
|
if (read >= SCRUB_WARM_MAX_SEGMENTS) return read;
|
|
24845
24906
|
if (Math.abs(probeMs - fromMs) > spanMs) return read;
|
|
@@ -24865,19 +24926,27 @@ var RecordedFeeder = class {
|
|
|
24865
24926
|
durMs: warm.durMs,
|
|
24866
24927
|
fetched: false
|
|
24867
24928
|
};
|
|
24929
|
+
if (this.idleWarm && !this.scrubbing) {
|
|
24930
|
+
await new Promise((r) => setTimeout(r, this.deps.idleWarmGapMs ?? IDLE_WARM_READ_GAP_MS));
|
|
24931
|
+
while (this.inFlightSeekEpochMs !== null) {
|
|
24932
|
+
if (!this.warmLoopAlive(dragGen) || fillGen !== this.scrubWarmGen) return null;
|
|
24933
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
24934
|
+
}
|
|
24935
|
+
if (!this.warmLoopAlive(dragGen) || fillGen !== this.scrubWarmGen) return null;
|
|
24936
|
+
}
|
|
24868
24937
|
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24869
24938
|
const loc = await this.withTimeout(this.deps.locate({
|
|
24870
24939
|
deviceId: this.deps.deviceId,
|
|
24871
24940
|
profile: this.deps.profile,
|
|
24872
24941
|
epochMs: probeMs
|
|
24873
24942
|
}));
|
|
24874
|
-
if (
|
|
24943
|
+
if (!this.warmLoopAlive(dragGen)) return null;
|
|
24875
24944
|
if (fillGen !== this.scrubWarmGen) return null;
|
|
24876
24945
|
if (loc.kind !== "segment") return null;
|
|
24877
24946
|
if (!await this.waitForTickReadIdle(fillGen)) return null;
|
|
24878
24947
|
const load = await this.loadScrubFragment(loc, probeMs);
|
|
24879
|
-
if (load !== null) this.scrubStats?.recordPrefetchRead(load.readBytes);
|
|
24880
|
-
if (
|
|
24948
|
+
if (load !== null && this.scrubbing) this.scrubStats?.recordPrefetchRead(load.readBytes);
|
|
24949
|
+
if (!this.warmLoopAlive(dragGen) || load === null) return null;
|
|
24881
24950
|
if (fillGen !== this.scrubWarmGen) return null;
|
|
24882
24951
|
this.scrubCache.put(this.deps.profile, load.seg);
|
|
24883
24952
|
this.scrubPrefetched.add(load.seg.startMs);
|
|
@@ -24894,7 +24963,8 @@ var RecordedFeeder = class {
|
|
|
24894
24963
|
async waitForTickReadIdle(fillGen) {
|
|
24895
24964
|
while (this.scrubTickReadInFlight) {
|
|
24896
24965
|
await new Promise((r) => setTimeout(r, 4));
|
|
24897
|
-
if (this.disposed ||
|
|
24966
|
+
if (this.disposed || fillGen !== this.scrubWarmGen) return false;
|
|
24967
|
+
if (!this.scrubbing && !this.idleWarm) return false;
|
|
24898
24968
|
}
|
|
24899
24969
|
return true;
|
|
24900
24970
|
}
|
|
@@ -26080,6 +26150,7 @@ var RecordedFeeder = class {
|
|
|
26080
26150
|
this.seekToken++;
|
|
26081
26151
|
this.advanceToken++;
|
|
26082
26152
|
this.scrubbing = false;
|
|
26153
|
+
this.idleWarm = false;
|
|
26083
26154
|
this.scrubToken++;
|
|
26084
26155
|
this.scrubSegment = null;
|
|
26085
26156
|
this.scrubPrevTarget = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.124",
|
|
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",
|
|
@@ -70,8 +70,7 @@
|
|
|
70
70
|
"execution": {
|
|
71
71
|
"placement": "any-node",
|
|
72
72
|
"heapProfile": "heavy",
|
|
73
|
-
"group": "detection"
|
|
74
|
-
"maxOldSpaceMb": 0
|
|
73
|
+
"group": "detection"
|
|
75
74
|
},
|
|
76
75
|
"capabilities": [
|
|
77
76
|
{
|
|
@@ -104,8 +103,7 @@
|
|
|
104
103
|
"execution": {
|
|
105
104
|
"placement": "any-node",
|
|
106
105
|
"heapProfile": "heavy",
|
|
107
|
-
"group": "detection"
|
|
108
|
-
"maxOldSpaceMb": 0
|
|
106
|
+
"group": "detection"
|
|
109
107
|
},
|
|
110
108
|
"protected": true,
|
|
111
109
|
"icon": "assets/icon.svg",
|
|
@@ -121,8 +119,7 @@
|
|
|
121
119
|
"execution": {
|
|
122
120
|
"placement": "any-node",
|
|
123
121
|
"heapProfile": "heavy",
|
|
124
|
-
"group": "detection"
|
|
125
|
-
"maxOldSpaceMb": 0
|
|
122
|
+
"group": "detection"
|
|
126
123
|
},
|
|
127
124
|
"capabilities": [
|
|
128
125
|
{
|
|
@@ -149,8 +146,7 @@
|
|
|
149
146
|
"entry": "./dist/audio-analyzer/index.js",
|
|
150
147
|
"execution": {
|
|
151
148
|
"placement": "any-node",
|
|
152
|
-
"heapProfile": "heavy"
|
|
153
|
-
"maxOldSpaceMb": 0
|
|
149
|
+
"heapProfile": "heavy"
|
|
154
150
|
},
|
|
155
151
|
"capabilities": [
|
|
156
152
|
{
|
|
@@ -172,8 +168,7 @@
|
|
|
172
168
|
"entry": "./dist/stream-broker/index.js",
|
|
173
169
|
"execution": {
|
|
174
170
|
"placement": "any-node",
|
|
175
|
-
"heapProfile": "heavy"
|
|
176
|
-
"maxOldSpaceMb": 0
|
|
171
|
+
"heapProfile": "heavy"
|
|
177
172
|
},
|
|
178
173
|
"capabilities": [
|
|
179
174
|
{
|
|
@@ -203,7 +198,7 @@
|
|
|
203
198
|
"execution": {
|
|
204
199
|
"placement": "any-node",
|
|
205
200
|
"heapProfile": "heavy",
|
|
206
|
-
"maxOldSpaceMb":
|
|
201
|
+
"maxOldSpaceMb": 3072
|
|
207
202
|
},
|
|
208
203
|
"capabilities": [
|
|
209
204
|
{
|