@camstack/addon-pipeline 1.2.115 → 1.2.119
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-C0ht2KPO.js → addon-utils-RAHF2hmW.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 +171 -167
- package/dist/detection-pipeline/index.mjs +169 -165
- package/dist/{dist-BqIbYW1A.js → dist-DIsM42Ex.js} +121 -11
- package/dist/{dist-DGQkkWc6.mjs → dist-Db664g2O.mjs} +116 -12
- package/dist/{event-loop-stall-monitor-ylMGpz48.js → event-loop-stall-monitor-CSpdFZb6.js} +1 -1
- package/dist/{event-loop-stall-monitor-D5jK5v4Z.mjs → event-loop-stall-monitor-DY6339bX.mjs} +1 -1
- package/dist/{lazy-sharp-cOsy7l_P.js → lazy-sharp-LSlEXQe_.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 +12 -14
- package/dist/pipeline-runner/index.mjs +11 -13
- package/dist/{process-memory-BtT7WfbJ.mjs → process-memory-CPm5TMBl.mjs} +1 -1
- package/dist/{process-memory-45juuHpN.js → process-memory-DpSJQF-p.js} +1 -1
- package/dist/recorder/index.js +261 -32
- package/dist/recorder/index.mjs +260 -31
- package/dist/session-decode/decode-worker-child.js +34 -26
- package/dist/session-decode/decode-worker-child.mjs +33 -25
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C_SFFNGT.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CstvrzmK.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BiNja8Hw.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-BAMyzCWw.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D-NFPV_g.mjs} +1 -1
- package/dist/stream-broker/{hostInit-CG28NvlV.mjs → hostInit-kHeLqEod.mjs} +2 -2
- package/dist/stream-broker/index.js +8 -6
- package/dist/stream-broker/index.mjs +8 -6
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-CIrqGbpG.js → worker-protocol-C1tr8WP3.js} +1 -1
- package/dist/{worker-protocol-DyocTIx_.mjs → worker-protocol-_zMPqSas.mjs} +1 -1
- 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-B1LDFHY9.mjs +0 -26
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as deriveRecordingMode, Dt as hydrateSchema, Gt as string, Ht as number, M as batteryCapability, Pt as selectAssignedProfileSlots, R as deriveBatteryPresence, St as BaseAddon, T as RecordingConfigSchema, Tt as DeviceType, Ut as object, Wt as record, at as recordingCapability, b as OpsLogEntrySchema, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, ft as storageEvictableCapability, g as ExportRecordSchema, gt as errMsg, i as BatteryStatusSchema, jt as nodePin, m as EVENT_PAD_MS, ot as recordingExportCapability, qt as EventCategory, ut as resolveRecordingProfiles, w as RECORDING_EXPORT_MAX_READ_BYTES, wt as DeviceFeature } from "../dist-Db664g2O.mjs";
|
|
2
2
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
3
3
|
import { r as withRecordingIntent } from "../restream-intent-B4BXZra7.mjs";
|
|
4
4
|
import { n as createFileDataPlaneHandler, s as parseRangeHeader, t as contentTypeFor } from "../addon-utils-CgBCF-mE.mjs";
|
|
@@ -2983,11 +2983,23 @@ function concatFileDirective(uri) {
|
|
|
2983
2983
|
function buildExportConcatList(segments) {
|
|
2984
2984
|
return `ffconcat version 1.0\n${segments.map((s) => `${concatFileDirective(s.uri)}\nduration ${sec(s.durMs / 1e3)}`).map((l) => `${l}\n`).join("")}`;
|
|
2985
2985
|
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Concat list for already-finished MP4s. A `duration` line here would
|
|
2988
|
+
* TRUNCATE each hour to whatever dummy we wrote (the stream clock is
|
|
2989
|
+
* already inside the file).
|
|
2990
|
+
*/
|
|
2991
|
+
function buildAssembleConcatList(uris) {
|
|
2992
|
+
return `ffconcat version 1.0\n${uris.map((u) => `${concatFileDirective(u)}\n`).join("")}`;
|
|
2993
|
+
}
|
|
2986
2994
|
/** Pass 1 of a timelapse: remux the ffconcat list into one continuous file.
|
|
2987
2995
|
* `-safe 0` because the list's paths are absolute — which is exactly what the
|
|
2988
2996
|
* per-export source list contains (`export-source-playlist.ts`). */
|
|
2989
2997
|
function buildConcatPassArgs(input) {
|
|
2990
2998
|
return [
|
|
2999
|
+
"-fflags",
|
|
3000
|
+
"+discardcorrupt+genpts",
|
|
3001
|
+
"-err_detect",
|
|
3002
|
+
"ignore_err",
|
|
2991
3003
|
"-f",
|
|
2992
3004
|
"concat",
|
|
2993
3005
|
"-safe",
|
|
@@ -3002,6 +3014,21 @@ function buildConcatPassArgs(input) {
|
|
|
3002
3014
|
input.outPath
|
|
3003
3015
|
];
|
|
3004
3016
|
}
|
|
3017
|
+
/** Final stitch of the surviving per-hour MP4s. Already selected + encoded. */
|
|
3018
|
+
function buildAssembleArgs(input) {
|
|
3019
|
+
return [
|
|
3020
|
+
"-f",
|
|
3021
|
+
"concat",
|
|
3022
|
+
"-safe",
|
|
3023
|
+
"0",
|
|
3024
|
+
"-i",
|
|
3025
|
+
input.inputConcatList,
|
|
3026
|
+
"-c",
|
|
3027
|
+
"copy",
|
|
3028
|
+
...FASTSTART,
|
|
3029
|
+
input.outPath
|
|
3030
|
+
];
|
|
3031
|
+
}
|
|
3005
3032
|
/** Build the ffmpeg argv for one export render. */
|
|
3006
3033
|
function buildExportArgs(input) {
|
|
3007
3034
|
const { inputPlaylist, outPath, options } = input;
|
|
@@ -3047,6 +3074,65 @@ function buildExportArgs(input) {
|
|
|
3047
3074
|
];
|
|
3048
3075
|
}
|
|
3049
3076
|
//#endregion
|
|
3077
|
+
//#region src/recorder/addon/export-timelapse-chunks.ts
|
|
3078
|
+
/** One hour of concatenated footage — not one hour of wall clock. */
|
|
3079
|
+
var TIMELAPSE_CHUNK_MS = 36e5;
|
|
3080
|
+
/**
|
|
3081
|
+
* Split a concat list into hour-sized chunks. A single file longer than the
|
|
3082
|
+
* chunk stays whole — it is already one open, and cutting it would need a
|
|
3083
|
+
* second demux we do not have.
|
|
3084
|
+
*/
|
|
3085
|
+
function splitTimelapseChunks(entries, chunkMs = TIMELAPSE_CHUNK_MS) {
|
|
3086
|
+
if (entries.length === 0) return [];
|
|
3087
|
+
const chunks = [];
|
|
3088
|
+
let current = [];
|
|
3089
|
+
let currentDur = 0;
|
|
3090
|
+
let streamStartMs = 0;
|
|
3091
|
+
for (const entry of entries) {
|
|
3092
|
+
if (current.length > 0 && currentDur + entry.durMs > chunkMs) {
|
|
3093
|
+
chunks.push({
|
|
3094
|
+
entries: current,
|
|
3095
|
+
streamStartMs
|
|
3096
|
+
});
|
|
3097
|
+
streamStartMs += currentDur;
|
|
3098
|
+
current = [];
|
|
3099
|
+
currentDur = 0;
|
|
3100
|
+
}
|
|
3101
|
+
current.push(entry);
|
|
3102
|
+
currentDur += entry.durMs;
|
|
3103
|
+
}
|
|
3104
|
+
if (current.length > 0) chunks.push({
|
|
3105
|
+
entries: current,
|
|
3106
|
+
streamStartMs
|
|
3107
|
+
});
|
|
3108
|
+
return chunks;
|
|
3109
|
+
}
|
|
3110
|
+
/**
|
|
3111
|
+
* Move stream-time dense ranges onto a chunk's own `t=0`. Ranges that miss
|
|
3112
|
+
* the chunk disappear; a range that straddles the edge is clipped. Empty →
|
|
3113
|
+
* `undefined` so the select stays the single-rate form.
|
|
3114
|
+
*/
|
|
3115
|
+
function denseRangesForChunk(dense, chunkStreamStartMs, chunkDurMs) {
|
|
3116
|
+
if (dense === void 0 || dense.ranges.length === 0) return void 0;
|
|
3117
|
+
const startSec = chunkStreamStartMs / 1e3;
|
|
3118
|
+
const endSec = (chunkStreamStartMs + chunkDurMs) / 1e3;
|
|
3119
|
+
const ranges = [];
|
|
3120
|
+
for (const r of dense.ranges) {
|
|
3121
|
+
const from = Math.max(r.fromSec, startSec);
|
|
3122
|
+
const to = Math.min(r.toSec, endSec);
|
|
3123
|
+
if (to <= from) continue;
|
|
3124
|
+
ranges.push({
|
|
3125
|
+
fromSec: from - startSec,
|
|
3126
|
+
toSec: to - startSec
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
if (ranges.length === 0) return void 0;
|
|
3130
|
+
return {
|
|
3131
|
+
everyMs: dense.everyMs,
|
|
3132
|
+
ranges
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
//#endregion
|
|
3050
3136
|
//#region src/recorder/addon/export-store.ts
|
|
3051
3137
|
/**
|
|
3052
3138
|
* Recording-export persistence for the recorder addon.
|
|
@@ -3310,15 +3396,15 @@ function expectedOutSeconds(rec) {
|
|
|
3310
3396
|
var ExportEngine = class {
|
|
3311
3397
|
deps;
|
|
3312
3398
|
queue = [];
|
|
3313
|
-
|
|
3314
|
-
|
|
3399
|
+
/** In-flight jobs → their current ffmpeg (null between the two timelapse passes). */
|
|
3400
|
+
active = /* @__PURE__ */ new Map();
|
|
3315
3401
|
cancelled = /* @__PURE__ */ new Set();
|
|
3316
3402
|
constructor(deps) {
|
|
3317
3403
|
this.deps = deps;
|
|
3318
3404
|
}
|
|
3319
3405
|
/** Queue a persisted `queued` record for rendering. */
|
|
3320
3406
|
enqueue(id) {
|
|
3321
|
-
if (this.queue.includes(id) || this.
|
|
3407
|
+
if (this.queue.includes(id) || this.active.has(id)) return;
|
|
3322
3408
|
this.queue.push(id);
|
|
3323
3409
|
this.pump();
|
|
3324
3410
|
}
|
|
@@ -3327,29 +3413,32 @@ var ExportEngine = class {
|
|
|
3327
3413
|
this.cancelled.add(id);
|
|
3328
3414
|
const idx = this.queue.indexOf(id);
|
|
3329
3415
|
if (idx >= 0) this.queue.splice(idx, 1);
|
|
3330
|
-
|
|
3331
|
-
|
|
3416
|
+
const proc = this.active.get(id);
|
|
3417
|
+
if (proc) try {
|
|
3418
|
+
proc.kill("SIGKILL");
|
|
3332
3419
|
} catch {}
|
|
3333
3420
|
else if (idx >= 0) await this.fail(id, "cancelled");
|
|
3334
3421
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3422
|
+
pump() {
|
|
3423
|
+
while (this.active.size < 2 && this.queue.length > 0) {
|
|
3424
|
+
const id = this.queue.shift();
|
|
3425
|
+
if (!id) return;
|
|
3426
|
+
if (this.cancelled.has(id)) {
|
|
3427
|
+
this.cancelled.delete(id);
|
|
3428
|
+
this.fail(id, "cancelled");
|
|
3429
|
+
continue;
|
|
3430
|
+
}
|
|
3431
|
+
this.active.set(id, null);
|
|
3432
|
+
this.runOne(id);
|
|
3344
3433
|
}
|
|
3345
|
-
|
|
3434
|
+
}
|
|
3435
|
+
async runOne(id) {
|
|
3346
3436
|
try {
|
|
3347
3437
|
await this.render(id);
|
|
3348
3438
|
} catch (err) {
|
|
3349
3439
|
await this.fail(id, err instanceof Error ? err.message : String(err));
|
|
3350
3440
|
} finally {
|
|
3351
|
-
this.
|
|
3352
|
-
this.activeProc = null;
|
|
3441
|
+
this.active.delete(id);
|
|
3353
3442
|
this.pump();
|
|
3354
3443
|
}
|
|
3355
3444
|
}
|
|
@@ -3367,7 +3456,7 @@ var ExportEngine = class {
|
|
|
3367
3456
|
try {
|
|
3368
3457
|
await this.spawnRender(id, rec, playlist, this.streamTimeOptions(rec, playlist.segments));
|
|
3369
3458
|
} finally {
|
|
3370
|
-
if (this.deps.cleanupPlaylist) for (const file of [playlist.path, playlist.concatPath]) try {
|
|
3459
|
+
if (this.deps.cleanupPlaylist) for (const file of [playlist.path, playlist.concatPath].filter((p) => typeof p === "string" && p.length > 0)) try {
|
|
3371
3460
|
await this.deps.cleanupPlaylist(file);
|
|
3372
3461
|
} catch (err) {
|
|
3373
3462
|
this.deps.logger.warn("export: source playlist cleanup failed", {
|
|
@@ -3430,7 +3519,7 @@ var ExportEngine = class {
|
|
|
3430
3519
|
}
|
|
3431
3520
|
});
|
|
3432
3521
|
if (options.timelapse) {
|
|
3433
|
-
await this.spawnTimelapse(id, rec, playlist
|
|
3522
|
+
await this.spawnTimelapse(id, rec, playlist, outPath, options);
|
|
3434
3523
|
return;
|
|
3435
3524
|
}
|
|
3436
3525
|
const args = buildExportArgs({
|
|
@@ -3454,8 +3543,118 @@ var ExportEngine = class {
|
|
|
3454
3543
|
* timestamp line runs on the SAME clock the dense map summed — see
|
|
3455
3544
|
* `export-ffmpeg-args.ts` for what the HLS demuxer's content-stitching cost.
|
|
3456
3545
|
*/
|
|
3457
|
-
async spawnTimelapse(id, rec,
|
|
3546
|
+
async spawnTimelapse(id, rec, playlist, outPath, options) {
|
|
3547
|
+
const chunks = splitTimelapseChunks(playlist.concatEntries ?? []);
|
|
3548
|
+
if (chunks.length <= 1) {
|
|
3549
|
+
const concatList = playlist.concatPath;
|
|
3550
|
+
if (concatList === void 0) {
|
|
3551
|
+
await this.fail(id, "no concat list for timelapse");
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
await this.spawnTimelapsePair(id, rec, concatList, outPath, options, {
|
|
3555
|
+
floor: 0,
|
|
3556
|
+
ceil: 99
|
|
3557
|
+
});
|
|
3558
|
+
return;
|
|
3559
|
+
}
|
|
3560
|
+
if (this.deps.writeTextFile === void 0) {
|
|
3561
|
+
const concatList = playlist.concatPath;
|
|
3562
|
+
if (concatList === void 0) {
|
|
3563
|
+
await this.fail(id, "no concat list for timelapse");
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
await this.spawnTimelapsePair(id, rec, concatList, outPath, options, {
|
|
3567
|
+
floor: 0,
|
|
3568
|
+
ceil: 99
|
|
3569
|
+
});
|
|
3570
|
+
return;
|
|
3571
|
+
}
|
|
3572
|
+
const survivors = [];
|
|
3573
|
+
const sidecars = [];
|
|
3574
|
+
try {
|
|
3575
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
3576
|
+
if (this.cancelled.has(id)) {
|
|
3577
|
+
await this.fail(id, "cancelled");
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3580
|
+
const chunk = chunks[i];
|
|
3581
|
+
const chunkDur = chunk.entries.reduce((s, e) => s + e.durMs, 0);
|
|
3582
|
+
const listPath = `${outPath}.chunk${i}.ffconcat`;
|
|
3583
|
+
const concatPath = `${outPath}.chunk${i}.concat.ts`;
|
|
3584
|
+
const chunkMp4 = `${outPath}.chunk${i}.mp4`;
|
|
3585
|
+
sidecars.push(listPath, concatPath, chunkMp4);
|
|
3586
|
+
await this.deps.writeTextFile(listPath, buildExportConcatList(chunk.entries));
|
|
3587
|
+
const span = 90 / chunks.length;
|
|
3588
|
+
const floor = Math.round(i * span);
|
|
3589
|
+
if (await this.spawnTimelapsePair(id, rec, listPath, chunkMp4, {
|
|
3590
|
+
...options,
|
|
3591
|
+
timelapse: options.timelapse ? {
|
|
3592
|
+
...options.timelapse,
|
|
3593
|
+
dense: denseRangesForChunk(options.timelapse.dense, chunk.streamStartMs, chunkDur)
|
|
3594
|
+
} : options.timelapse
|
|
3595
|
+
}, {
|
|
3596
|
+
floor,
|
|
3597
|
+
ceil: floor + Math.round(span * .95),
|
|
3598
|
+
finish: false
|
|
3599
|
+
}) === "ok") survivors.push(chunkMp4);
|
|
3600
|
+
else this.deps.logger.warn("export timelapse hour skipped", {
|
|
3601
|
+
tags: { deviceId: rec.deviceId },
|
|
3602
|
+
meta: {
|
|
3603
|
+
exportId: id,
|
|
3604
|
+
chunk: i,
|
|
3605
|
+
streamStartMs: chunk.streamStartMs
|
|
3606
|
+
}
|
|
3607
|
+
});
|
|
3608
|
+
}
|
|
3609
|
+
if (survivors.length === 0) {
|
|
3610
|
+
await this.fail(id, "every timelapse hour failed");
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
if (survivors.length === 1) {
|
|
3614
|
+
const only = survivors[0];
|
|
3615
|
+
const copyArgs = buildExportArgs({
|
|
3616
|
+
inputPlaylist: only,
|
|
3617
|
+
outPath,
|
|
3618
|
+
options: {
|
|
3619
|
+
includeAudio: false,
|
|
3620
|
+
maxLifeMs: 1,
|
|
3621
|
+
deleteAfterDownload: false,
|
|
3622
|
+
speed: 1
|
|
3623
|
+
}
|
|
3624
|
+
});
|
|
3625
|
+
const copied = await this.runFfmpeg(id, rec.deviceId, copyArgs, {
|
|
3626
|
+
totalSec: expectedOutSeconds(rec),
|
|
3627
|
+
floor: 95,
|
|
3628
|
+
ceil: 99
|
|
3629
|
+
});
|
|
3630
|
+
await this.finish(id, copied.code, outPath, copied.stderrTail);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
const assembleList = `${outPath}.assemble.ffconcat`;
|
|
3634
|
+
sidecars.push(assembleList);
|
|
3635
|
+
await this.deps.writeTextFile(assembleList, buildAssembleConcatList(survivors));
|
|
3636
|
+
const assembled = await this.runFfmpeg(id, rec.deviceId, buildAssembleArgs({
|
|
3637
|
+
inputConcatList: assembleList,
|
|
3638
|
+
outPath
|
|
3639
|
+
}), {
|
|
3640
|
+
totalSec: expectedOutSeconds(rec),
|
|
3641
|
+
floor: 95,
|
|
3642
|
+
ceil: 99
|
|
3643
|
+
});
|
|
3644
|
+
await this.finish(id, assembled.code, outPath, assembled.stderrTail);
|
|
3645
|
+
} finally {
|
|
3646
|
+
for (const file of sidecars) try {
|
|
3647
|
+
await this.deps.removeFile(file);
|
|
3648
|
+
} catch {}
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
/**
|
|
3652
|
+
* Concat remux then select. `finish: false` lets the chunk walker skip a
|
|
3653
|
+
* dead hour without failing the whole night.
|
|
3654
|
+
*/
|
|
3655
|
+
async spawnTimelapsePair(id, rec, concatList, outPath, options, progress) {
|
|
3458
3656
|
const concatPath = concatPassOutPath(outPath);
|
|
3657
|
+
const mid = progress.floor + Math.round((progress.ceil - progress.floor) / 2);
|
|
3459
3658
|
try {
|
|
3460
3659
|
const concatArgs = buildConcatPassArgs({
|
|
3461
3660
|
inputConcatList: concatList,
|
|
@@ -3463,12 +3662,12 @@ var ExportEngine = class {
|
|
|
3463
3662
|
});
|
|
3464
3663
|
const concatRan = await this.runFfmpeg(id, rec.deviceId, concatArgs, {
|
|
3465
3664
|
totalSec: Math.max(.001, (rec.toMs - rec.fromMs) / 1e3),
|
|
3466
|
-
floor:
|
|
3467
|
-
ceil:
|
|
3665
|
+
floor: progress.floor,
|
|
3666
|
+
ceil: mid
|
|
3468
3667
|
});
|
|
3469
3668
|
if (this.cancelled.has(id) || concatRan.code !== 0) {
|
|
3470
|
-
await this.finish(id, concatRan.code, concatPath, concatRan.stderrTail);
|
|
3471
|
-
return;
|
|
3669
|
+
if (progress.finish !== false) await this.finish(id, concatRan.code, concatPath, concatRan.stderrTail);
|
|
3670
|
+
return "fail";
|
|
3472
3671
|
}
|
|
3473
3672
|
const selectArgs = buildExportArgs({
|
|
3474
3673
|
inputPlaylist: concatPath,
|
|
@@ -3477,10 +3676,11 @@ var ExportEngine = class {
|
|
|
3477
3676
|
});
|
|
3478
3677
|
const selectRan = await this.runFfmpeg(id, rec.deviceId, selectArgs, {
|
|
3479
3678
|
totalSec: expectedOutSeconds(rec),
|
|
3480
|
-
floor:
|
|
3481
|
-
ceil:
|
|
3679
|
+
floor: mid,
|
|
3680
|
+
ceil: progress.ceil
|
|
3482
3681
|
});
|
|
3483
|
-
await this.finish(id, selectRan.code, outPath, selectRan.stderrTail);
|
|
3682
|
+
if (progress.finish !== false) await this.finish(id, selectRan.code, outPath, selectRan.stderrTail);
|
|
3683
|
+
return selectRan.code === 0 ? "ok" : "fail";
|
|
3484
3684
|
} finally {
|
|
3485
3685
|
try {
|
|
3486
3686
|
await this.deps.removeFile(concatPath);
|
|
@@ -3492,10 +3692,28 @@ var ExportEngine = class {
|
|
|
3492
3692
|
exportId: id,
|
|
3493
3693
|
args
|
|
3494
3694
|
} });
|
|
3695
|
+
const stallMs = this.deps.stallMs ?? 9e4;
|
|
3495
3696
|
return new Promise((resolve) => {
|
|
3496
3697
|
const proc = this.deps.spawn(this.deps.ffmpegPath ?? "ffmpeg", args);
|
|
3497
|
-
this.
|
|
3698
|
+
this.active.set(id, proc);
|
|
3498
3699
|
const stderrTail = [];
|
|
3700
|
+
let stallTimer = null;
|
|
3701
|
+
const clearStall = () => {
|
|
3702
|
+
if (stallTimer !== null) {
|
|
3703
|
+
clearTimeout(stallTimer);
|
|
3704
|
+
stallTimer = null;
|
|
3705
|
+
}
|
|
3706
|
+
};
|
|
3707
|
+
const armStall = () => {
|
|
3708
|
+
clearStall();
|
|
3709
|
+
stallTimer = setTimeout(() => {
|
|
3710
|
+
stderrTail.push(`ffmpeg stalled: no progress for ${stallMs}ms`);
|
|
3711
|
+
try {
|
|
3712
|
+
proc.kill("SIGKILL");
|
|
3713
|
+
} catch {}
|
|
3714
|
+
}, stallMs);
|
|
3715
|
+
};
|
|
3716
|
+
armStall();
|
|
3499
3717
|
proc.stderr?.on("data", (chunk) => {
|
|
3500
3718
|
const text = chunk instanceof Buffer ? chunk.toString("utf8") : String(chunk);
|
|
3501
3719
|
for (const line of text.split("\n")) {
|
|
@@ -3506,6 +3724,7 @@ var ExportEngine = class {
|
|
|
3506
3724
|
}
|
|
3507
3725
|
const m = TIME_RE.exec(line);
|
|
3508
3726
|
if (m) {
|
|
3727
|
+
armStall();
|
|
3509
3728
|
const sec = Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]);
|
|
3510
3729
|
const span = progress.ceil - progress.floor;
|
|
3511
3730
|
const pct = Math.min(progress.ceil, progress.floor + Math.round(sec / progress.totalSec * span));
|
|
@@ -3518,14 +3737,16 @@ var ExportEngine = class {
|
|
|
3518
3737
|
}
|
|
3519
3738
|
});
|
|
3520
3739
|
proc.on("exit", (code) => {
|
|
3521
|
-
|
|
3740
|
+
clearStall();
|
|
3741
|
+
this.active.set(id, null);
|
|
3522
3742
|
resolve({
|
|
3523
3743
|
code: typeof code === "number" ? code : null,
|
|
3524
3744
|
stderrTail
|
|
3525
3745
|
});
|
|
3526
3746
|
});
|
|
3527
3747
|
proc.on("error", (raw) => {
|
|
3528
|
-
|
|
3748
|
+
clearStall();
|
|
3749
|
+
this.active.set(id, null);
|
|
3529
3750
|
const failure = readSpawnFailure(raw);
|
|
3530
3751
|
this.deps.logger.error("export ffmpeg could not be spawned", {
|
|
3531
3752
|
tags: { deviceId },
|
|
@@ -4810,6 +5031,10 @@ async function writeExportSourcePlaylist(input) {
|
|
|
4810
5031
|
segments: segments.map((s) => ({
|
|
4811
5032
|
startMs: s.startMs,
|
|
4812
5033
|
durMs: s.durMs
|
|
5034
|
+
})),
|
|
5035
|
+
concatEntries: segments.map((s) => ({
|
|
5036
|
+
uri: s.uri,
|
|
5037
|
+
durMs: s.durMs
|
|
4813
5038
|
}))
|
|
4814
5039
|
};
|
|
4815
5040
|
}
|
|
@@ -10763,6 +10988,10 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
10763
10988
|
});
|
|
10764
10989
|
},
|
|
10765
10990
|
cleanupPlaylist: removeExportFile,
|
|
10991
|
+
writeTextFile: async (p, contents) => {
|
|
10992
|
+
await promises.mkdir(path.dirname(p), { recursive: true });
|
|
10993
|
+
await promises.writeFile(p, contents, "utf8");
|
|
10994
|
+
},
|
|
10766
10995
|
outPath: exportOutPath,
|
|
10767
10996
|
statBytes: async (p) => (await promises.stat(p)).size,
|
|
10768
10997
|
removeFile: removeExportFile,
|
|
@@ -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-C1tr8WP3.js");
|
|
3
|
+
const require_lazy_sharp = require("../lazy-sharp-LSlEXQe_.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.
|
|
@@ -874,6 +874,15 @@ var NativeTileStore = class {
|
|
|
874
874
|
};
|
|
875
875
|
//#endregion
|
|
876
876
|
//#region src/session-decode/planar-crop-source.ts
|
|
877
|
+
/** Layout a detection `toBuffer` can read, or `null` when it must name the
|
|
878
|
+
* format and refuse. RGB24 is the VAAPI `pinnedRgbCrop` lease; NV12 is the
|
|
879
|
+
* videotoolbox download. The old path only accepted 3-plane YUV420P. */
|
|
880
|
+
function softwareRoiKind(format, C) {
|
|
881
|
+
if (format === C.AV_PIX_FMT_RGB24) return "rgb24";
|
|
882
|
+
if (format === C.AV_PIX_FMT_YUV420P) return "yuv420p";
|
|
883
|
+
if (format === C.AV_PIX_FMT_NV12) return "nv12";
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
877
886
|
/** How many planes each layout must present before the offsets mean anything. */
|
|
878
887
|
var PLANE_COUNT = {
|
|
879
888
|
yuv420p: 3,
|
|
@@ -2600,29 +2609,28 @@ var DecodeWorkerChild = class {
|
|
|
2600
2609
|
const dstFmt = format === "gray" ? C.AV_PIX_FMT_GRAY8 : C.AV_PIX_FMT_RGB24;
|
|
2601
2610
|
const channels = format === "gray" ? 1 : 3;
|
|
2602
2611
|
this.logColorProvenance(isFullFrameRegion(region, frame.width, frame.height) ? "sw-fullframe" : "sw-crop", this.resolveFrameColor(frame));
|
|
2603
|
-
const
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
const
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2612
|
+
const kind = softwareRoiKind(frame.format, C);
|
|
2613
|
+
if (kind === null) throw new Error(`decode-worker-child: decoded frame in unsupported pixel format ${frame.format} (planes=${frame.data?.length ?? 0}, hw=${frame.isHwFrame()})`);
|
|
2614
|
+
if (kind === "rgb24" && format === "rgb") return this.nativeRgbCropToBuffer(nav, C, frame, {
|
|
2615
|
+
region,
|
|
2616
|
+
target
|
|
2617
|
+
});
|
|
2618
|
+
const source = kind === "rgb24" ? (() => {
|
|
2619
|
+
const plane = frame.data?.[0];
|
|
2620
|
+
const stride = frame.linesize[0] ?? 0;
|
|
2621
|
+
if (!plane || stride <= 0) return null;
|
|
2622
|
+
const rgb = resolveRgbCropSource(plane, stride, region);
|
|
2623
|
+
return {
|
|
2624
|
+
planes: [rgb.src],
|
|
2625
|
+
strides: [rgb.stride]
|
|
2626
|
+
};
|
|
2627
|
+
})() : resolvePlanarCropSource(kind, frame.data, frame.linesize, region);
|
|
2628
|
+
if (!source) throw new Error(`decode-worker-child: retained ${kind} frame missing plane data for ${region.width}x${region.height}+${region.left}+${region.top} (planes=${frame.data?.length ?? 0})`);
|
|
2629
|
+
const scaler = this.ensureScaler(nav, C, region.width, region.height, target.width, target.height, dstFmt, kind);
|
|
2615
2630
|
const dstStride = target.width * channels;
|
|
2616
2631
|
const dstBuffer = Buffer.allocUnsafe(dstStride * target.height + 64);
|
|
2617
|
-
const
|
|
2618
|
-
|
|
2619
|
-
uSlice,
|
|
2620
|
-
vSlice
|
|
2621
|
-
], [
|
|
2622
|
-
yStride,
|
|
2623
|
-
cStride,
|
|
2624
|
-
cStride
|
|
2625
|
-
], 0, region.height, [dstBuffer], [dstStride]);
|
|
2632
|
+
const srcPlanes = source.planes.map((plane) => Buffer.from(plane.buffer, plane.byteOffset, plane.byteLength));
|
|
2633
|
+
const scaledHeight = scaler.scaleSync(srcPlanes, [...source.strides], 0, region.height, [dstBuffer], [dstStride]);
|
|
2626
2634
|
if (scaledHeight < 0) throw new Error(`sws_scale failed: ${scaledHeight}`);
|
|
2627
2635
|
if (scaledHeight !== target.height) throw new Error(`scaler produced ${scaledHeight} of ${target.height} rows`);
|
|
2628
2636
|
return dstBuffer.subarray(0, dstStride * target.height);
|
|
@@ -2635,15 +2643,15 @@ var DecodeWorkerChild = class {
|
|
|
2635
2643
|
* that leaked native libav memory at frame rate. Synchronous + single-thread
|
|
2636
2644
|
* (Node), so no epoch/race guard is needed (unlike the async shared session).
|
|
2637
2645
|
*/
|
|
2638
|
-
ensureScaler(nav, C, srcW, srcH, dstW, dstH, dstFmt) {
|
|
2639
|
-
const key = `${srcW}x${srcH}->${dstW}x${dstH}:${dstFmt}`;
|
|
2646
|
+
ensureScaler(nav, C, srcW, srcH, dstW, dstH, dstFmt, srcKind = "yuv420p") {
|
|
2647
|
+
const key = `${srcKind}:${srcW}x${srcH}->${dstW}x${dstH}:${dstFmt}`;
|
|
2640
2648
|
const cached = this.scaler;
|
|
2641
2649
|
if (cached && key === this.scalerKey) return cached;
|
|
2642
2650
|
this.scaler?.[Symbol.dispose]?.();
|
|
2643
2651
|
this.scaler = null;
|
|
2644
2652
|
this.scalerKey = "";
|
|
2645
2653
|
const scaler = new nav.SoftwareScaleContext();
|
|
2646
|
-
scaler.getContext(srcW, srcH, C
|
|
2654
|
+
scaler.getContext(srcW, srcH, navPixelFormatFor(srcKind, C), dstW, dstH, dstFmt, C.SWS_FAST_BILINEAR);
|
|
2647
2655
|
const initRet = scaler.initContext();
|
|
2648
2656
|
if (initRet < 0) {
|
|
2649
2657
|
scaler[Symbol.dispose]?.();
|
|
@@ -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-_zMPqSas.mjs";
|
|
2
2
|
import { n as setSharpWarnSink, r as hostExternalEntryUrls, t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
|
|
3
3
|
//#region src/session-decode/color-conversion.ts
|
|
4
4
|
/**
|
|
@@ -873,6 +873,15 @@ var NativeTileStore = class {
|
|
|
873
873
|
};
|
|
874
874
|
//#endregion
|
|
875
875
|
//#region src/session-decode/planar-crop-source.ts
|
|
876
|
+
/** Layout a detection `toBuffer` can read, or `null` when it must name the
|
|
877
|
+
* format and refuse. RGB24 is the VAAPI `pinnedRgbCrop` lease; NV12 is the
|
|
878
|
+
* videotoolbox download. The old path only accepted 3-plane YUV420P. */
|
|
879
|
+
function softwareRoiKind(format, C) {
|
|
880
|
+
if (format === C.AV_PIX_FMT_RGB24) return "rgb24";
|
|
881
|
+
if (format === C.AV_PIX_FMT_YUV420P) return "yuv420p";
|
|
882
|
+
if (format === C.AV_PIX_FMT_NV12) return "nv12";
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
876
885
|
/** How many planes each layout must present before the offsets mean anything. */
|
|
877
886
|
var PLANE_COUNT = {
|
|
878
887
|
yuv420p: 3,
|
|
@@ -2599,29 +2608,28 @@ var DecodeWorkerChild = class {
|
|
|
2599
2608
|
const dstFmt = format === "gray" ? C.AV_PIX_FMT_GRAY8 : C.AV_PIX_FMT_RGB24;
|
|
2600
2609
|
const channels = format === "gray" ? 1 : 3;
|
|
2601
2610
|
this.logColorProvenance(isFullFrameRegion(region, frame.width, frame.height) ? "sw-fullframe" : "sw-crop", this.resolveFrameColor(frame));
|
|
2602
|
-
const
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
const
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2611
|
+
const kind = softwareRoiKind(frame.format, C);
|
|
2612
|
+
if (kind === null) throw new Error(`decode-worker-child: decoded frame in unsupported pixel format ${frame.format} (planes=${frame.data?.length ?? 0}, hw=${frame.isHwFrame()})`);
|
|
2613
|
+
if (kind === "rgb24" && format === "rgb") return this.nativeRgbCropToBuffer(nav, C, frame, {
|
|
2614
|
+
region,
|
|
2615
|
+
target
|
|
2616
|
+
});
|
|
2617
|
+
const source = kind === "rgb24" ? (() => {
|
|
2618
|
+
const plane = frame.data?.[0];
|
|
2619
|
+
const stride = frame.linesize[0] ?? 0;
|
|
2620
|
+
if (!plane || stride <= 0) return null;
|
|
2621
|
+
const rgb = resolveRgbCropSource(plane, stride, region);
|
|
2622
|
+
return {
|
|
2623
|
+
planes: [rgb.src],
|
|
2624
|
+
strides: [rgb.stride]
|
|
2625
|
+
};
|
|
2626
|
+
})() : resolvePlanarCropSource(kind, frame.data, frame.linesize, region);
|
|
2627
|
+
if (!source) throw new Error(`decode-worker-child: retained ${kind} frame missing plane data for ${region.width}x${region.height}+${region.left}+${region.top} (planes=${frame.data?.length ?? 0})`);
|
|
2628
|
+
const scaler = this.ensureScaler(nav, C, region.width, region.height, target.width, target.height, dstFmt, kind);
|
|
2614
2629
|
const dstStride = target.width * channels;
|
|
2615
2630
|
const dstBuffer = Buffer.allocUnsafe(dstStride * target.height + 64);
|
|
2616
|
-
const
|
|
2617
|
-
|
|
2618
|
-
uSlice,
|
|
2619
|
-
vSlice
|
|
2620
|
-
], [
|
|
2621
|
-
yStride,
|
|
2622
|
-
cStride,
|
|
2623
|
-
cStride
|
|
2624
|
-
], 0, region.height, [dstBuffer], [dstStride]);
|
|
2631
|
+
const srcPlanes = source.planes.map((plane) => Buffer.from(plane.buffer, plane.byteOffset, plane.byteLength));
|
|
2632
|
+
const scaledHeight = scaler.scaleSync(srcPlanes, [...source.strides], 0, region.height, [dstBuffer], [dstStride]);
|
|
2625
2633
|
if (scaledHeight < 0) throw new Error(`sws_scale failed: ${scaledHeight}`);
|
|
2626
2634
|
if (scaledHeight !== target.height) throw new Error(`scaler produced ${scaledHeight} of ${target.height} rows`);
|
|
2627
2635
|
return dstBuffer.subarray(0, dstStride * target.height);
|
|
@@ -2634,15 +2642,15 @@ var DecodeWorkerChild = class {
|
|
|
2634
2642
|
* that leaked native libav memory at frame rate. Synchronous + single-thread
|
|
2635
2643
|
* (Node), so no epoch/race guard is needed (unlike the async shared session).
|
|
2636
2644
|
*/
|
|
2637
|
-
ensureScaler(nav, C, srcW, srcH, dstW, dstH, dstFmt) {
|
|
2638
|
-
const key = `${srcW}x${srcH}->${dstW}x${dstH}:${dstFmt}`;
|
|
2645
|
+
ensureScaler(nav, C, srcW, srcH, dstW, dstH, dstFmt, srcKind = "yuv420p") {
|
|
2646
|
+
const key = `${srcKind}:${srcW}x${srcH}->${dstW}x${dstH}:${dstFmt}`;
|
|
2639
2647
|
const cached = this.scaler;
|
|
2640
2648
|
if (cached && key === this.scalerKey) return cached;
|
|
2641
2649
|
this.scaler?.[Symbol.dispose]?.();
|
|
2642
2650
|
this.scaler = null;
|
|
2643
2651
|
this.scalerKey = "";
|
|
2644
2652
|
const scaler = new nav.SoftwareScaleContext();
|
|
2645
|
-
scaler.getContext(srcW, srcH, C
|
|
2653
|
+
scaler.getContext(srcW, srcH, navPixelFormatFor(srcKind, C), dstW, dstH, dstFmt, C.SWS_FAST_BILINEAR);
|
|
2646
2654
|
const initRet = scaler.initContext();
|
|
2647
2655
|
if (initRet < 0) {
|
|
2648
2656
|
scaler[Symbol.dispose]?.();
|
|
@@ -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-D-NFPV_g.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-BiNja8Hw.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) => {
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.101",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_stream_broker_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.69",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|