@camstack/addon-pipeline 1.2.131 → 1.2.133
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-DCQ5AP_v.js → addon-utils-FV2O25id.js} +1 -1
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +5 -5
- package/dist/detection-pipeline/index.mjs +3 -3
- package/dist/{dist-DO0UVhOk.mjs → dist-7Uc6NdFm.mjs} +736 -67
- package/dist/{dist-CqoMNE1e.js → dist-DsnZUYTs.js} +753 -66
- package/dist/{event-loop-stall-monitor-eM7_yYfn.js → event-loop-stall-monitor-D86J6d2c.js} +1 -1
- package/dist/{event-loop-stall-monitor-D0Juf7Dn.mjs → event-loop-stall-monitor-XzDMVU9d.mjs} +1 -1
- package/dist/{lazy-sharp-BGrpS8yi.js → lazy-sharp-UzEUL79V.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 +4 -4
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-Bac3FuG6.mjs → process-memory-Ci90-zF_.mjs} +1 -1
- package/dist/{process-memory-xAPjvSw9.js → process-memory-Co59RQIK.js} +1 -1
- package/dist/recorder/index.js +677 -99
- package/dist/recorder/index.mjs +676 -98
- package/dist/{segment-demux-js-skhsAZx6.mjs → segment-demux-js-0NZ9TXro.mjs} +1 -1
- package/dist/{segment-demux-js-Cu0oDMUL.js → segment-demux-js-DKcc0bxA.js} +1 -1
- package/dist/session-decode/decode-worker-child.js +195 -13
- package/dist/session-decode/decode-worker-child.mjs +194 -12
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DGQkDEYw.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Dqj2f3H7.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CVBnNONy.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-CQKwHIHI.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-77rGxv1J.mjs} +1 -1
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-D7FwwZgj.mjs → hostInit-DnwyBL4I.mjs} +3 -3
- package/dist/stream-broker/index.js +2 -2
- package/dist/stream-broker/index.mjs +2 -2
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-_Ujvj1hl.mjs → worker-protocol-CMDUAqvX.mjs} +12 -6
- package/dist/{worker-protocol-G3HXTY8z.js → worker-protocol-DzRUHIcM.js} +12 -6
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bme2OZQp.mjs +0 -26
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { yt as errMsg } from "./dist-7Uc6NdFm.mjs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region ../../node_modules/mp4box/dist/mp4box.all.js
|
|
5
5
|
var require_mp4box_all = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-DsnZUYTs.js");
|
|
2
2
|
let node_child_process = require("node:child_process");
|
|
3
3
|
//#region ../../node_modules/mp4box/dist/mp4box.all.js
|
|
4
4
|
var require_mp4box_all = /* @__PURE__ */ require_dist.__commonJSMin(((exports) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
3
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
2
|
+
const require_worker_protocol = require("../worker-protocol-DzRUHIcM.js");
|
|
3
|
+
const require_lazy_sharp = require("../lazy-sharp-UzEUL79V.js");
|
|
4
4
|
//#region src/session-decode/color-conversion.ts
|
|
5
5
|
/**
|
|
6
6
|
* Explicit YUV→RGB colorspace/range resolution for the session-decode worker.
|
|
@@ -792,11 +792,24 @@ var NativeTileStore = class {
|
|
|
792
792
|
ttlMs;
|
|
793
793
|
now;
|
|
794
794
|
bytes = 0;
|
|
795
|
+
/**
|
|
796
|
+
* Frames whose tiles were dropped by the BUDGET before their TTL ran out.
|
|
797
|
+
*
|
|
798
|
+
* Counted, not silent: budget eviction is the one path here that destroys
|
|
799
|
+
* retained work, and on the scene store a dropped entry is a key frame that
|
|
800
|
+
* can never be captured again. `0` means the TTL is what bounds this store —
|
|
801
|
+
* anything else is the signal that its budget is the binding limit.
|
|
802
|
+
*/
|
|
803
|
+
evicted = 0;
|
|
795
804
|
constructor(options) {
|
|
796
805
|
this.budgetBytes = options.budgetBytes;
|
|
797
806
|
this.ttlMs = options.ttlMs;
|
|
798
807
|
this.now = options.now ?? Date.now;
|
|
799
808
|
}
|
|
809
|
+
/** Frames dropped by the byte budget rather than by the TTL (tests / metrics). */
|
|
810
|
+
get evictedCount() {
|
|
811
|
+
return this.evicted;
|
|
812
|
+
}
|
|
800
813
|
/** `false` when tiles are disabled (`budgetBytes <= 0`). */
|
|
801
814
|
get enabled() {
|
|
802
815
|
return this.budgetBytes > 0;
|
|
@@ -837,6 +850,7 @@ var NativeTileStore = class {
|
|
|
837
850
|
const oldest = this.entries.keys().next().value;
|
|
838
851
|
if (oldest === void 0 || oldest === frameId) break;
|
|
839
852
|
this.drop(oldest);
|
|
853
|
+
this.evicted += 1;
|
|
840
854
|
}
|
|
841
855
|
return true;
|
|
842
856
|
}
|
|
@@ -1078,6 +1092,107 @@ function resolveRgbCropSource(plane, planeStride, region) {
|
|
|
1078
1092
|
};
|
|
1079
1093
|
}
|
|
1080
1094
|
//#endregion
|
|
1095
|
+
//#region src/session-decode/scene-tile.ts
|
|
1096
|
+
/**
|
|
1097
|
+
* The whole frame, in the same normalized space every other cut uses. Identical
|
|
1098
|
+
* to the bbox `getNativeCrop`'s full-frame callers send, which is the point —
|
|
1099
|
+
* the rectangle asked for and the rectangle retained are the same one.
|
|
1100
|
+
*/
|
|
1101
|
+
var SCENE_TILE_BBOX = {
|
|
1102
|
+
x: 0,
|
|
1103
|
+
y: 0,
|
|
1104
|
+
w: 1,
|
|
1105
|
+
h: 1
|
|
1106
|
+
};
|
|
1107
|
+
/**
|
|
1108
|
+
* Plan one frame's cut.
|
|
1109
|
+
*
|
|
1110
|
+
* The scene rectangle is appended to the SAME cut rather than taken separately,
|
|
1111
|
+
* because `subject-tile-cut.ts` reads every rectangle in one synchronous turn:
|
|
1112
|
+
* a second read after the first cut's encodes have awaited is exactly the
|
|
1113
|
+
* eviction window that once cut subject 1 of each frame and lost the rest.
|
|
1114
|
+
*
|
|
1115
|
+
* A frame that detected NOTHING gets nothing, scene tile included. That
|
|
1116
|
+
* asymmetry is what keeps this tier affordable — the frames a key-frame capture
|
|
1117
|
+
* can ever name are the frames that carried a best detection, and on this
|
|
1118
|
+
* cluster those are ~0.4/s per camera against ~2.1/s delivered.
|
|
1119
|
+
*/
|
|
1120
|
+
function planFrameCuts(subjects, conditions) {
|
|
1121
|
+
if (!conditions.sceneEnabled || !conditions.hasDetections) return {
|
|
1122
|
+
bboxes: subjects,
|
|
1123
|
+
scene: false
|
|
1124
|
+
};
|
|
1125
|
+
return {
|
|
1126
|
+
bboxes: [...subjects, SCENE_TILE_BBOX],
|
|
1127
|
+
scene: true
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Does this region cover the whole frame?
|
|
1132
|
+
*
|
|
1133
|
+
* Judged on the RESOLVED region rather than on the position in the cut list,
|
|
1134
|
+
* because `cutSubjectTiles` drops a rectangle whose read or encode failed and
|
|
1135
|
+
* positional identity does not survive that. Unknown frame dimensions (a freed
|
|
1136
|
+
* node-av frame reports 0×0) are `false` — classifying against those would file
|
|
1137
|
+
* every subject tile as the scene.
|
|
1138
|
+
*/
|
|
1139
|
+
function isSceneRegion(region, frameWidth, frameHeight) {
|
|
1140
|
+
if (frameWidth <= 0 || frameHeight <= 0) return false;
|
|
1141
|
+
return region.left <= 0 && region.top <= 0 && region.width >= frameWidth && region.height >= frameHeight;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Route each cut tile to the store that owns it.
|
|
1145
|
+
*
|
|
1146
|
+
* The FIRST frame-spanning tile is the scene; any later one stays a subject. A
|
|
1147
|
+
* padded subject that already covers the frame genuinely IS the scene, and both
|
|
1148
|
+
* stores are consulted on a serve, so claiming one of them costs no hit and
|
|
1149
|
+
* keeps the two stores from holding the same pixels twice.
|
|
1150
|
+
*/
|
|
1151
|
+
function partitionSceneTile(tiles, frameWidth, frameHeight) {
|
|
1152
|
+
const subjects = [];
|
|
1153
|
+
let scene = null;
|
|
1154
|
+
for (const tile of tiles) {
|
|
1155
|
+
if (scene === null && isSceneRegion(tile.region, frameWidth, frameHeight)) {
|
|
1156
|
+
scene = tile;
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
subjects.push(tile);
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
subjects,
|
|
1163
|
+
scene
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Pick the retained tile that CONTAINS `rect`, subject tiles first.
|
|
1168
|
+
*
|
|
1169
|
+
* The order is the whole of the policy and it is not cosmetic. Both rungs are
|
|
1170
|
+
* native-resolution, so either would be honest — but a subject tile is
|
|
1171
|
+
* ~60-120 KB against a scene tile's ~1.5-2.5 MB, and the extract runs on
|
|
1172
|
+
* whatever it is handed. Asking the subject store first keeps an ordinary
|
|
1173
|
+
* face/plate crop off the 20×-larger raster; the scene tile is consulted only
|
|
1174
|
+
* for the rectangles no subject tile could ever contain, which is exactly the
|
|
1175
|
+
* set that had no rung at all before it existed.
|
|
1176
|
+
*
|
|
1177
|
+
* `null` means no retained rung contains the rectangle — a MISS, never a clamp
|
|
1178
|
+
* into the nearest tile ([D52](../../../../docs/decisions/adr-0052.md)).
|
|
1179
|
+
*/
|
|
1180
|
+
function selectRetainedTile(subjects, scenes, rect) {
|
|
1181
|
+
const fromSubject = selectTileForRect(subjects, rect);
|
|
1182
|
+
if (fromSubject) return {
|
|
1183
|
+
tile: fromSubject.tile,
|
|
1184
|
+
local: fromSubject.local,
|
|
1185
|
+
source: "subject"
|
|
1186
|
+
};
|
|
1187
|
+
const fromScene = selectTileForRect(scenes, rect);
|
|
1188
|
+
if (fromScene) return {
|
|
1189
|
+
tile: fromScene.tile,
|
|
1190
|
+
local: fromScene.local,
|
|
1191
|
+
source: "scene"
|
|
1192
|
+
};
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
//#endregion
|
|
1081
1196
|
//#region src/session-decode/subject-tile-cut.ts
|
|
1082
1197
|
function failureReason(err) {
|
|
1083
1198
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -1226,6 +1341,14 @@ var NATIVE_LEASE_HOLD_FRAMES = NATIVE_LEASE_KNOBS.values.holdFrames;
|
|
|
1226
1341
|
*/
|
|
1227
1342
|
var NATIVE_TILE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.tileBudgetMb * 1024 * 1024;
|
|
1228
1343
|
/**
|
|
1344
|
+
* Hard RAM budget (bytes) for the SCENE TILE store — one JPEG of the whole
|
|
1345
|
+
* native frame per detection-bearing frame, which is the only thing that can
|
|
1346
|
+
* answer a FULL-FRAME native request after the hold ends. Separate from the
|
|
1347
|
+
* subject budget so a key frame can never evict a face tile. `0` disables the
|
|
1348
|
+
* rung. See `scene-tile.ts` for the measurements that sized it.
|
|
1349
|
+
*/
|
|
1350
|
+
var NATIVE_SCENE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.sceneBudgetMb * 1024 * 1024;
|
|
1351
|
+
/**
|
|
1229
1352
|
* How long a subject tile is served. A TTL is honest HERE and dishonest on the
|
|
1230
1353
|
* hold: nothing is waiting on a tile, it is speculative retention against a
|
|
1231
1354
|
* request that may never come. Sized an order of magnitude above the measured
|
|
@@ -1563,11 +1686,33 @@ var DecodeWorkerChild = class {
|
|
|
1563
1686
|
budgetBytes: NATIVE_TILE_BUDGET_BYTES,
|
|
1564
1687
|
ttlMs: NATIVE_TILE_TTL_MS
|
|
1565
1688
|
});
|
|
1689
|
+
/**
|
|
1690
|
+
* What survives a held frame for a FULL-FRAME request: one JPEG of the whole
|
|
1691
|
+
* native raster, cut in the SAME synchronous read phase as the subject tiles.
|
|
1692
|
+
*
|
|
1693
|
+
* A separate store, not a fifth entry in {@link tileStore}, because a scene
|
|
1694
|
+
* tile is ~20× a subject tile: one shared budget would let a busy camera's
|
|
1695
|
+
* key frames evict the face/plate tiles the recognisers live on. Two stores
|
|
1696
|
+
* make starving that tier impossible rather than unlikely.
|
|
1697
|
+
*/
|
|
1698
|
+
sceneStore = new NativeTileStore({
|
|
1699
|
+
budgetBytes: NATIVE_SCENE_BUDGET_BYTES,
|
|
1700
|
+
ttlMs: NATIVE_TILE_TTL_MS
|
|
1701
|
+
});
|
|
1566
1702
|
/** Tiles cut / served / refused-for-containment, on the throughput line. */
|
|
1567
1703
|
tilesCut = 0;
|
|
1568
1704
|
tileHits = 0;
|
|
1569
1705
|
tileMisses = 0;
|
|
1570
1706
|
tileCutFailures = 0;
|
|
1707
|
+
/** Scene tiles cut / served, on the throughput line. */
|
|
1708
|
+
scenesCut = 0;
|
|
1709
|
+
sceneHits = 0;
|
|
1710
|
+
/**
|
|
1711
|
+
* Full-frame cuts NOT taken because the rung is switched off
|
|
1712
|
+
* (`sceneBudgetMb: 0`). A branch that drops work counts it — this one drops a
|
|
1713
|
+
* key frame that can never be captured again.
|
|
1714
|
+
*/
|
|
1715
|
+
sceneRefused = 0;
|
|
1571
1716
|
/**
|
|
1572
1717
|
* Native frame dimensions the retained tiles were cut from. A session's
|
|
1573
1718
|
* resolution does not change (a re-dial clears the store), so ONE pair is
|
|
@@ -1653,6 +1798,9 @@ var DecodeWorkerChild = class {
|
|
|
1653
1798
|
* un-guarded version a log flood rather than a diagnosis.
|
|
1654
1799
|
*/
|
|
1655
1800
|
warnedTileEncoderUnavailable = false;
|
|
1801
|
+
/** One-shot: the scene rung is off by configuration, which cannot change under
|
|
1802
|
+
* a running worker. The VOLUME lives on `sceneRefused`. */
|
|
1803
|
+
warnedSceneDisabled = false;
|
|
1656
1804
|
/**
|
|
1657
1805
|
* The last subject-tile-cut failure REASON logged — see
|
|
1658
1806
|
* {@link reportTileCutFailure}. Compared, not counted: the count lives on the
|
|
@@ -1781,7 +1929,7 @@ var DecodeWorkerChild = class {
|
|
|
1781
1929
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
1782
1930
|
const rssMb = Math.round(process.memoryUsage().rss / 1048576);
|
|
1783
1931
|
const ageS = Math.round((now - this.startedAt) / 1e3);
|
|
1784
|
-
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1932
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, scenesCut:${this.scenesCut}, sceneHits:${this.sceneHits}, sceneRefused:${this.sceneRefused}, sceneEvicted:${this.sceneStore.evictedCount}, sceneMb:${Math.round(this.sceneStore.totalBytes / 1048576)}, sceneFrames:${this.sceneStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1785
1933
|
this.lastMetricsAt = now;
|
|
1786
1934
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
1787
1935
|
if (!this.warnedLarge && rssMb >= WORKER_RSS_WARN_MB) {
|
|
@@ -1923,7 +2071,7 @@ var DecodeWorkerChild = class {
|
|
|
1923
2071
|
this.send({
|
|
1924
2072
|
kind: "nativeCropMiss",
|
|
1925
2073
|
requestId,
|
|
1926
|
-
reason: this.
|
|
2074
|
+
reason: this.tileMissReason(frameId)
|
|
1927
2075
|
});
|
|
1928
2076
|
} catch (err) {
|
|
1929
2077
|
this.tileMisses += 1;
|
|
@@ -1937,6 +2085,18 @@ var DecodeWorkerChild = class {
|
|
|
1937
2085
|
});
|
|
1938
2086
|
}
|
|
1939
2087
|
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Why nothing retained could answer — named per RUNG, because they have
|
|
2090
|
+
* different fixes. "Subject tiles are off" and "the scene rung is off" and
|
|
2091
|
+
* "the rectangle is outside every container" are three different sentences,
|
|
2092
|
+
* and one "not retained" hid all of them.
|
|
2093
|
+
*/
|
|
2094
|
+
tileMissReason(frameId) {
|
|
2095
|
+
if (!this.tileStore.enabled && !this.sceneStore.enabled) return `frame ${frameId} released and retention is DISABLED (tileBudgetMb 0, sceneBudgetMb 0)`;
|
|
2096
|
+
if (!this.sceneStore.enabled) return `frame ${frameId} released and no retained subject tile contains the requested rectangle; scene tiles are DISABLED (sceneBudgetMb 0), so a full-frame request cannot be served at all`;
|
|
2097
|
+
if (!this.tileStore.enabled) return `frame ${frameId} released and subject tiles are DISABLED (tileBudgetMb 0)`;
|
|
2098
|
+
return `frame ${frameId} released and no retained subject or scene tile contains the requested rectangle`;
|
|
2099
|
+
}
|
|
1940
2100
|
/** The GPU-ring or reserved-slot frame for `frameId` (hold-disabled / newest paths). */
|
|
1941
2101
|
resolveRetainedFrame(frameId) {
|
|
1942
2102
|
return this.nativeRing.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
@@ -1963,8 +2123,8 @@ var DecodeWorkerChild = class {
|
|
|
1963
2123
|
*/
|
|
1964
2124
|
async handleCutTiles(frameId, bboxes) {
|
|
1965
2125
|
try {
|
|
1966
|
-
if (!this.tileStore.enabled || bboxes.length === 0) return;
|
|
1967
|
-
const wanted = bboxes.slice(0, NATIVE_TILE_MAX_PER_FRAME);
|
|
2126
|
+
if (!this.tileStore.enabled && !this.sceneStore.enabled || bboxes.length === 0) return;
|
|
2127
|
+
const wanted = this.tileStore.enabled ? bboxes.slice(0, NATIVE_TILE_MAX_PER_FRAME) : [];
|
|
1968
2128
|
if (bboxes.length > wanted.length) this.emitStderr(`decode-worker-child: tile cut capped at ${NATIVE_TILE_MAX_PER_FRAME} of ${bboxes.length} subjects on frame ${frameId} — the rest fall back to the ≤640 tier\n`);
|
|
1969
2129
|
const sharp = await this.tileEncoder();
|
|
1970
2130
|
const frame = this.leaseStore.get(frameId)?.frame ?? this.resolveRetainedFrame(frameId);
|
|
@@ -1983,7 +2143,18 @@ var DecodeWorkerChild = class {
|
|
|
1983
2143
|
}
|
|
1984
2144
|
this.tileFrameWidth = frame.width;
|
|
1985
2145
|
this.tileFrameHeight = frame.height;
|
|
1986
|
-
const
|
|
2146
|
+
const plan = planFrameCuts(wanted, {
|
|
2147
|
+
sceneEnabled: this.sceneStore.enabled,
|
|
2148
|
+
hasDetections: bboxes.length > 0
|
|
2149
|
+
});
|
|
2150
|
+
if (!plan.scene) {
|
|
2151
|
+
this.sceneRefused += 1;
|
|
2152
|
+
if (!this.warnedSceneDisabled) {
|
|
2153
|
+
this.warnedSceneDisabled = true;
|
|
2154
|
+
this.emitStderr("decode-worker-child: scene tiles DISABLED (sceneBudgetMb 0) — every late FULL-FRAME request (native keyFrame, detail frameJpeg, gallery fallback) will miss once this frame is released; refusals are counted as sceneRefused\n");
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
const cut = await cutSubjectTiles(frame, plan.bboxes, {
|
|
1987
2158
|
isLive: (f) => this.isRetainedFrameLive(f),
|
|
1988
2159
|
cutRaw: (f, bbox) => {
|
|
1989
2160
|
const { region } = resolveNativeTileRegion(f.width, f.height, bbox, NATIVE_TILE_PADDING_RATIO);
|
|
@@ -2017,10 +2188,18 @@ var DecodeWorkerChild = class {
|
|
|
2017
2188
|
this.reportTileCutFailure(frameId, reason);
|
|
2018
2189
|
}
|
|
2019
2190
|
});
|
|
2020
|
-
this.tileCutFailures +=
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2191
|
+
this.tileCutFailures += plan.bboxes.length - cut.length;
|
|
2192
|
+
const { subjects, scene } = plan.scene ? partitionSceneTile(cut, frame.width, frame.height) : {
|
|
2193
|
+
subjects: cut,
|
|
2194
|
+
scene: null
|
|
2195
|
+
};
|
|
2196
|
+
if (subjects.length > 0) {
|
|
2197
|
+
this.tilesCut += subjects.length;
|
|
2198
|
+
this.tileStore.put(frameId, subjects);
|
|
2199
|
+
}
|
|
2200
|
+
if (scene !== null) {
|
|
2201
|
+
this.scenesCut += 1;
|
|
2202
|
+
this.sceneStore.put(frameId, [scene]);
|
|
2024
2203
|
}
|
|
2025
2204
|
} finally {
|
|
2026
2205
|
this.leaseStore.release(frameId);
|
|
@@ -2103,14 +2282,16 @@ var DecodeWorkerChild = class {
|
|
|
2103
2282
|
* which would enrich a different part of the scene and error nowhere.
|
|
2104
2283
|
*/
|
|
2105
2284
|
async serveFromTile(frameId, bbox, maxWidth) {
|
|
2285
|
+
const scenes = this.sceneStore.get(frameId);
|
|
2106
2286
|
const tiles = this.tileStore.get(frameId);
|
|
2107
|
-
if (tiles.length === 0) return null;
|
|
2287
|
+
if (tiles.length === 0 && scenes.length === 0) return null;
|
|
2108
2288
|
const width = this.tileFrameWidth;
|
|
2109
2289
|
const height = this.tileFrameHeight;
|
|
2110
2290
|
if (width <= 0 || height <= 0) return null;
|
|
2111
2291
|
const { region, target } = resolveNativeCropGeometry(width, height, bbox, maxWidth);
|
|
2112
|
-
const served =
|
|
2292
|
+
const served = selectRetainedTile(tiles, scenes, region);
|
|
2113
2293
|
if (!served) return null;
|
|
2294
|
+
if (served.source === "scene") this.sceneHits += 1;
|
|
2114
2295
|
let pipeline = (await require_lazy_sharp.getSharp())(served.tile.bytes).extract({
|
|
2115
2296
|
left: served.local.left,
|
|
2116
2297
|
top: served.local.top,
|
|
@@ -2767,6 +2948,7 @@ var DecodeWorkerChild = class {
|
|
|
2767
2948
|
this.nativeRing.clear();
|
|
2768
2949
|
this.leaseStore.clear();
|
|
2769
2950
|
this.tileStore.clear();
|
|
2951
|
+
this.sceneStore.clear();
|
|
2770
2952
|
this.tileFrameWidth = 0;
|
|
2771
2953
|
this.tileFrameHeight = 0;
|
|
2772
2954
|
this.nativeLeaseDownloadFilter?.close();
|
|
@@ -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-CMDUAqvX.mjs";
|
|
2
2
|
import { n as setSharpWarnSink, r as hostExternalEntryUrls, t as getSharp } from "../lazy-sharp-BtT5x-qP.mjs";
|
|
3
3
|
//#region src/session-decode/color-conversion.ts
|
|
4
4
|
/**
|
|
@@ -791,11 +791,24 @@ var NativeTileStore = class {
|
|
|
791
791
|
ttlMs;
|
|
792
792
|
now;
|
|
793
793
|
bytes = 0;
|
|
794
|
+
/**
|
|
795
|
+
* Frames whose tiles were dropped by the BUDGET before their TTL ran out.
|
|
796
|
+
*
|
|
797
|
+
* Counted, not silent: budget eviction is the one path here that destroys
|
|
798
|
+
* retained work, and on the scene store a dropped entry is a key frame that
|
|
799
|
+
* can never be captured again. `0` means the TTL is what bounds this store —
|
|
800
|
+
* anything else is the signal that its budget is the binding limit.
|
|
801
|
+
*/
|
|
802
|
+
evicted = 0;
|
|
794
803
|
constructor(options) {
|
|
795
804
|
this.budgetBytes = options.budgetBytes;
|
|
796
805
|
this.ttlMs = options.ttlMs;
|
|
797
806
|
this.now = options.now ?? Date.now;
|
|
798
807
|
}
|
|
808
|
+
/** Frames dropped by the byte budget rather than by the TTL (tests / metrics). */
|
|
809
|
+
get evictedCount() {
|
|
810
|
+
return this.evicted;
|
|
811
|
+
}
|
|
799
812
|
/** `false` when tiles are disabled (`budgetBytes <= 0`). */
|
|
800
813
|
get enabled() {
|
|
801
814
|
return this.budgetBytes > 0;
|
|
@@ -836,6 +849,7 @@ var NativeTileStore = class {
|
|
|
836
849
|
const oldest = this.entries.keys().next().value;
|
|
837
850
|
if (oldest === void 0 || oldest === frameId) break;
|
|
838
851
|
this.drop(oldest);
|
|
852
|
+
this.evicted += 1;
|
|
839
853
|
}
|
|
840
854
|
return true;
|
|
841
855
|
}
|
|
@@ -1077,6 +1091,107 @@ function resolveRgbCropSource(plane, planeStride, region) {
|
|
|
1077
1091
|
};
|
|
1078
1092
|
}
|
|
1079
1093
|
//#endregion
|
|
1094
|
+
//#region src/session-decode/scene-tile.ts
|
|
1095
|
+
/**
|
|
1096
|
+
* The whole frame, in the same normalized space every other cut uses. Identical
|
|
1097
|
+
* to the bbox `getNativeCrop`'s full-frame callers send, which is the point —
|
|
1098
|
+
* the rectangle asked for and the rectangle retained are the same one.
|
|
1099
|
+
*/
|
|
1100
|
+
var SCENE_TILE_BBOX = {
|
|
1101
|
+
x: 0,
|
|
1102
|
+
y: 0,
|
|
1103
|
+
w: 1,
|
|
1104
|
+
h: 1
|
|
1105
|
+
};
|
|
1106
|
+
/**
|
|
1107
|
+
* Plan one frame's cut.
|
|
1108
|
+
*
|
|
1109
|
+
* The scene rectangle is appended to the SAME cut rather than taken separately,
|
|
1110
|
+
* because `subject-tile-cut.ts` reads every rectangle in one synchronous turn:
|
|
1111
|
+
* a second read after the first cut's encodes have awaited is exactly the
|
|
1112
|
+
* eviction window that once cut subject 1 of each frame and lost the rest.
|
|
1113
|
+
*
|
|
1114
|
+
* A frame that detected NOTHING gets nothing, scene tile included. That
|
|
1115
|
+
* asymmetry is what keeps this tier affordable — the frames a key-frame capture
|
|
1116
|
+
* can ever name are the frames that carried a best detection, and on this
|
|
1117
|
+
* cluster those are ~0.4/s per camera against ~2.1/s delivered.
|
|
1118
|
+
*/
|
|
1119
|
+
function planFrameCuts(subjects, conditions) {
|
|
1120
|
+
if (!conditions.sceneEnabled || !conditions.hasDetections) return {
|
|
1121
|
+
bboxes: subjects,
|
|
1122
|
+
scene: false
|
|
1123
|
+
};
|
|
1124
|
+
return {
|
|
1125
|
+
bboxes: [...subjects, SCENE_TILE_BBOX],
|
|
1126
|
+
scene: true
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Does this region cover the whole frame?
|
|
1131
|
+
*
|
|
1132
|
+
* Judged on the RESOLVED region rather than on the position in the cut list,
|
|
1133
|
+
* because `cutSubjectTiles` drops a rectangle whose read or encode failed and
|
|
1134
|
+
* positional identity does not survive that. Unknown frame dimensions (a freed
|
|
1135
|
+
* node-av frame reports 0×0) are `false` — classifying against those would file
|
|
1136
|
+
* every subject tile as the scene.
|
|
1137
|
+
*/
|
|
1138
|
+
function isSceneRegion(region, frameWidth, frameHeight) {
|
|
1139
|
+
if (frameWidth <= 0 || frameHeight <= 0) return false;
|
|
1140
|
+
return region.left <= 0 && region.top <= 0 && region.width >= frameWidth && region.height >= frameHeight;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Route each cut tile to the store that owns it.
|
|
1144
|
+
*
|
|
1145
|
+
* The FIRST frame-spanning tile is the scene; any later one stays a subject. A
|
|
1146
|
+
* padded subject that already covers the frame genuinely IS the scene, and both
|
|
1147
|
+
* stores are consulted on a serve, so claiming one of them costs no hit and
|
|
1148
|
+
* keeps the two stores from holding the same pixels twice.
|
|
1149
|
+
*/
|
|
1150
|
+
function partitionSceneTile(tiles, frameWidth, frameHeight) {
|
|
1151
|
+
const subjects = [];
|
|
1152
|
+
let scene = null;
|
|
1153
|
+
for (const tile of tiles) {
|
|
1154
|
+
if (scene === null && isSceneRegion(tile.region, frameWidth, frameHeight)) {
|
|
1155
|
+
scene = tile;
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
subjects.push(tile);
|
|
1159
|
+
}
|
|
1160
|
+
return {
|
|
1161
|
+
subjects,
|
|
1162
|
+
scene
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Pick the retained tile that CONTAINS `rect`, subject tiles first.
|
|
1167
|
+
*
|
|
1168
|
+
* The order is the whole of the policy and it is not cosmetic. Both rungs are
|
|
1169
|
+
* native-resolution, so either would be honest — but a subject tile is
|
|
1170
|
+
* ~60-120 KB against a scene tile's ~1.5-2.5 MB, and the extract runs on
|
|
1171
|
+
* whatever it is handed. Asking the subject store first keeps an ordinary
|
|
1172
|
+
* face/plate crop off the 20×-larger raster; the scene tile is consulted only
|
|
1173
|
+
* for the rectangles no subject tile could ever contain, which is exactly the
|
|
1174
|
+
* set that had no rung at all before it existed.
|
|
1175
|
+
*
|
|
1176
|
+
* `null` means no retained rung contains the rectangle — a MISS, never a clamp
|
|
1177
|
+
* into the nearest tile ([D52](../../../../docs/decisions/adr-0052.md)).
|
|
1178
|
+
*/
|
|
1179
|
+
function selectRetainedTile(subjects, scenes, rect) {
|
|
1180
|
+
const fromSubject = selectTileForRect(subjects, rect);
|
|
1181
|
+
if (fromSubject) return {
|
|
1182
|
+
tile: fromSubject.tile,
|
|
1183
|
+
local: fromSubject.local,
|
|
1184
|
+
source: "subject"
|
|
1185
|
+
};
|
|
1186
|
+
const fromScene = selectTileForRect(scenes, rect);
|
|
1187
|
+
if (fromScene) return {
|
|
1188
|
+
tile: fromScene.tile,
|
|
1189
|
+
local: fromScene.local,
|
|
1190
|
+
source: "scene"
|
|
1191
|
+
};
|
|
1192
|
+
return null;
|
|
1193
|
+
}
|
|
1194
|
+
//#endregion
|
|
1080
1195
|
//#region src/session-decode/subject-tile-cut.ts
|
|
1081
1196
|
function failureReason(err) {
|
|
1082
1197
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -1225,6 +1340,14 @@ var NATIVE_LEASE_HOLD_FRAMES = NATIVE_LEASE_KNOBS.values.holdFrames;
|
|
|
1225
1340
|
*/
|
|
1226
1341
|
var NATIVE_TILE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.tileBudgetMb * 1024 * 1024;
|
|
1227
1342
|
/**
|
|
1343
|
+
* Hard RAM budget (bytes) for the SCENE TILE store — one JPEG of the whole
|
|
1344
|
+
* native frame per detection-bearing frame, which is the only thing that can
|
|
1345
|
+
* answer a FULL-FRAME native request after the hold ends. Separate from the
|
|
1346
|
+
* subject budget so a key frame can never evict a face tile. `0` disables the
|
|
1347
|
+
* rung. See `scene-tile.ts` for the measurements that sized it.
|
|
1348
|
+
*/
|
|
1349
|
+
var NATIVE_SCENE_BUDGET_BYTES = NATIVE_LEASE_KNOBS.values.sceneBudgetMb * 1024 * 1024;
|
|
1350
|
+
/**
|
|
1228
1351
|
* How long a subject tile is served. A TTL is honest HERE and dishonest on the
|
|
1229
1352
|
* hold: nothing is waiting on a tile, it is speculative retention against a
|
|
1230
1353
|
* request that may never come. Sized an order of magnitude above the measured
|
|
@@ -1562,11 +1685,33 @@ var DecodeWorkerChild = class {
|
|
|
1562
1685
|
budgetBytes: NATIVE_TILE_BUDGET_BYTES,
|
|
1563
1686
|
ttlMs: NATIVE_TILE_TTL_MS
|
|
1564
1687
|
});
|
|
1688
|
+
/**
|
|
1689
|
+
* What survives a held frame for a FULL-FRAME request: one JPEG of the whole
|
|
1690
|
+
* native raster, cut in the SAME synchronous read phase as the subject tiles.
|
|
1691
|
+
*
|
|
1692
|
+
* A separate store, not a fifth entry in {@link tileStore}, because a scene
|
|
1693
|
+
* tile is ~20× a subject tile: one shared budget would let a busy camera's
|
|
1694
|
+
* key frames evict the face/plate tiles the recognisers live on. Two stores
|
|
1695
|
+
* make starving that tier impossible rather than unlikely.
|
|
1696
|
+
*/
|
|
1697
|
+
sceneStore = new NativeTileStore({
|
|
1698
|
+
budgetBytes: NATIVE_SCENE_BUDGET_BYTES,
|
|
1699
|
+
ttlMs: NATIVE_TILE_TTL_MS
|
|
1700
|
+
});
|
|
1565
1701
|
/** Tiles cut / served / refused-for-containment, on the throughput line. */
|
|
1566
1702
|
tilesCut = 0;
|
|
1567
1703
|
tileHits = 0;
|
|
1568
1704
|
tileMisses = 0;
|
|
1569
1705
|
tileCutFailures = 0;
|
|
1706
|
+
/** Scene tiles cut / served, on the throughput line. */
|
|
1707
|
+
scenesCut = 0;
|
|
1708
|
+
sceneHits = 0;
|
|
1709
|
+
/**
|
|
1710
|
+
* Full-frame cuts NOT taken because the rung is switched off
|
|
1711
|
+
* (`sceneBudgetMb: 0`). A branch that drops work counts it — this one drops a
|
|
1712
|
+
* key frame that can never be captured again.
|
|
1713
|
+
*/
|
|
1714
|
+
sceneRefused = 0;
|
|
1570
1715
|
/**
|
|
1571
1716
|
* Native frame dimensions the retained tiles were cut from. A session's
|
|
1572
1717
|
* resolution does not change (a re-dial clears the store), so ONE pair is
|
|
@@ -1652,6 +1797,9 @@ var DecodeWorkerChild = class {
|
|
|
1652
1797
|
* un-guarded version a log flood rather than a diagnosis.
|
|
1653
1798
|
*/
|
|
1654
1799
|
warnedTileEncoderUnavailable = false;
|
|
1800
|
+
/** One-shot: the scene rung is off by configuration, which cannot change under
|
|
1801
|
+
* a running worker. The VOLUME lives on `sceneRefused`. */
|
|
1802
|
+
warnedSceneDisabled = false;
|
|
1655
1803
|
/**
|
|
1656
1804
|
* The last subject-tile-cut failure REASON logged — see
|
|
1657
1805
|
* {@link reportTileCutFailure}. Compared, not counted: the count lives on the
|
|
@@ -1780,7 +1928,7 @@ var DecodeWorkerChild = class {
|
|
|
1780
1928
|
const skipped = this.framesSkipped + this.frames.droppedCount;
|
|
1781
1929
|
const rssMb = Math.round(process.memoryUsage().rss / 1048576);
|
|
1782
1930
|
const ageS = Math.round((now - this.startedAt) / 1e3);
|
|
1783
|
-
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1931
|
+
this.emitStderr(`session-decode metrics {framesDecoded:${this.framesDecoded}, framesSkipped:${skipped}, deliveredFps:${deliveredFps}, nativeCropHits:${this.nativeCropHits}, nativeCropMisses:${this.nativeCropMisses}, leaseOffered:${this.admission.offered}, leaseAdmitted:${this.admission.admitted}, leaseMarks:${this.admission.marks}, leaseUnmarkedCrops:${this.admission.unmarkedCrops}, leaseAdmission:${NATIVE_LEASE_KNOBS.values.admission}, leaseMb:${Math.round(this.leaseStore.totalBytes / 1048576)}, leaseFrames:${this.leaseStore.size}, holdOverflow:${this.leaseStore.overflowCount}, tilesCut:${this.tilesCut}, tileHits:${this.tileHits}, tileMisses:${this.tileMisses}, tileCutFailures:${this.tileCutFailures}, tileMb:${Math.round(this.tileStore.totalBytes / 1048576)}, tileFrames:${this.tileStore.frameCount}, scenesCut:${this.scenesCut}, sceneHits:${this.sceneHits}, sceneRefused:${this.sceneRefused}, sceneEvicted:${this.sceneStore.evictedCount}, sceneMb:${Math.round(this.sceneStore.totalBytes / 1048576)}, sceneFrames:${this.sceneStore.frameCount}, rssMb:${rssMb}, ageS:${ageS}}${final ? " (final)" : ""}\n`);
|
|
1784
1932
|
this.lastMetricsAt = now;
|
|
1785
1933
|
this.lastDeliveredSnapshot = this.framesDelivered;
|
|
1786
1934
|
if (!this.warnedLarge && rssMb >= WORKER_RSS_WARN_MB) {
|
|
@@ -1922,7 +2070,7 @@ var DecodeWorkerChild = class {
|
|
|
1922
2070
|
this.send({
|
|
1923
2071
|
kind: "nativeCropMiss",
|
|
1924
2072
|
requestId,
|
|
1925
|
-
reason: this.
|
|
2073
|
+
reason: this.tileMissReason(frameId)
|
|
1926
2074
|
});
|
|
1927
2075
|
} catch (err) {
|
|
1928
2076
|
this.tileMisses += 1;
|
|
@@ -1936,6 +2084,18 @@ var DecodeWorkerChild = class {
|
|
|
1936
2084
|
});
|
|
1937
2085
|
}
|
|
1938
2086
|
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Why nothing retained could answer — named per RUNG, because they have
|
|
2089
|
+
* different fixes. "Subject tiles are off" and "the scene rung is off" and
|
|
2090
|
+
* "the rectangle is outside every container" are three different sentences,
|
|
2091
|
+
* and one "not retained" hid all of them.
|
|
2092
|
+
*/
|
|
2093
|
+
tileMissReason(frameId) {
|
|
2094
|
+
if (!this.tileStore.enabled && !this.sceneStore.enabled) return `frame ${frameId} released and retention is DISABLED (tileBudgetMb 0, sceneBudgetMb 0)`;
|
|
2095
|
+
if (!this.sceneStore.enabled) return `frame ${frameId} released and no retained subject tile contains the requested rectangle; scene tiles are DISABLED (sceneBudgetMb 0), so a full-frame request cannot be served at all`;
|
|
2096
|
+
if (!this.tileStore.enabled) return `frame ${frameId} released and subject tiles are DISABLED (tileBudgetMb 0)`;
|
|
2097
|
+
return `frame ${frameId} released and no retained subject or scene tile contains the requested rectangle`;
|
|
2098
|
+
}
|
|
1939
2099
|
/** The GPU-ring or reserved-slot frame for `frameId` (hold-disabled / newest paths). */
|
|
1940
2100
|
resolveRetainedFrame(frameId) {
|
|
1941
2101
|
return this.nativeRing.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
@@ -1962,8 +2122,8 @@ var DecodeWorkerChild = class {
|
|
|
1962
2122
|
*/
|
|
1963
2123
|
async handleCutTiles(frameId, bboxes) {
|
|
1964
2124
|
try {
|
|
1965
|
-
if (!this.tileStore.enabled || bboxes.length === 0) return;
|
|
1966
|
-
const wanted = bboxes.slice(0, NATIVE_TILE_MAX_PER_FRAME);
|
|
2125
|
+
if (!this.tileStore.enabled && !this.sceneStore.enabled || bboxes.length === 0) return;
|
|
2126
|
+
const wanted = this.tileStore.enabled ? bboxes.slice(0, NATIVE_TILE_MAX_PER_FRAME) : [];
|
|
1967
2127
|
if (bboxes.length > wanted.length) this.emitStderr(`decode-worker-child: tile cut capped at ${NATIVE_TILE_MAX_PER_FRAME} of ${bboxes.length} subjects on frame ${frameId} — the rest fall back to the ≤640 tier\n`);
|
|
1968
2128
|
const sharp = await this.tileEncoder();
|
|
1969
2129
|
const frame = this.leaseStore.get(frameId)?.frame ?? this.resolveRetainedFrame(frameId);
|
|
@@ -1982,7 +2142,18 @@ var DecodeWorkerChild = class {
|
|
|
1982
2142
|
}
|
|
1983
2143
|
this.tileFrameWidth = frame.width;
|
|
1984
2144
|
this.tileFrameHeight = frame.height;
|
|
1985
|
-
const
|
|
2145
|
+
const plan = planFrameCuts(wanted, {
|
|
2146
|
+
sceneEnabled: this.sceneStore.enabled,
|
|
2147
|
+
hasDetections: bboxes.length > 0
|
|
2148
|
+
});
|
|
2149
|
+
if (!plan.scene) {
|
|
2150
|
+
this.sceneRefused += 1;
|
|
2151
|
+
if (!this.warnedSceneDisabled) {
|
|
2152
|
+
this.warnedSceneDisabled = true;
|
|
2153
|
+
this.emitStderr("decode-worker-child: scene tiles DISABLED (sceneBudgetMb 0) — every late FULL-FRAME request (native keyFrame, detail frameJpeg, gallery fallback) will miss once this frame is released; refusals are counted as sceneRefused\n");
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
const cut = await cutSubjectTiles(frame, plan.bboxes, {
|
|
1986
2157
|
isLive: (f) => this.isRetainedFrameLive(f),
|
|
1987
2158
|
cutRaw: (f, bbox) => {
|
|
1988
2159
|
const { region } = resolveNativeTileRegion(f.width, f.height, bbox, NATIVE_TILE_PADDING_RATIO);
|
|
@@ -2016,10 +2187,18 @@ var DecodeWorkerChild = class {
|
|
|
2016
2187
|
this.reportTileCutFailure(frameId, reason);
|
|
2017
2188
|
}
|
|
2018
2189
|
});
|
|
2019
|
-
this.tileCutFailures +=
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2190
|
+
this.tileCutFailures += plan.bboxes.length - cut.length;
|
|
2191
|
+
const { subjects, scene } = plan.scene ? partitionSceneTile(cut, frame.width, frame.height) : {
|
|
2192
|
+
subjects: cut,
|
|
2193
|
+
scene: null
|
|
2194
|
+
};
|
|
2195
|
+
if (subjects.length > 0) {
|
|
2196
|
+
this.tilesCut += subjects.length;
|
|
2197
|
+
this.tileStore.put(frameId, subjects);
|
|
2198
|
+
}
|
|
2199
|
+
if (scene !== null) {
|
|
2200
|
+
this.scenesCut += 1;
|
|
2201
|
+
this.sceneStore.put(frameId, [scene]);
|
|
2023
2202
|
}
|
|
2024
2203
|
} finally {
|
|
2025
2204
|
this.leaseStore.release(frameId);
|
|
@@ -2102,14 +2281,16 @@ var DecodeWorkerChild = class {
|
|
|
2102
2281
|
* which would enrich a different part of the scene and error nowhere.
|
|
2103
2282
|
*/
|
|
2104
2283
|
async serveFromTile(frameId, bbox, maxWidth) {
|
|
2284
|
+
const scenes = this.sceneStore.get(frameId);
|
|
2105
2285
|
const tiles = this.tileStore.get(frameId);
|
|
2106
|
-
if (tiles.length === 0) return null;
|
|
2286
|
+
if (tiles.length === 0 && scenes.length === 0) return null;
|
|
2107
2287
|
const width = this.tileFrameWidth;
|
|
2108
2288
|
const height = this.tileFrameHeight;
|
|
2109
2289
|
if (width <= 0 || height <= 0) return null;
|
|
2110
2290
|
const { region, target } = resolveNativeCropGeometry(width, height, bbox, maxWidth);
|
|
2111
|
-
const served =
|
|
2291
|
+
const served = selectRetainedTile(tiles, scenes, region);
|
|
2112
2292
|
if (!served) return null;
|
|
2293
|
+
if (served.source === "scene") this.sceneHits += 1;
|
|
2113
2294
|
let pipeline = (await getSharp())(served.tile.bytes).extract({
|
|
2114
2295
|
left: served.local.left,
|
|
2115
2296
|
top: served.local.top,
|
|
@@ -2766,6 +2947,7 @@ var DecodeWorkerChild = class {
|
|
|
2766
2947
|
this.nativeRing.clear();
|
|
2767
2948
|
this.leaseStore.clear();
|
|
2768
2949
|
this.tileStore.clear();
|
|
2950
|
+
this.sceneStore.clear();
|
|
2769
2951
|
this.tileFrameWidth = 0;
|
|
2770
2952
|
this.tileFrameHeight = 0;
|
|
2771
2953
|
this.nativeLeaseDownloadFilter?.close();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-77rGxv1J.mjs";
|
|
2
2
|
import { a as p, i as m, n as h, o as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
|
|
3
3
|
import { n as y, r as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
|
|
4
|
-
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-
|
|
4
|
+
import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CVBnNONy.mjs";
|
|
5
5
|
import { n as C, t as w } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
|
|
6
6
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
7
7
|
var T = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), E = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), D = (e) => {
|