@camstack/addon-pipeline 1.2.307 → 1.2.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-analyzer/index.js +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-DgdiQtQb.js → default-detection-model-2rHeRf0Y.js} +1 -1
- package/dist/{default-detection-model-BV9bIuOo.mjs → default-detection-model-DATyir0t.mjs} +1 -1
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-CyJWk-_f.js → dist-CdRsXAov.js} +79 -1
- package/dist/{dist-DdVMFlJ6.mjs → dist-jy-7xoW5.mjs} +74 -2
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-COu_tHy4.mjs → node-NCge1qZM.mjs} +1 -53
- package/dist/{node-HsWG5fGp.js → node-pWHpwpBa.js} +1 -53
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-Bz3oIMzl.mjs → process-memory-4K7QaQab.mjs} +1 -1
- package/dist/{process-memory-TQXG7qrJ.js → process-memory-C4ayPnww.js} +1 -1
- package/dist/recorder/index.js +82 -15
- package/dist/recorder/index.mjs +82 -15
- package/dist/{segment-demux-js-BrKa7tpr.js → segment-demux-js-0bM2Anej.js} +22 -4
- package/dist/{segment-demux-js-CEJn4BR0.mjs → segment-demux-js-BaI2VdPX.mjs} +17 -5
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DdYUVQSA.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CkxD__Uy.mjs} +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DEPTgXS_.mjs +26 -0
- 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-D--a9rVM.mjs → hostInit-D56Gr-hD.mjs} +1 -1
- package/dist/stream-broker/index.js +305 -95
- package/dist/stream-broker/index.mjs +305 -95
- package/dist/stream-broker/remoteEntry.js +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-DPHv0v3Q.mjs +0 -26
package/dist/recorder/index.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
6
|
+
const require_dist = require("../dist-CdRsXAov.js");
|
|
7
|
+
const require_node = require("../node-pWHpwpBa.js");
|
|
8
8
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
9
9
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
10
10
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
|
@@ -3450,7 +3450,20 @@ var StillFrameService = class {
|
|
|
3450
3450
|
return null;
|
|
3451
3451
|
}
|
|
3452
3452
|
try {
|
|
3453
|
-
|
|
3453
|
+
let bin;
|
|
3454
|
+
try {
|
|
3455
|
+
bin = await this.deps.resolveFfmpeg();
|
|
3456
|
+
} catch (err) {
|
|
3457
|
+
this.deps.logger.error("still: refused — no pinned ffmpeg on this node (D540)", {
|
|
3458
|
+
tags: { deviceId },
|
|
3459
|
+
meta: {
|
|
3460
|
+
epochMs,
|
|
3461
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3462
|
+
}
|
|
3463
|
+
});
|
|
3464
|
+
return null;
|
|
3465
|
+
}
|
|
3466
|
+
const jpeg = await this.decodeFrame(bin, abs, source.offsetSec);
|
|
3454
3467
|
if (jpeg === null || jpeg.byteLength === 0) return null;
|
|
3455
3468
|
return {
|
|
3456
3469
|
jpeg,
|
|
@@ -3461,7 +3474,7 @@ var StillFrameService = class {
|
|
|
3461
3474
|
}
|
|
3462
3475
|
}
|
|
3463
3476
|
/** Run ONE ffmpeg input-seek + single-frame decode; JPEG bytes on stdout. */
|
|
3464
|
-
decodeFrame(absPath, offsetSec) {
|
|
3477
|
+
decodeFrame(bin, absPath, offsetSec) {
|
|
3465
3478
|
const args = [
|
|
3466
3479
|
"-hide_banner",
|
|
3467
3480
|
"-loglevel",
|
|
@@ -3477,7 +3490,6 @@ var StillFrameService = class {
|
|
|
3477
3490
|
"pipe:1"
|
|
3478
3491
|
];
|
|
3479
3492
|
const spawn = this.deps.spawn ?? node_child_process.spawn;
|
|
3480
|
-
const bin = this.deps.ffmpegPath ?? "ffmpeg";
|
|
3481
3493
|
return new Promise((resolve) => {
|
|
3482
3494
|
let child;
|
|
3483
3495
|
try {
|
|
@@ -7549,15 +7561,32 @@ var ExportEngine = class {
|
|
|
7549
7561
|
} catch {}
|
|
7550
7562
|
}
|
|
7551
7563
|
}
|
|
7552
|
-
runFfmpeg(id, deviceId, args, progress) {
|
|
7564
|
+
async runFfmpeg(id, deviceId, args, progress) {
|
|
7565
|
+
let ffmpegPath;
|
|
7566
|
+
try {
|
|
7567
|
+
ffmpegPath = await this.deps.resolveFfmpeg();
|
|
7568
|
+
} catch (err) {
|
|
7569
|
+
const reason = `no pinned ffmpeg on this node (D540): ${err instanceof Error ? err.message : String(err)}`;
|
|
7570
|
+
this.deps.logger.error("export render refused — no pinned ffmpeg on this node (D540)", {
|
|
7571
|
+
tags: { deviceId },
|
|
7572
|
+
meta: {
|
|
7573
|
+
exportId: id,
|
|
7574
|
+
error: reason
|
|
7575
|
+
}
|
|
7576
|
+
});
|
|
7577
|
+
return {
|
|
7578
|
+
code: null,
|
|
7579
|
+
stderrTail: [reason]
|
|
7580
|
+
};
|
|
7581
|
+
}
|
|
7553
7582
|
this.deps.logger.debug("export ffmpeg argv", { meta: {
|
|
7554
7583
|
exportId: id,
|
|
7555
7584
|
args
|
|
7556
7585
|
} });
|
|
7557
7586
|
const stallMs = this.deps.stallMs ?? 9e4;
|
|
7558
7587
|
const faststartStallMs = this.deps.faststartStallMs ?? 9e5;
|
|
7559
|
-
return new Promise((resolve) => {
|
|
7560
|
-
const proc = this.deps.spawn(
|
|
7588
|
+
return await new Promise((resolve) => {
|
|
7589
|
+
const proc = this.deps.spawn(ffmpegPath, args);
|
|
7561
7590
|
this.active.set(id, proc);
|
|
7562
7591
|
const stderrTail = [];
|
|
7563
7592
|
let stallTimer = null;
|
|
@@ -9459,6 +9488,17 @@ async function renderFootage(renderDeps, input, ext, buildArgs) {
|
|
|
9459
9488
|
const toMs = input.aroundMs + input.postRollSec * 1e3;
|
|
9460
9489
|
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || toMs <= fromMs) throw new Error(`invalid ${ext} window [${fromMs}, ${toMs}) for device ${input.deviceId} — aroundMs=${input.aroundMs} preRollSec=${input.preRollSec} postRollSec=${input.postRollSec}`);
|
|
9461
9490
|
const renderId = `${ext}-${now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
9491
|
+
let ffmpegPath;
|
|
9492
|
+
try {
|
|
9493
|
+
ffmpegPath = await renderDeps.resolveFfmpeg();
|
|
9494
|
+
} catch (err) {
|
|
9495
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
9496
|
+
renderDeps.deps.logger.error(`${ext} render refused — no pinned ffmpeg on this node (D540)`, {
|
|
9497
|
+
tags: { deviceId: input.deviceId },
|
|
9498
|
+
meta: { error: reason }
|
|
9499
|
+
});
|
|
9500
|
+
throw new Error(`${ext} render refused for device ${input.deviceId}: no pinned ffmpeg — ${reason}`, { cause: err });
|
|
9501
|
+
}
|
|
9462
9502
|
const availableEnd = latestFinalizedEnd(renderDeps, input.deviceId);
|
|
9463
9503
|
if (availableEnd === null || availableEnd < toMs) throw new Error(`${FOOTAGE_NOT_READY}: [${fromMs}, ${toMs}) for device ${input.deviceId} — finalized footage ends at ${availableEnd ?? "never"}`);
|
|
9464
9504
|
const window = {
|
|
@@ -9477,7 +9517,7 @@ async function renderFootage(renderDeps, input, ext, buildArgs) {
|
|
|
9477
9517
|
if (playlist === null) throw new Error(`no footage covers [${window.fromMs}, ${window.toMs}) for device ${input.deviceId}`);
|
|
9478
9518
|
const outPath = node_path.default.join(node_path.default.dirname(playlist.path), `${renderId}.${ext}`);
|
|
9479
9519
|
try {
|
|
9480
|
-
await runFfmpeg(renderDeps, buildArgs(playlist.path, outPath), ext);
|
|
9520
|
+
await runFfmpeg(renderDeps, ffmpegPath, buildArgs(playlist.path, outPath), ext);
|
|
9481
9521
|
const bytes = await node_fs.promises.readFile(outPath);
|
|
9482
9522
|
if (bytes.byteLength === 0) throw new Error(`${ext} render produced an empty file`);
|
|
9483
9523
|
return {
|
|
@@ -9514,9 +9554,9 @@ function latestFinalizedEnd(renderDeps, deviceId) {
|
|
|
9514
9554
|
* throws ESRCH, which is swallowed, so the cost of being wrong is nothing while
|
|
9515
9555
|
* the cost of NOT killing is a zombie per render.
|
|
9516
9556
|
*/
|
|
9517
|
-
function runFfmpeg(renderDeps, args, ext) {
|
|
9557
|
+
function runFfmpeg(renderDeps, ffmpegPath, args, ext) {
|
|
9518
9558
|
return new Promise((resolve, reject) => {
|
|
9519
|
-
const proc = renderDeps.spawn(
|
|
9559
|
+
const proc = renderDeps.spawn(ffmpegPath, args);
|
|
9520
9560
|
let done = false;
|
|
9521
9561
|
let timer = null;
|
|
9522
9562
|
const settle = (err) => {
|
|
@@ -15748,7 +15788,7 @@ var SegmentWriter = class {
|
|
|
15748
15788
|
start() {
|
|
15749
15789
|
if (this.stopped || this.proc) return;
|
|
15750
15790
|
const args = buildPassthroughArgs(this.cfg);
|
|
15751
|
-
const proc = this.deps.spawn(this.deps.ffmpegPath
|
|
15791
|
+
const proc = this.deps.spawn(this.deps.ffmpegPath, args);
|
|
15752
15792
|
this.proc = proc;
|
|
15753
15793
|
this.exitWait = new Promise((resolve) => {
|
|
15754
15794
|
this.resolveExit = resolve;
|
|
@@ -17495,6 +17535,19 @@ var RecordingController = class {
|
|
|
17495
17535
|
*/
|
|
17496
17536
|
async attachProfile(deviceId, profile, segmentSeconds, audio) {
|
|
17497
17537
|
const attachProfileStartMs = this.now();
|
|
17538
|
+
let ffmpegPath;
|
|
17539
|
+
try {
|
|
17540
|
+
ffmpegPath = await this.deps.resolveFfmpeg();
|
|
17541
|
+
} catch (err) {
|
|
17542
|
+
this.deps.logger.error("recorder: writer refused — no pinned ffmpeg on this node (D540)", {
|
|
17543
|
+
tags: { deviceId },
|
|
17544
|
+
meta: {
|
|
17545
|
+
profile,
|
|
17546
|
+
error: require_dist.errMsg(err)
|
|
17547
|
+
}
|
|
17548
|
+
});
|
|
17549
|
+
throw new Error(`recorder: no pinned ffmpeg for device ${deviceId}/${profile}`);
|
|
17550
|
+
}
|
|
17498
17551
|
const placement = await this.resolvePlacement(deviceId, profile);
|
|
17499
17552
|
const placementDoneMs = this.now();
|
|
17500
17553
|
const outDir = node_path.default.join(placement.root, STAGING_DIR_NAME, String(deviceId), profile);
|
|
@@ -17530,6 +17583,7 @@ var RecordingController = class {
|
|
|
17530
17583
|
consumerTag
|
|
17531
17584
|
}, {
|
|
17532
17585
|
spawn: this.deps.spawn,
|
|
17586
|
+
ffmpegPath,
|
|
17533
17587
|
logger: deviceLog,
|
|
17534
17588
|
deviceId,
|
|
17535
17589
|
claimCost: (pid) => this.deps.costRegistry?.claim({
|
|
@@ -20823,6 +20877,14 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
20823
20877
|
* because those children were this process's children.
|
|
20824
20878
|
*/
|
|
20825
20879
|
costRegistry = new require_node.ChildCostRegistry();
|
|
20880
|
+
/**
|
|
20881
|
+
* This node's PINNED ffmpeg (D540), asked once and shared by every child the
|
|
20882
|
+
* recorder spawns: segment writers, exports, gif/clip renders and stills.
|
|
20883
|
+
* Each of those used to take an optional path that no one passed, and fell
|
|
20884
|
+
* back to a bare `'ffmpeg'` — the HOST's binary. It rejects rather than
|
|
20885
|
+
* naming PATH; each site logs its refusal per camera.
|
|
20886
|
+
*/
|
|
20887
|
+
pinnedFfmpeg = require_dist.memoizePinnedFfmpeg(() => this.ctx.deps.ensureFfmpeg());
|
|
20826
20888
|
/** True when THIS node is the designated recording node (`recordingNodeId`).
|
|
20827
20889
|
* Every other node stays fully inert — registers no providers, serves no
|
|
20828
20890
|
* playback, runs no controller. Resolved in `onInitialize`. */
|
|
@@ -21216,6 +21278,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
21216
21278
|
now: () => Date.now()
|
|
21217
21279
|
});
|
|
21218
21280
|
this.controller = new RecordingController({
|
|
21281
|
+
resolveFfmpeg: this.pinnedFfmpeg,
|
|
21219
21282
|
...this.stagingGuard !== null ? { stagingGuard: this.stagingGuard } : {},
|
|
21220
21283
|
recoverStagingDirectory: (input) => this.recoverReapedDirectory(input),
|
|
21221
21284
|
heapProbe: this.heapProbe,
|
|
@@ -21363,12 +21426,14 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
21363
21426
|
gifRender: (input) => renderGif({
|
|
21364
21427
|
deps: recordingDeps,
|
|
21365
21428
|
exportsDir: this.exportsDirOrThrow(),
|
|
21366
|
-
spawn: node_child_process.spawn
|
|
21429
|
+
spawn: node_child_process.spawn,
|
|
21430
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21367
21431
|
}, input),
|
|
21368
21432
|
clipRender: (input) => renderClip({
|
|
21369
21433
|
deps: recordingDeps,
|
|
21370
21434
|
exportsDir: this.exportsDirOrThrow(),
|
|
21371
|
-
spawn: node_child_process.spawn
|
|
21435
|
+
spawn: node_child_process.spawn,
|
|
21436
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21372
21437
|
}, input),
|
|
21373
21438
|
locations: () => this.resolvedLocations,
|
|
21374
21439
|
refreshLocations: () => this.refreshLocations(),
|
|
@@ -21497,6 +21562,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
21497
21562
|
};
|
|
21498
21563
|
this.exportEngine = new ExportEngine({
|
|
21499
21564
|
spawn: node_child_process.spawn,
|
|
21565
|
+
resolveFfmpeg: this.pinnedFfmpeg,
|
|
21500
21566
|
logger: this.ctx.logger.child("export"),
|
|
21501
21567
|
state: exportsState,
|
|
21502
21568
|
resolvePlaylist: async (rec) => {
|
|
@@ -21649,7 +21715,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
21649
21715
|
const stillService = new StillFrameService({
|
|
21650
21716
|
lowSegmentsAt: (deviceId, epochMs) => this.index.segmentsOverlapping(deviceId, "low", epochMs, epochMs + 1),
|
|
21651
21717
|
resolveAbsPath: (row) => this.resolveSegmentAbsPath(row),
|
|
21652
|
-
logger: this.ctx.logger.child("Still")
|
|
21718
|
+
logger: this.ctx.logger.child("Still"),
|
|
21719
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21653
21720
|
});
|
|
21654
21721
|
const served = await this.ctx.dataPlane?.serve({
|
|
21655
21722
|
prefix: STILL_PREFIX,
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
1
|
+
import { An as _enum, B as VISIT_MERGE_GAP_MS, Bn as string, Bt as recordingSignalCapability, Ct as mayReadLocation, Dt as modeMayWrite, Et as migrateLegacyDeviceSignalConfig, F as RECORDING_SOURCE_CAMSTACK_ADDON, Hn as EventCategory, Ht as resolveCreateExportSubject, I as RecordingConfigSchema, In as number, J as clipExportFailure, K as batteryCapability, Kt as resolveRecordingProfiles, L as RecordingSignalStatusSchema, Ln as object, Lt as recordingArchiveCapability, Mn as boolean, N as RECORDING_EXPORT_MAX_READ_BYTES, On as selectAssignedProfileSlots, P as RECORDING_SOURCE_CAMSTACK, Qt as stripRetiredBandPreBufferSec, Rn as preprocess, Rt as recordingCapability, S as ExportRecordSchema, Tn as nodePin, Tt as memoizePinnedFfmpeg, Wt as resolveLocationMode, Xt as storageOccupancyCapability, Y as clipExportFailureOf, Yt as storageEvictableCapability, a as BatteryStatusSchema, b as EVENT_PAD_MS, ct as evictionPolicyForMode, dn as errMsg$1, en as videoclipsCapability, et as deriveBatteryPresence, g as DEFAULT_EVENTS_BAND_BUFFER_SEC, jn as array, k as OpsLogEntrySchema, kn as sleep$1, ln as DeviceFeature, lt as evictionPolicyOfLocation, nt as deriveRecordingMode, o as CLIP_BROKER_PACED_RATES, on as evaluateSensorEdge, pn as BaseAddon, pt as gbToBytes, un as DeviceType, ut as exportSubjectOf, vn as customAction, vt as isFirstLevelMacroClass, wt as mayWriteToLocation, xn as hydrateSchema, y as EVENT_OWNER_TYPES, yn as defineCustomActions, yt as loadContributionCapability, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as record, zt as recordingExportCapability } from "../dist-jy-7xoW5.mjs";
|
|
2
|
+
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-NCge1qZM.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
|
|
5
5
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
@@ -3443,7 +3443,20 @@ var StillFrameService = class {
|
|
|
3443
3443
|
return null;
|
|
3444
3444
|
}
|
|
3445
3445
|
try {
|
|
3446
|
-
|
|
3446
|
+
let bin;
|
|
3447
|
+
try {
|
|
3448
|
+
bin = await this.deps.resolveFfmpeg();
|
|
3449
|
+
} catch (err) {
|
|
3450
|
+
this.deps.logger.error("still: refused — no pinned ffmpeg on this node (D540)", {
|
|
3451
|
+
tags: { deviceId },
|
|
3452
|
+
meta: {
|
|
3453
|
+
epochMs,
|
|
3454
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3455
|
+
}
|
|
3456
|
+
});
|
|
3457
|
+
return null;
|
|
3458
|
+
}
|
|
3459
|
+
const jpeg = await this.decodeFrame(bin, abs, source.offsetSec);
|
|
3447
3460
|
if (jpeg === null || jpeg.byteLength === 0) return null;
|
|
3448
3461
|
return {
|
|
3449
3462
|
jpeg,
|
|
@@ -3454,7 +3467,7 @@ var StillFrameService = class {
|
|
|
3454
3467
|
}
|
|
3455
3468
|
}
|
|
3456
3469
|
/** Run ONE ffmpeg input-seek + single-frame decode; JPEG bytes on stdout. */
|
|
3457
|
-
decodeFrame(absPath, offsetSec) {
|
|
3470
|
+
decodeFrame(bin, absPath, offsetSec) {
|
|
3458
3471
|
const args = [
|
|
3459
3472
|
"-hide_banner",
|
|
3460
3473
|
"-loglevel",
|
|
@@ -3470,7 +3483,6 @@ var StillFrameService = class {
|
|
|
3470
3483
|
"pipe:1"
|
|
3471
3484
|
];
|
|
3472
3485
|
const spawn$1 = this.deps.spawn ?? spawn;
|
|
3473
|
-
const bin = this.deps.ffmpegPath ?? "ffmpeg";
|
|
3474
3486
|
return new Promise((resolve) => {
|
|
3475
3487
|
let child;
|
|
3476
3488
|
try {
|
|
@@ -7541,15 +7553,32 @@ var ExportEngine = class {
|
|
|
7541
7553
|
} catch {}
|
|
7542
7554
|
}
|
|
7543
7555
|
}
|
|
7544
|
-
runFfmpeg(id, deviceId, args, progress) {
|
|
7556
|
+
async runFfmpeg(id, deviceId, args, progress) {
|
|
7557
|
+
let ffmpegPath;
|
|
7558
|
+
try {
|
|
7559
|
+
ffmpegPath = await this.deps.resolveFfmpeg();
|
|
7560
|
+
} catch (err) {
|
|
7561
|
+
const reason = `no pinned ffmpeg on this node (D540): ${err instanceof Error ? err.message : String(err)}`;
|
|
7562
|
+
this.deps.logger.error("export render refused — no pinned ffmpeg on this node (D540)", {
|
|
7563
|
+
tags: { deviceId },
|
|
7564
|
+
meta: {
|
|
7565
|
+
exportId: id,
|
|
7566
|
+
error: reason
|
|
7567
|
+
}
|
|
7568
|
+
});
|
|
7569
|
+
return {
|
|
7570
|
+
code: null,
|
|
7571
|
+
stderrTail: [reason]
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7545
7574
|
this.deps.logger.debug("export ffmpeg argv", { meta: {
|
|
7546
7575
|
exportId: id,
|
|
7547
7576
|
args
|
|
7548
7577
|
} });
|
|
7549
7578
|
const stallMs = this.deps.stallMs ?? 9e4;
|
|
7550
7579
|
const faststartStallMs = this.deps.faststartStallMs ?? 9e5;
|
|
7551
|
-
return new Promise((resolve) => {
|
|
7552
|
-
const proc = this.deps.spawn(
|
|
7580
|
+
return await new Promise((resolve) => {
|
|
7581
|
+
const proc = this.deps.spawn(ffmpegPath, args);
|
|
7553
7582
|
this.active.set(id, proc);
|
|
7554
7583
|
const stderrTail = [];
|
|
7555
7584
|
let stallTimer = null;
|
|
@@ -9451,6 +9480,17 @@ async function renderFootage(renderDeps, input, ext, buildArgs) {
|
|
|
9451
9480
|
const toMs = input.aroundMs + input.postRollSec * 1e3;
|
|
9452
9481
|
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || toMs <= fromMs) throw new Error(`invalid ${ext} window [${fromMs}, ${toMs}) for device ${input.deviceId} — aroundMs=${input.aroundMs} preRollSec=${input.preRollSec} postRollSec=${input.postRollSec}`);
|
|
9453
9482
|
const renderId = `${ext}-${now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
9483
|
+
let ffmpegPath;
|
|
9484
|
+
try {
|
|
9485
|
+
ffmpegPath = await renderDeps.resolveFfmpeg();
|
|
9486
|
+
} catch (err) {
|
|
9487
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
9488
|
+
renderDeps.deps.logger.error(`${ext} render refused — no pinned ffmpeg on this node (D540)`, {
|
|
9489
|
+
tags: { deviceId: input.deviceId },
|
|
9490
|
+
meta: { error: reason }
|
|
9491
|
+
});
|
|
9492
|
+
throw new Error(`${ext} render refused for device ${input.deviceId}: no pinned ffmpeg — ${reason}`, { cause: err });
|
|
9493
|
+
}
|
|
9454
9494
|
const availableEnd = latestFinalizedEnd(renderDeps, input.deviceId);
|
|
9455
9495
|
if (availableEnd === null || availableEnd < toMs) throw new Error(`${FOOTAGE_NOT_READY}: [${fromMs}, ${toMs}) for device ${input.deviceId} — finalized footage ends at ${availableEnd ?? "never"}`);
|
|
9456
9496
|
const window = {
|
|
@@ -9469,7 +9509,7 @@ async function renderFootage(renderDeps, input, ext, buildArgs) {
|
|
|
9469
9509
|
if (playlist === null) throw new Error(`no footage covers [${window.fromMs}, ${window.toMs}) for device ${input.deviceId}`);
|
|
9470
9510
|
const outPath = path.join(path.dirname(playlist.path), `${renderId}.${ext}`);
|
|
9471
9511
|
try {
|
|
9472
|
-
await runFfmpeg(renderDeps, buildArgs(playlist.path, outPath), ext);
|
|
9512
|
+
await runFfmpeg(renderDeps, ffmpegPath, buildArgs(playlist.path, outPath), ext);
|
|
9473
9513
|
const bytes = await promises.readFile(outPath);
|
|
9474
9514
|
if (bytes.byteLength === 0) throw new Error(`${ext} render produced an empty file`);
|
|
9475
9515
|
return {
|
|
@@ -9506,9 +9546,9 @@ function latestFinalizedEnd(renderDeps, deviceId) {
|
|
|
9506
9546
|
* throws ESRCH, which is swallowed, so the cost of being wrong is nothing while
|
|
9507
9547
|
* the cost of NOT killing is a zombie per render.
|
|
9508
9548
|
*/
|
|
9509
|
-
function runFfmpeg(renderDeps, args, ext) {
|
|
9549
|
+
function runFfmpeg(renderDeps, ffmpegPath, args, ext) {
|
|
9510
9550
|
return new Promise((resolve, reject) => {
|
|
9511
|
-
const proc = renderDeps.spawn(
|
|
9551
|
+
const proc = renderDeps.spawn(ffmpegPath, args);
|
|
9512
9552
|
let done = false;
|
|
9513
9553
|
let timer = null;
|
|
9514
9554
|
const settle = (err) => {
|
|
@@ -15740,7 +15780,7 @@ var SegmentWriter = class {
|
|
|
15740
15780
|
start() {
|
|
15741
15781
|
if (this.stopped || this.proc) return;
|
|
15742
15782
|
const args = buildPassthroughArgs(this.cfg);
|
|
15743
|
-
const proc = this.deps.spawn(this.deps.ffmpegPath
|
|
15783
|
+
const proc = this.deps.spawn(this.deps.ffmpegPath, args);
|
|
15744
15784
|
this.proc = proc;
|
|
15745
15785
|
this.exitWait = new Promise((resolve) => {
|
|
15746
15786
|
this.resolveExit = resolve;
|
|
@@ -17487,6 +17527,19 @@ var RecordingController = class {
|
|
|
17487
17527
|
*/
|
|
17488
17528
|
async attachProfile(deviceId, profile, segmentSeconds, audio) {
|
|
17489
17529
|
const attachProfileStartMs = this.now();
|
|
17530
|
+
let ffmpegPath;
|
|
17531
|
+
try {
|
|
17532
|
+
ffmpegPath = await this.deps.resolveFfmpeg();
|
|
17533
|
+
} catch (err) {
|
|
17534
|
+
this.deps.logger.error("recorder: writer refused — no pinned ffmpeg on this node (D540)", {
|
|
17535
|
+
tags: { deviceId },
|
|
17536
|
+
meta: {
|
|
17537
|
+
profile,
|
|
17538
|
+
error: errMsg$1(err)
|
|
17539
|
+
}
|
|
17540
|
+
});
|
|
17541
|
+
throw new Error(`recorder: no pinned ffmpeg for device ${deviceId}/${profile}`);
|
|
17542
|
+
}
|
|
17490
17543
|
const placement = await this.resolvePlacement(deviceId, profile);
|
|
17491
17544
|
const placementDoneMs = this.now();
|
|
17492
17545
|
const outDir = path.join(placement.root, STAGING_DIR_NAME, String(deviceId), profile);
|
|
@@ -17522,6 +17575,7 @@ var RecordingController = class {
|
|
|
17522
17575
|
consumerTag
|
|
17523
17576
|
}, {
|
|
17524
17577
|
spawn: this.deps.spawn,
|
|
17578
|
+
ffmpegPath,
|
|
17525
17579
|
logger: deviceLog,
|
|
17526
17580
|
deviceId,
|
|
17527
17581
|
claimCost: (pid) => this.deps.costRegistry?.claim({
|
|
@@ -20815,6 +20869,14 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
20815
20869
|
* because those children were this process's children.
|
|
20816
20870
|
*/
|
|
20817
20871
|
costRegistry = new ChildCostRegistry();
|
|
20872
|
+
/**
|
|
20873
|
+
* This node's PINNED ffmpeg (D540), asked once and shared by every child the
|
|
20874
|
+
* recorder spawns: segment writers, exports, gif/clip renders and stills.
|
|
20875
|
+
* Each of those used to take an optional path that no one passed, and fell
|
|
20876
|
+
* back to a bare `'ffmpeg'` — the HOST's binary. It rejects rather than
|
|
20877
|
+
* naming PATH; each site logs its refusal per camera.
|
|
20878
|
+
*/
|
|
20879
|
+
pinnedFfmpeg = memoizePinnedFfmpeg(() => this.ctx.deps.ensureFfmpeg());
|
|
20818
20880
|
/** True when THIS node is the designated recording node (`recordingNodeId`).
|
|
20819
20881
|
* Every other node stays fully inert — registers no providers, serves no
|
|
20820
20882
|
* playback, runs no controller. Resolved in `onInitialize`. */
|
|
@@ -21208,6 +21270,7 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
21208
21270
|
now: () => Date.now()
|
|
21209
21271
|
});
|
|
21210
21272
|
this.controller = new RecordingController({
|
|
21273
|
+
resolveFfmpeg: this.pinnedFfmpeg,
|
|
21211
21274
|
...this.stagingGuard !== null ? { stagingGuard: this.stagingGuard } : {},
|
|
21212
21275
|
recoverStagingDirectory: (input) => this.recoverReapedDirectory(input),
|
|
21213
21276
|
heapProbe: this.heapProbe,
|
|
@@ -21355,12 +21418,14 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
21355
21418
|
gifRender: (input) => renderGif({
|
|
21356
21419
|
deps: recordingDeps,
|
|
21357
21420
|
exportsDir: this.exportsDirOrThrow(),
|
|
21358
|
-
spawn
|
|
21421
|
+
spawn,
|
|
21422
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21359
21423
|
}, input),
|
|
21360
21424
|
clipRender: (input) => renderClip({
|
|
21361
21425
|
deps: recordingDeps,
|
|
21362
21426
|
exportsDir: this.exportsDirOrThrow(),
|
|
21363
|
-
spawn
|
|
21427
|
+
spawn,
|
|
21428
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21364
21429
|
}, input),
|
|
21365
21430
|
locations: () => this.resolvedLocations,
|
|
21366
21431
|
refreshLocations: () => this.refreshLocations(),
|
|
@@ -21489,6 +21554,7 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
21489
21554
|
};
|
|
21490
21555
|
this.exportEngine = new ExportEngine({
|
|
21491
21556
|
spawn,
|
|
21557
|
+
resolveFfmpeg: this.pinnedFfmpeg,
|
|
21492
21558
|
logger: this.ctx.logger.child("export"),
|
|
21493
21559
|
state: exportsState,
|
|
21494
21560
|
resolvePlaylist: async (rec) => {
|
|
@@ -21641,7 +21707,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
21641
21707
|
const stillService = new StillFrameService({
|
|
21642
21708
|
lowSegmentsAt: (deviceId, epochMs) => this.index.segmentsOverlapping(deviceId, "low", epochMs, epochMs + 1),
|
|
21643
21709
|
resolveAbsPath: (row) => this.resolveSegmentAbsPath(row),
|
|
21644
|
-
logger: this.ctx.logger.child("Still")
|
|
21710
|
+
logger: this.ctx.logger.child("Still"),
|
|
21711
|
+
resolveFfmpeg: this.pinnedFfmpeg
|
|
21645
21712
|
});
|
|
21646
21713
|
const served = await this.ctx.dataPlane?.serve({
|
|
21647
21714
|
prefix: STILL_PREFIX,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-emK7D4bc.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-CdRsXAov.js");
|
|
3
3
|
let node_child_process = require("node:child_process");
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|
|
@@ -7185,10 +7185,10 @@ var SEGMENT_DEMUX_FFMPEG_ARGS = [
|
|
|
7185
7185
|
* of the bytes. Throws on a non-H.264/H.265 stream or a failed conversion.
|
|
7186
7186
|
*
|
|
7187
7187
|
* @param bytes - The full `.m4s` fragment (one moov + one IDR-led GOP).
|
|
7188
|
-
* @param ffmpegPath -
|
|
7188
|
+
* @param ffmpegPath - The node's pinned ffmpeg (D540). Required: no PATH default.
|
|
7189
7189
|
* @returns Access units in source presentation order, each tagged with its codec.
|
|
7190
7190
|
*/
|
|
7191
|
-
async function demuxSegmentToAnnexB(bytes, ffmpegPath
|
|
7191
|
+
async function demuxSegmentToAnnexB(bytes, ffmpegPath) {
|
|
7192
7192
|
const { codec, accessUnits } = demuxMpegTsToAnnexB(await runFfmpegOnce({
|
|
7193
7193
|
ffmpegPath,
|
|
7194
7194
|
args: SEGMENT_DEMUX_FFMPEG_ARGS,
|
|
@@ -7201,6 +7201,17 @@ async function demuxSegmentToAnnexB(bytes, ffmpegPath = "ffmpeg") {
|
|
|
7201
7201
|
codec
|
|
7202
7202
|
}));
|
|
7203
7203
|
}
|
|
7204
|
+
/**
|
|
7205
|
+
* The ffmpeg fallback was needed and the node has no pinned binary to run it
|
|
7206
|
+
* with. Thrown rather than spawning a PATH `ffmpeg` (D540); the session that
|
|
7207
|
+
* asked names it once, per camera.
|
|
7208
|
+
*/
|
|
7209
|
+
var FfmpegFallbackRefusedError = class extends Error {
|
|
7210
|
+
constructor(what) {
|
|
7211
|
+
super(`${what}: ffmpeg fallback refused — no pinned ffmpeg on this node (D540)`);
|
|
7212
|
+
this.name = "FfmpegFallbackRefusedError";
|
|
7213
|
+
}
|
|
7214
|
+
};
|
|
7204
7215
|
//#endregion
|
|
7205
7216
|
//#region src/stream-broker/recorded/segment-demux-js.ts
|
|
7206
7217
|
/**
|
|
@@ -7265,7 +7276,7 @@ async function demuxSegmentFullJs(bytes) {
|
|
|
7265
7276
|
* unit or throws on malformed bytes. Preserves the exact {@link AccessUnit}
|
|
7266
7277
|
* contract either way, so it is a drop-in for `RecordedFeederDeps.demux`.
|
|
7267
7278
|
*/
|
|
7268
|
-
async function demuxSegmentPreferJs(bytes, opts
|
|
7279
|
+
async function demuxSegmentPreferJs(bytes, opts) {
|
|
7269
7280
|
try {
|
|
7270
7281
|
const js = await (opts.jsParse ?? demuxSegmentFullJs)(bytes);
|
|
7271
7282
|
if (js && js.length > 0) {
|
|
@@ -7282,9 +7293,16 @@ async function demuxSegmentPreferJs(bytes, opts = {}) {
|
|
|
7282
7293
|
reason: `mp4box parse threw: ${String(err)}`
|
|
7283
7294
|
});
|
|
7284
7295
|
}
|
|
7296
|
+
if (opts.ffmpegPath === null) throw new FfmpegFallbackRefusedError("recorded video demux");
|
|
7285
7297
|
return demuxSegmentToAnnexB(bytes, opts.ffmpegPath);
|
|
7286
7298
|
}
|
|
7287
7299
|
//#endregion
|
|
7300
|
+
Object.defineProperty(exports, "FfmpegFallbackRefusedError", {
|
|
7301
|
+
enumerable: true,
|
|
7302
|
+
get: function() {
|
|
7303
|
+
return FfmpegFallbackRefusedError;
|
|
7304
|
+
}
|
|
7305
|
+
});
|
|
7288
7306
|
Object.defineProperty(exports, "avccSampleToAnnexB", {
|
|
7289
7307
|
enumerable: true,
|
|
7290
7308
|
get: function() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { dn as errMsg } from "./dist-jy-7xoW5.mjs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|
|
@@ -7185,10 +7185,10 @@ var SEGMENT_DEMUX_FFMPEG_ARGS = [
|
|
|
7185
7185
|
* of the bytes. Throws on a non-H.264/H.265 stream or a failed conversion.
|
|
7186
7186
|
*
|
|
7187
7187
|
* @param bytes - The full `.m4s` fragment (one moov + one IDR-led GOP).
|
|
7188
|
-
* @param ffmpegPath -
|
|
7188
|
+
* @param ffmpegPath - The node's pinned ffmpeg (D540). Required: no PATH default.
|
|
7189
7189
|
* @returns Access units in source presentation order, each tagged with its codec.
|
|
7190
7190
|
*/
|
|
7191
|
-
async function demuxSegmentToAnnexB(bytes, ffmpegPath
|
|
7191
|
+
async function demuxSegmentToAnnexB(bytes, ffmpegPath) {
|
|
7192
7192
|
const { codec, accessUnits } = demuxMpegTsToAnnexB(await runFfmpegOnce({
|
|
7193
7193
|
ffmpegPath,
|
|
7194
7194
|
args: SEGMENT_DEMUX_FFMPEG_ARGS,
|
|
@@ -7201,6 +7201,17 @@ async function demuxSegmentToAnnexB(bytes, ffmpegPath = "ffmpeg") {
|
|
|
7201
7201
|
codec
|
|
7202
7202
|
}));
|
|
7203
7203
|
}
|
|
7204
|
+
/**
|
|
7205
|
+
* The ffmpeg fallback was needed and the node has no pinned binary to run it
|
|
7206
|
+
* with. Thrown rather than spawning a PATH `ffmpeg` (D540); the session that
|
|
7207
|
+
* asked names it once, per camera.
|
|
7208
|
+
*/
|
|
7209
|
+
var FfmpegFallbackRefusedError = class extends Error {
|
|
7210
|
+
constructor(what) {
|
|
7211
|
+
super(`${what}: ffmpeg fallback refused — no pinned ffmpeg on this node (D540)`);
|
|
7212
|
+
this.name = "FfmpegFallbackRefusedError";
|
|
7213
|
+
}
|
|
7214
|
+
};
|
|
7204
7215
|
//#endregion
|
|
7205
7216
|
//#region src/stream-broker/recorded/segment-demux-js.ts
|
|
7206
7217
|
/**
|
|
@@ -7265,7 +7276,7 @@ async function demuxSegmentFullJs(bytes) {
|
|
|
7265
7276
|
* unit or throws on malformed bytes. Preserves the exact {@link AccessUnit}
|
|
7266
7277
|
* contract either way, so it is a drop-in for `RecordedFeederDeps.demux`.
|
|
7267
7278
|
*/
|
|
7268
|
-
async function demuxSegmentPreferJs(bytes, opts
|
|
7279
|
+
async function demuxSegmentPreferJs(bytes, opts) {
|
|
7269
7280
|
try {
|
|
7270
7281
|
const js = await (opts.jsParse ?? demuxSegmentFullJs)(bytes);
|
|
7271
7282
|
if (js && js.length > 0) {
|
|
@@ -7282,7 +7293,8 @@ async function demuxSegmentPreferJs(bytes, opts = {}) {
|
|
|
7282
7293
|
reason: `mp4box parse threw: ${String(err)}`
|
|
7283
7294
|
});
|
|
7284
7295
|
}
|
|
7296
|
+
if (opts.ffmpegPath === null) throw new FfmpegFallbackRefusedError("recorded video demux");
|
|
7285
7297
|
return demuxSegmentToAnnexB(bytes, opts.ffmpegPath);
|
|
7286
7298
|
}
|
|
7287
7299
|
//#endregion
|
|
7288
|
-
export {
|
|
7300
|
+
export { require_mp4box_all as a, parseNalLengthSize as c, runFfmpegOnce as i, demuxSegmentPreferJs as n, avccSampleToAnnexB as o, FfmpegFallbackRefusedError as r, buildKeyframeAnnexB as s, demuxSegmentFullJs as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
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-CdKbuxqG.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-DEPTgXS_.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/mergeClasses.js
|
|
7
7
|
var T = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), E = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), D = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), O = (e) => {
|