@hyperframes/producer 0.8.180 → 0.8.183
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/distributed.d.ts +2 -2
- package/dist/distributed.js +486 -237
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +17 -17
- package/dist/index.js +508 -255
- package/dist/public-server.js +224 -120
- package/dist/services/distributed/assemble.d.ts +2 -1
- package/dist/services/distributed/chunkFrames.d.ts +6 -1
- package/dist/services/distributed/mp4Stitch.d.ts +4 -4
- package/dist/services/distributed/plan.d.ts +11 -0
- package/dist/services/distributed/planV2.d.ts +1 -0
- package/dist/services/distributed/projectHash.d.ts +1 -4
- package/dist/services/render/artifactTransaction.d.ts +7 -0
- package/dist/services/render/audioPadTrim.d.ts +20 -15
- package/dist/services/render/audioTruePeak.d.ts +8 -0
- package/dist/services/render/perfSummary.d.ts +2 -0
- package/dist/services/render/stages/audioStage.d.ts +2 -0
- package/dist/services/render/stages/captureStage.d.ts +9 -3
- package/dist/services/render/stages/freezePlan.d.ts +1 -0
- package/dist/services/renderOrchestrator.d.ts +2 -2
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -48,6 +48,8 @@ import {
|
|
|
48
48
|
extractMediaMetadata as extractMediaMetadata4,
|
|
49
49
|
resolveConfig as resolveConfig2,
|
|
50
50
|
closeCaptureSession as closeCaptureSession8,
|
|
51
|
+
createCaptureSession as createCaptureSession8,
|
|
52
|
+
initializeSession as initializeSession8,
|
|
51
53
|
createVideoFrameInjector,
|
|
52
54
|
getEncoderPreset as getEncoderPreset3,
|
|
53
55
|
distributeFrames as distributeFrames2,
|
|
@@ -1421,7 +1423,7 @@ function pidScope() {
|
|
|
1421
1423
|
return {
|
|
1422
1424
|
host: hostname(),
|
|
1423
1425
|
// Without /proc (macOS, Windows) the boot time, to the minute, stands in for the boot id.
|
|
1424
|
-
boot: attempt(() => readFileSync2("/proc/sys/kernel/random/boot_id", "utf-8").trim()) ?? String(Math.round(Date.now() / 6e4 - uptime() / 60)),
|
|
1426
|
+
boot: attempt(() => readFileSync2("/proc/sys/kernel/random/boot_id", "utf-8").trim()) ?? attempt(() => String(Math.round(Date.now() / 6e4 - uptime() / 60))),
|
|
1425
1427
|
pidns: attempt(() => readlinkSync("/proc/self/ns/pid"))
|
|
1426
1428
|
};
|
|
1427
1429
|
}
|
|
@@ -1439,9 +1441,14 @@ function alive(pid) {
|
|
|
1439
1441
|
return error.code === "EPERM";
|
|
1440
1442
|
}
|
|
1441
1443
|
}
|
|
1442
|
-
function
|
|
1444
|
+
function knownPidScope() {
|
|
1443
1445
|
const scope = pidScope();
|
|
1444
|
-
|
|
1446
|
+
const unknown = scope.boot === void 0 || process.platform === "linux" && scope.pidns === void 0;
|
|
1447
|
+
return unknown ? void 0 : scope;
|
|
1448
|
+
}
|
|
1449
|
+
function inThisPidScope(owner) {
|
|
1450
|
+
const scope = knownPidScope();
|
|
1451
|
+
return scope !== void 0 && owner.host === scope.host && owner.boot === scope.boot && owner.pidns === scope.pidns;
|
|
1445
1452
|
}
|
|
1446
1453
|
function ownerState(dir) {
|
|
1447
1454
|
let owner;
|
|
@@ -1506,7 +1513,9 @@ async function defaultArtifactDurationProbe(path) {
|
|
|
1506
1513
|
const meta = await extractMediaMetadata(path);
|
|
1507
1514
|
return {
|
|
1508
1515
|
durationSeconds: meta.durationSeconds,
|
|
1509
|
-
frames: meta.frames
|
|
1516
|
+
frames: meta.frames,
|
|
1517
|
+
width: meta.width,
|
|
1518
|
+
height: meta.height
|
|
1510
1519
|
};
|
|
1511
1520
|
}
|
|
1512
1521
|
function buildArtifactExpectation(input) {
|
|
@@ -1515,7 +1524,9 @@ function buildArtifactExpectation(input) {
|
|
|
1515
1524
|
const expectation = {
|
|
1516
1525
|
expectedDurationSeconds: input.durationSeconds,
|
|
1517
1526
|
fps: input.fps,
|
|
1518
|
-
expectedFrames: input.expectedFrames
|
|
1527
|
+
expectedFrames: input.expectedFrames,
|
|
1528
|
+
expectedWidth: input.width,
|
|
1529
|
+
expectedHeight: input.height
|
|
1519
1530
|
};
|
|
1520
1531
|
if (input.outputFormat !== "hls") return expectation;
|
|
1521
1532
|
return {
|
|
@@ -1583,6 +1594,16 @@ function assertFrameCountWithinTolerance(expectedFrames, probedFrames, stagingPa
|
|
|
1583
1594
|
`Render artifact is truncated: expected ${expectedFrames} frames, probed ${probedFrames} frames (shortfall ${shortfall} frames). Artifact: ${stagingPath}`
|
|
1584
1595
|
);
|
|
1585
1596
|
}
|
|
1597
|
+
function assertSizeMatches(expected, probed, stagingPath) {
|
|
1598
|
+
const { expectedWidth: width, expectedHeight: height } = expected;
|
|
1599
|
+
if (width === void 0 || height === void 0) return;
|
|
1600
|
+
if (probed.width === void 0 || probed.height === void 0) return;
|
|
1601
|
+
const fits = (want, got) => got === want || got === want + want % 2;
|
|
1602
|
+
if (fits(width, probed.width) && fits(height, probed.height)) return;
|
|
1603
|
+
throw new Error(
|
|
1604
|
+
`Render artifact has the wrong size: expected ${width}x${height}, probed ${probed.width}x${probed.height}. Artifact: ${stagingPath}`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1586
1607
|
var ArtifactTransaction = class {
|
|
1587
1608
|
constructor(destinationPath, kind, fileSystem = defaultFileSystem, durationProbe = defaultArtifactDurationProbe) {
|
|
1588
1609
|
this.kind = kind;
|
|
@@ -1653,6 +1674,7 @@ var ArtifactTransaction = class {
|
|
|
1653
1674
|
if (expected.expectedFrames !== void 0) {
|
|
1654
1675
|
assertFrameCountWithinTolerance(expected.expectedFrames, probed.frames, probePath);
|
|
1655
1676
|
}
|
|
1677
|
+
assertSizeMatches(expected, probed, probePath);
|
|
1656
1678
|
}
|
|
1657
1679
|
async runDurationProbe(probePath) {
|
|
1658
1680
|
try {
|
|
@@ -3201,7 +3223,9 @@ function aggregateDrawElement(perfs, de) {
|
|
|
3201
3223
|
if (perfs.length === 0) return void 0;
|
|
3202
3224
|
const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
|
|
3203
3225
|
const gateReasons = [
|
|
3204
|
-
...new Set(
|
|
3226
|
+
...new Set(
|
|
3227
|
+
[de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
|
|
3228
|
+
)
|
|
3205
3229
|
].sort();
|
|
3206
3230
|
const gpuRenderers = [
|
|
3207
3231
|
...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
|
|
@@ -8665,6 +8689,7 @@ async function runAudioStage(input) {
|
|
|
8665
8689
|
audios,
|
|
8666
8690
|
masterFxChain,
|
|
8667
8691
|
urlByAbsentLocalPath,
|
|
8692
|
+
keepEncoderInputAt,
|
|
8668
8693
|
abortSignal,
|
|
8669
8694
|
assertNotAborted,
|
|
8670
8695
|
log
|
|
@@ -8684,7 +8709,13 @@ async function runAudioStage(input) {
|
|
|
8684
8709
|
audioOutputPath,
|
|
8685
8710
|
duration,
|
|
8686
8711
|
abortSignal,
|
|
8687
|
-
{
|
|
8712
|
+
{
|
|
8713
|
+
ffmpegProcessTimeout,
|
|
8714
|
+
audioGain,
|
|
8715
|
+
masterFxChain,
|
|
8716
|
+
urlByAbsentLocalPath,
|
|
8717
|
+
keepEncoderInputAt
|
|
8718
|
+
},
|
|
8688
8719
|
compiledDir
|
|
8689
8720
|
);
|
|
8690
8721
|
} catch (err) {
|
|
@@ -8798,13 +8829,29 @@ function inspectDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, free
|
|
|
8798
8829
|
}
|
|
8799
8830
|
return { available: false, estimatedBytes, freeBytes };
|
|
8800
8831
|
}
|
|
8801
|
-
function
|
|
8832
|
+
function diskCaptureRemedy(cfg, outputFormat, durationSeconds, workerCount) {
|
|
8833
|
+
const oneWorker = explainStreamingEncodeGate(cfg, outputFormat, 1, durationSeconds);
|
|
8834
|
+
if (workerCount > 1 && oneWorker.enabled) {
|
|
8835
|
+
return `This render stores frames because it captures with ${workerCount} workers. Free up disk space, or re-run this render with --workers 1 to stream frames into the encoder instead. One worker is slower, so keep the default worker count for renders that fit on disk.`;
|
|
8836
|
+
}
|
|
8837
|
+
switch (oneWorker.reason) {
|
|
8838
|
+
case "format_excluded":
|
|
8839
|
+
return `${outputFormat} output stores every frame at any worker count. Free up disk space or render to mp4.`;
|
|
8840
|
+
case "disabled_by_config":
|
|
8841
|
+
return "PRODUCER_ENABLE_STREAMING_ENCODE=false sends every render to disk. Free up disk space or unset it.";
|
|
8842
|
+
case "duration_cap":
|
|
8843
|
+
return "The streaming duration cap (PRODUCER_STREAMING_ENCODE_DURATION_CAP_ENABLED=true) sends renders longer than PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS to disk at any worker count. Free up disk space, unset the cap, or use --low-memory-mode, which streams past it on one worker.";
|
|
8844
|
+
default:
|
|
8845
|
+
return "Free up disk space.";
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remedy) {
|
|
8802
8849
|
const basisNote = basis === "measured" ? ` (measured from the first ${DISK_PROJECTION_SAMPLE_FRAMES} frames)` : "";
|
|
8803
8850
|
return new Error(
|
|
8804
|
-
`Disk capture may need ~${(needBytes / 1e6).toFixed(1)} MB of temporary frame storage${basisNote}, but only ${(freeBytes / 1e6).toFixed(1)} MB is free at ${framesDir}. Disk capture stores every frame as an image file (JPEG, or PNG for alpha).
|
|
8851
|
+
`Disk capture may need ~${(needBytes / 1e6).toFixed(1)} MB of temporary frame storage${basisNote}, but only ${(freeBytes / 1e6).toFixed(1)} MB is free at ${framesDir}. Disk capture stores every frame as an image file (JPEG, or PNG for alpha). ${remedy}`
|
|
8805
8852
|
);
|
|
8806
8853
|
}
|
|
8807
|
-
function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeDiskBytes) {
|
|
8854
|
+
function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy, freeDiskBytes) {
|
|
8808
8855
|
const headroom = inspectDiskCaptureHeadroom(
|
|
8809
8856
|
framesDir,
|
|
8810
8857
|
totalFrames,
|
|
@@ -8816,7 +8863,8 @@ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeD
|
|
|
8816
8863
|
headroom.estimatedBytes,
|
|
8817
8864
|
headroom.freeBytes,
|
|
8818
8865
|
framesDir,
|
|
8819
|
-
"estimate"
|
|
8866
|
+
"estimate",
|
|
8867
|
+
remedy
|
|
8820
8868
|
);
|
|
8821
8869
|
}
|
|
8822
8870
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
@@ -8836,7 +8884,7 @@ function measureCaptureFrameBytes(workDir, framesDir) {
|
|
|
8836
8884
|
return total;
|
|
8837
8885
|
}
|
|
8838
8886
|
function createDiskCaptureProjection(input) {
|
|
8839
|
-
const { framesDir, totalFrames, measureBytes, freeDiskBytes = freeDiskBytesAt } = input;
|
|
8887
|
+
const { framesDir, totalFrames, measureBytes, remedy, freeDiskBytes = freeDiskBytesAt } = input;
|
|
8840
8888
|
let checked = false;
|
|
8841
8889
|
return (capturedFrames) => {
|
|
8842
8890
|
if (checked || capturedFrames < DISK_PROJECTION_SAMPLE_FRAMES) return;
|
|
@@ -8848,7 +8896,7 @@ function createDiskCaptureProjection(input) {
|
|
|
8848
8896
|
if (remaining <= 0 || freeBytes === null) return;
|
|
8849
8897
|
const needBytes = Math.ceil(measured / capturedFrames * remaining);
|
|
8850
8898
|
if (needBytes > freeBytes * 0.9) {
|
|
8851
|
-
throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured");
|
|
8899
|
+
throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured", remedy);
|
|
8852
8900
|
}
|
|
8853
8901
|
};
|
|
8854
8902
|
}
|
|
@@ -8890,11 +8938,13 @@ async function runCaptureStage(input) {
|
|
|
8890
8938
|
}
|
|
8891
8939
|
}
|
|
8892
8940
|
const captureOptions = buildCaptureOptions();
|
|
8893
|
-
|
|
8941
|
+
const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
|
|
8942
|
+
assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy);
|
|
8894
8943
|
const checkDiskProjection = createDiskCaptureProjection({
|
|
8895
8944
|
framesDir,
|
|
8896
8945
|
totalFrames,
|
|
8897
|
-
measureBytes: () => measureCaptureFrameBytes(workDir, framesDir)
|
|
8946
|
+
measureBytes: () => measureCaptureFrameBytes(workDir, framesDir),
|
|
8947
|
+
remedy
|
|
8898
8948
|
});
|
|
8899
8949
|
if (workerCount > 1) {
|
|
8900
8950
|
const attempts = await executeDiskCaptureWithAdaptiveRetry({
|
|
@@ -10105,8 +10155,10 @@ function fpsToFfmpegArg2(fps) {
|
|
|
10105
10155
|
}
|
|
10106
10156
|
var KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA = ["-reinit_filter", "0"];
|
|
10107
10157
|
var WRITE_EVERY_FRAME_WHOLE = ["-gifflags", "0"];
|
|
10158
|
+
var FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE = ["-xerror"];
|
|
10108
10159
|
function framesInput(input, fpsArg) {
|
|
10109
10160
|
return [
|
|
10161
|
+
...FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE,
|
|
10110
10162
|
"-framerate",
|
|
10111
10163
|
fpsArg,
|
|
10112
10164
|
...KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA,
|
|
@@ -10356,36 +10408,97 @@ import { mkdtempSync as mkdtempSync3, renameSync as renameSync4, rmSync as rmSyn
|
|
|
10356
10408
|
import { dirname as dirname9, join as join25 } from "node:path";
|
|
10357
10409
|
import {
|
|
10358
10410
|
DEFAULT_CONFIG,
|
|
10359
|
-
describeFfmpegFailure,
|
|
10360
|
-
|
|
10411
|
+
describeFfmpegFailure as describeFfmpegFailure2,
|
|
10412
|
+
encodeAac,
|
|
10413
|
+
extractAudioMetadata as extractAudioMetadata3,
|
|
10361
10414
|
getFfprobeBinary,
|
|
10362
10415
|
ManagedChildProcess,
|
|
10363
|
-
runFfmpeg as
|
|
10416
|
+
runFfmpeg as runFfmpeg5,
|
|
10364
10417
|
trackChildProcess
|
|
10365
10418
|
} from "@hyperframes/engine";
|
|
10366
10419
|
import { redactKnownPaths, redactTelemetryString as redactTelemetryString3 } from "@hyperframes/core";
|
|
10420
|
+
|
|
10421
|
+
// src/services/render/audioTruePeak.ts
|
|
10422
|
+
import {
|
|
10423
|
+
aacSegmentCount,
|
|
10424
|
+
describeFfmpegFailure,
|
|
10425
|
+
extractAudioMetadata as extractAudioMetadata2,
|
|
10426
|
+
runFfmpeg as runFfmpeg4
|
|
10427
|
+
} from "@hyperframes/engine";
|
|
10428
|
+
var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
10429
|
+
function checkedDbfs(value) {
|
|
10430
|
+
if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
|
|
10431
|
+
throw new Error("[audioTruePeak] FFmpeg did not report AAC true peak");
|
|
10432
|
+
}
|
|
10433
|
+
return value;
|
|
10434
|
+
}
|
|
10435
|
+
async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
|
|
10436
|
+
let firstBlock;
|
|
10437
|
+
let laterBlocks = Number.NEGATIVE_INFINITY;
|
|
10438
|
+
let partialLine = "";
|
|
10439
|
+
const readLines = (chunk) => {
|
|
10440
|
+
const lines = (partialLine + chunk).split(/\r|\n/);
|
|
10441
|
+
partialLine = lines.pop() ?? "";
|
|
10442
|
+
for (const line of lines) {
|
|
10443
|
+
const ftpk = /FTPK:([^d]*)dBFS/.exec(line)?.[1];
|
|
10444
|
+
if (!ftpk) continue;
|
|
10445
|
+
const block = Math.max(...ftpk.trim().split(/\s+/).map(toDbfs));
|
|
10446
|
+
if (firstBlock === void 0) firstBlock = block;
|
|
10447
|
+
else laterBlocks = Math.max(laterBlocks, block);
|
|
10448
|
+
}
|
|
10449
|
+
};
|
|
10450
|
+
const result = await runFfmpeg4(
|
|
10451
|
+
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
|
|
10452
|
+
{ signal, timeout: timeoutMs, onStderr: readLines }
|
|
10453
|
+
);
|
|
10454
|
+
if (!result.success) {
|
|
10455
|
+
throw new Error(`[audioTruePeak] ${describeFfmpegFailure(result, timeoutMs)}`);
|
|
10456
|
+
}
|
|
10457
|
+
const summary = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
|
|
10458
|
+
return {
|
|
10459
|
+
peak: checkedDbfs(toDbfs(summary.at(-1)?.[1] ?? "nan")),
|
|
10460
|
+
firstBlock: checkedDbfs(firstBlock ?? Number.NEGATIVE_INFINITY),
|
|
10461
|
+
laterBlocks: checkedDbfs(laterBlocks)
|
|
10462
|
+
};
|
|
10463
|
+
}
|
|
10464
|
+
async function measureTruePeakDbfs(audioPath, options) {
|
|
10465
|
+
const { timeoutMs, signal } = options;
|
|
10466
|
+
const wholeFile = async () => (await scanTruePeak(audioPath, "", timeoutMs, signal)).peak;
|
|
10467
|
+
const { durationSeconds, sampleRate } = await extractAudioMetadata2(audioPath, { signal });
|
|
10468
|
+
const block = 4800;
|
|
10469
|
+
const count = sampleRate === 48e3 && Number.isFinite(durationSeconds) ? aacSegmentCount(durationSeconds) : 1;
|
|
10470
|
+
if (count === 1) return wholeFile();
|
|
10471
|
+
const cut = (k) => k * Math.floor(durationSeconds * 10 / count) * block;
|
|
10472
|
+
const peers = new AbortController();
|
|
10473
|
+
const segmentSignal = signal ? AbortSignal.any([signal, peers.signal]) : peers.signal;
|
|
10474
|
+
const scans = await Promise.all(
|
|
10475
|
+
Array.from({ length: count }, (_, k) => {
|
|
10476
|
+
const start = k === 0 ? 0 : cut(k) - block;
|
|
10477
|
+
const end = k === count - 1 ? "" : `:end_sample=${cut(k + 1)}`;
|
|
10478
|
+
return scanTruePeak(
|
|
10479
|
+
audioPath,
|
|
10480
|
+
`atrim=start_sample=${start}${end},`,
|
|
10481
|
+
timeoutMs,
|
|
10482
|
+
segmentSignal
|
|
10483
|
+
);
|
|
10484
|
+
})
|
|
10485
|
+
).catch((error) => {
|
|
10486
|
+
peers.abort();
|
|
10487
|
+
throw error;
|
|
10488
|
+
});
|
|
10489
|
+
const last = scans[count - 1];
|
|
10490
|
+
const counted = Math.max(scans[0].peak, ...scans.slice(1).map((scan) => scan.laterBlocks));
|
|
10491
|
+
if (last.peak > last.firstBlock || last.peak <= counted) return Math.max(counted, last.peak);
|
|
10492
|
+
return wholeFile();
|
|
10493
|
+
}
|
|
10494
|
+
|
|
10495
|
+
// src/services/render/audioPadTrim.ts
|
|
10367
10496
|
var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
|
|
10368
10497
|
var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
|
|
10369
10498
|
var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
|
|
10370
10499
|
var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
|
|
10371
|
-
function
|
|
10372
|
-
return
|
|
10373
|
-
"-i",
|
|
10374
|
-
inputPath,
|
|
10375
|
-
"-map",
|
|
10376
|
-
"0:a:0",
|
|
10377
|
-
"-vn",
|
|
10378
|
-
"-af",
|
|
10379
|
-
`volume=${attenuationDb.toFixed(3)}dB`,
|
|
10380
|
-
"-t",
|
|
10381
|
-
formatSeconds(targetDurationSeconds),
|
|
10382
|
-
"-c:a",
|
|
10383
|
-
"aac",
|
|
10384
|
-
"-b:a",
|
|
10385
|
-
"192k",
|
|
10386
|
-
"-y",
|
|
10387
|
-
outputPath
|
|
10388
|
-
];
|
|
10500
|
+
function aacTruePeakCorrectionFilter(targetDurationSeconds, attenuationDb) {
|
|
10501
|
+
return `volume=${attenuationDb.toFixed(3)}dB,apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
|
|
10389
10502
|
}
|
|
10390
10503
|
function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
|
|
10391
10504
|
const delta = targetDurationSeconds - sourceDurationSeconds;
|
|
@@ -10400,53 +10513,13 @@ function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, tar
|
|
|
10400
10513
|
if (delta > 0) {
|
|
10401
10514
|
return {
|
|
10402
10515
|
operation: "pad",
|
|
10403
|
-
steps: [
|
|
10404
|
-
{
|
|
10405
|
-
kind: "normalize",
|
|
10406
|
-
args: [
|
|
10407
|
-
"-i",
|
|
10408
|
-
audioPath,
|
|
10409
|
-
"-af",
|
|
10410
|
-
`apad,atrim=0:${targetSec}`,
|
|
10411
|
-
"-t",
|
|
10412
|
-
targetSec,
|
|
10413
|
-
"-c:a",
|
|
10414
|
-
"aac",
|
|
10415
|
-
"-b:a",
|
|
10416
|
-
"192k",
|
|
10417
|
-
"-y",
|
|
10418
|
-
outputPath
|
|
10419
|
-
]
|
|
10420
|
-
}
|
|
10421
|
-
],
|
|
10516
|
+
steps: [{ kind: "normalize", filter: `apad,atrim=0:${targetSec}` }],
|
|
10422
10517
|
cleanupPaths: []
|
|
10423
10518
|
};
|
|
10424
10519
|
}
|
|
10425
10520
|
return {
|
|
10426
10521
|
operation: "trim",
|
|
10427
|
-
steps: [
|
|
10428
|
-
{
|
|
10429
|
-
kind: "trim",
|
|
10430
|
-
args: [
|
|
10431
|
-
"-i",
|
|
10432
|
-
audioPath,
|
|
10433
|
-
"-af",
|
|
10434
|
-
`atrim=duration=${targetSec},asetpts=PTS-STARTPTS`,
|
|
10435
|
-
// `atrim` limits decoded samples but does not cap muxer timestamps
|
|
10436
|
-
// introduced by encoder delay/flush. The output duration contract
|
|
10437
|
-
// is enforced at the container boundary as well; without `-t`, a
|
|
10438
|
-
// long primed source can retain its original tail after re-encode.
|
|
10439
|
-
"-t",
|
|
10440
|
-
targetSec,
|
|
10441
|
-
"-c:a",
|
|
10442
|
-
"aac",
|
|
10443
|
-
"-b:a",
|
|
10444
|
-
"192k",
|
|
10445
|
-
"-y",
|
|
10446
|
-
outputPath
|
|
10447
|
-
]
|
|
10448
|
-
}
|
|
10449
|
-
],
|
|
10522
|
+
steps: [{ kind: "trim", filter: `atrim=duration=${targetSec},asetpts=PTS-STARTPTS` }],
|
|
10450
10523
|
cleanupPaths: []
|
|
10451
10524
|
};
|
|
10452
10525
|
}
|
|
@@ -10463,7 +10536,8 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10463
10536
|
const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
|
|
10464
10537
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
10465
10538
|
const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
|
|
10466
|
-
const
|
|
10539
|
+
const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal);
|
|
10540
|
+
const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg || input.encodeAac ? void 0 : (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }));
|
|
10467
10541
|
const [videoResult, audioResult] = await Promise.allSettled([
|
|
10468
10542
|
probeVideo(input.videoPath),
|
|
10469
10543
|
probeAudio(input.audioPath, input.signal)
|
|
@@ -10510,7 +10584,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10510
10584
|
);
|
|
10511
10585
|
try {
|
|
10512
10586
|
for (const step of plan.steps) {
|
|
10513
|
-
const ffmpegResult = await runner(step.args);
|
|
10587
|
+
const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(input.audioPath, input.outputPath, targetDurationSeconds, step.filter);
|
|
10514
10588
|
if (!ffmpegResult.success) {
|
|
10515
10589
|
return {
|
|
10516
10590
|
success: false,
|
|
@@ -10542,7 +10616,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
10542
10616
|
targetDurationSeconds,
|
|
10543
10617
|
signal: input.signal,
|
|
10544
10618
|
probeTruePeak,
|
|
10545
|
-
|
|
10619
|
+
encode
|
|
10546
10620
|
});
|
|
10547
10621
|
if (!correction.success) {
|
|
10548
10622
|
return {
|
|
@@ -10590,13 +10664,11 @@ async function enforceAacTruePeak(input) {
|
|
|
10590
10664
|
}
|
|
10591
10665
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
10592
10666
|
attenuationDb += correctionTargetDbfs - truePeakDbfs;
|
|
10593
|
-
const result = await input.
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
attenuationDb
|
|
10599
|
-
)
|
|
10667
|
+
const result = await input.encode(
|
|
10668
|
+
input.audioPath,
|
|
10669
|
+
correctedPath,
|
|
10670
|
+
input.targetDurationSeconds,
|
|
10671
|
+
aacTruePeakCorrectionFilter(input.targetDurationSeconds, attenuationDb)
|
|
10600
10672
|
);
|
|
10601
10673
|
if (!result.success) {
|
|
10602
10674
|
return {
|
|
@@ -10691,7 +10763,7 @@ function parseFrameRate(rate) {
|
|
|
10691
10763
|
return { fpsNum, fpsDen };
|
|
10692
10764
|
}
|
|
10693
10765
|
async function defaultProbeAudioInfo(audioPath, signal) {
|
|
10694
|
-
const metadata = await
|
|
10766
|
+
const metadata = await extractAudioMetadata3(audioPath, { signal });
|
|
10695
10767
|
return {
|
|
10696
10768
|
durationSeconds: metadata.durationSeconds,
|
|
10697
10769
|
sampleRate: metadata.sampleRate,
|
|
@@ -10699,28 +10771,23 @@ async function defaultProbeAudioInfo(audioPath, signal) {
|
|
|
10699
10771
|
audioCodec: metadata.audioCodec
|
|
10700
10772
|
};
|
|
10701
10773
|
}
|
|
10702
|
-
async function defaultProbeAudioTruePeakDbfs(audioPath, timeoutMs, signal) {
|
|
10703
|
-
const result = await runFfmpeg4(
|
|
10704
|
-
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", "ebur128=peak=true", "-f", "null", "-"],
|
|
10705
|
-
{ signal, timeout: timeoutMs }
|
|
10706
|
-
);
|
|
10707
|
-
if (!result.success) {
|
|
10708
|
-
throw new Error(`[audioPadTrim] ${describeFfmpegFailure(result, timeoutMs)}`);
|
|
10709
|
-
}
|
|
10710
|
-
const matches = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
|
|
10711
|
-
const token = matches.at(-1)?.[1]?.toLowerCase();
|
|
10712
|
-
const value = token === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
10713
|
-
if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
|
|
10714
|
-
throw new Error("[audioPadTrim] FFmpeg did not report AAC true peak");
|
|
10715
|
-
}
|
|
10716
|
-
return value;
|
|
10717
|
-
}
|
|
10718
10774
|
async function defaultRunFfmpeg(args, timeoutMs, signal) {
|
|
10719
|
-
|
|
10775
|
+
return toStepResult(await runFfmpeg5(args, { signal, timeout: timeoutMs }), timeoutMs);
|
|
10776
|
+
}
|
|
10777
|
+
function defaultAacEncoder(timeoutMs, signal, pcmSourcePath) {
|
|
10778
|
+
return async (inputPath, outputPath, durationSeconds, filter) => {
|
|
10779
|
+
const options = pcmSourcePath ? { signal, timeout: timeoutMs, filter } : { signal, timeout: timeoutMs, filter, segments: 1 };
|
|
10780
|
+
return toStepResult(
|
|
10781
|
+
await encodeAac(pcmSourcePath ?? inputPath, outputPath, durationSeconds, options),
|
|
10782
|
+
timeoutMs
|
|
10783
|
+
);
|
|
10784
|
+
};
|
|
10785
|
+
}
|
|
10786
|
+
function toStepResult(result, timeoutMs) {
|
|
10720
10787
|
if (result.success) return { success: true };
|
|
10721
10788
|
return {
|
|
10722
10789
|
success: false,
|
|
10723
|
-
error: `[audioPadTrim] ${
|
|
10790
|
+
error: `[audioPadTrim] ${describeFfmpegFailure2(result, timeoutMs)}`,
|
|
10724
10791
|
failureReason: result.failureReason
|
|
10725
10792
|
};
|
|
10726
10793
|
}
|
|
@@ -10904,7 +10971,7 @@ function assertOutputScaleSupported(deviceScaleFactor, route) {
|
|
|
10904
10971
|
const reason = UNSUPPORTED_REASON2[route];
|
|
10905
10972
|
if (reason === null) return;
|
|
10906
10973
|
throw new Error(
|
|
10907
|
-
`[Render] --resolution other than the composition's size is not supported on the "${route}" capture route: ${reason}. Render at the composition's size, or
|
|
10974
|
+
`[Render] --resolution other than the composition's size is not supported on the "${route}" capture route: ${reason}. Render at the composition's size; or, for HDR footage, add --sdr to render this size in standard colour; or, for shader transitions, render as mp4.`
|
|
10908
10975
|
);
|
|
10909
10976
|
}
|
|
10910
10977
|
|
|
@@ -11121,7 +11188,7 @@ function getNextRetryWorkerCount(currentWorkers) {
|
|
|
11121
11188
|
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
11122
11189
|
}
|
|
11123
11190
|
function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
|
|
11124
|
-
if (platform2 === "win32") return join26(systemTempDir, "hf-render-");
|
|
11191
|
+
if (platform2 === "win32" || platform2 === "darwin") return join26(systemTempDir, "hf-render-");
|
|
11125
11192
|
return join26(dirname10(outputPath), `work-${jobId}-`);
|
|
11126
11193
|
}
|
|
11127
11194
|
var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
|
|
@@ -11615,6 +11682,9 @@ function resolveDeShortBand(args) {
|
|
|
11615
11682
|
function shouldPreferSingleWorkerDrawElement(args) {
|
|
11616
11683
|
return args.workerCount > 1 && typeof args.requestedWorkers !== "number" && args.useDrawElement && !args.deCompileGate && !args.forceScreenshot && outputUsesH264Pipeline(args.outputFormat) && args.minFrames > 0 && args.totalFrames >= args.minFrames && args.singleWorkerStreamingOk && !args.layeredOrEffectRoute && !args.supersampling && !args.probeDeGated && !args.experimentalParallelDeOptIn;
|
|
11617
11684
|
}
|
|
11685
|
+
function isProbeDeGated(session) {
|
|
11686
|
+
return session !== null && session.captureMode !== "drawelement" && !session.deInitDeferred;
|
|
11687
|
+
}
|
|
11618
11688
|
function resolveInversionRetryPlan(args) {
|
|
11619
11689
|
if (args.deWorkerInversion !== "inverted") return null;
|
|
11620
11690
|
const workerCount = args.isMemoryExhaustion ? 1 : args.preInversionWorkerCount;
|
|
@@ -11696,7 +11766,8 @@ async function closeOrphanedProbeForRetry(probe, closer, log, retryContext) {
|
|
|
11696
11766
|
}
|
|
11697
11767
|
function resolveParallelCaptureMode(args) {
|
|
11698
11768
|
const supersampling = (args.deviceScaleFactor ?? 1) !== 1;
|
|
11699
|
-
|
|
11769
|
+
const headlessShell = args.headlessShellPath !== void 0;
|
|
11770
|
+
return headlessShell && args.platform === "linux" && !args.forceScreenshot && !supersampling ? "beginframe" : "screenshot";
|
|
11700
11771
|
}
|
|
11701
11772
|
function isCaptureParallelStreamRouterEnabled(env, resolvedMode) {
|
|
11702
11773
|
const raw = env.HF_CAPTURE_PARALLEL_STREAM?.trim().toLowerCase();
|
|
@@ -12304,6 +12375,7 @@ async function executeRenderPipeline(input) {
|
|
|
12304
12375
|
);
|
|
12305
12376
|
const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
|
|
12306
12377
|
const hasHdrContent = Boolean(effectiveHdr && nativeHdrIds.size > 0);
|
|
12378
|
+
if (hasHdrContent) assertOutputScaleSupported(deviceScaleFactor, "hdr_layered");
|
|
12307
12379
|
const deSingleMinFramesRaw = process.env.HF_DE_SINGLE_MIN_FRAMES;
|
|
12308
12380
|
const deSingleMinFramesNum = deSingleMinFramesRaw === void 0 || deSingleMinFramesRaw.trim() === "" ? 900 : Number(deSingleMinFramesRaw);
|
|
12309
12381
|
const deSingleMinFrames = Number.isFinite(deSingleMinFramesNum) ? deSingleMinFramesNum : 900;
|
|
@@ -12344,7 +12416,7 @@ async function executeRenderPipeline(input) {
|
|
|
12344
12416
|
singleWorkerStreamingOk: shouldUseStreamingEncode(cfg, outputFormat, 1, job.duration),
|
|
12345
12417
|
layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
|
|
12346
12418
|
supersampling: deviceScaleFactor !== 1,
|
|
12347
|
-
probeDeGated: probeSession
|
|
12419
|
+
probeDeGated: isProbeDeGated(probeSession),
|
|
12348
12420
|
experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || // Verified parallel DE streaming (opt-in) wants its parallelism kept.
|
|
12349
12421
|
process.env.HF_DE_PARALLEL_STREAM === "true"
|
|
12350
12422
|
};
|
|
@@ -12353,14 +12425,14 @@ async function executeRenderPipeline(input) {
|
|
|
12353
12425
|
...deInversionArgs,
|
|
12354
12426
|
minFrames: Math.min(deSingleMinFrames, deShortBandMinFrames)
|
|
12355
12427
|
});
|
|
12356
|
-
|
|
12428
|
+
let deShortBand = resolveDeShortBand({
|
|
12357
12429
|
invertAtBaseFloor,
|
|
12358
12430
|
invertAtBandFloor,
|
|
12359
12431
|
bandEnabled: deShortBandEnabled,
|
|
12360
12432
|
bandOpen: deShortBandOpen,
|
|
12361
12433
|
elementCountSource: compositionElementCountSource
|
|
12362
12434
|
});
|
|
12363
|
-
|
|
12435
|
+
let deInversionEligible = deShortBandRoute && deShortBand === "applied" ? invertAtBandFloor : invertAtBaseFloor;
|
|
12364
12436
|
const deInversionEffectiveMinFrames = deShortBandRoute && deShortBand === "applied" ? Math.min(deSingleMinFrames, deShortBandMinFrames) : deSingleMinFrames;
|
|
12365
12437
|
const deParallelRouterEnabled = isDeParallelRouterEnabled(process.env);
|
|
12366
12438
|
const deParallelMinFramesRaw = process.env.HF_DE_PARALLEL_MIN_FRAMES;
|
|
@@ -12369,7 +12441,7 @@ async function executeRenderPipeline(input) {
|
|
|
12369
12441
|
const deParallelMinMemRaw = process.env.HF_DE_PARALLEL_MIN_MEM_MB;
|
|
12370
12442
|
const deParallelMinMemNum = deParallelMinMemRaw === void 0 || deParallelMinMemRaw.trim() === "" ? 24576 : Number(deParallelMinMemRaw);
|
|
12371
12443
|
const deParallelMinMemoryMb = Number.isFinite(deParallelMinMemNum) ? deParallelMinMemNum : 24576;
|
|
12372
|
-
|
|
12444
|
+
let deParallelRouterEligible = shouldPreferParallelDrawElement({
|
|
12373
12445
|
workerCount: WOULD_RESOLVE_MULTI_WORKER,
|
|
12374
12446
|
requestedWorkers: job.config.workers,
|
|
12375
12447
|
useDrawElement: cfg.useDrawElement,
|
|
@@ -12380,7 +12452,7 @@ async function executeRenderPipeline(input) {
|
|
|
12380
12452
|
minFrames: deParallelMinFrames,
|
|
12381
12453
|
layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
|
|
12382
12454
|
supersampling: deviceScaleFactor !== 1,
|
|
12383
|
-
probeDeGated: probeSession
|
|
12455
|
+
probeDeGated: isProbeDeGated(probeSession),
|
|
12384
12456
|
experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
|
|
12385
12457
|
routerEnabled: deParallelRouterEnabled,
|
|
12386
12458
|
// Router pins 3 workers for the streaming path; don't pin when a
|
|
@@ -12396,6 +12468,35 @@ async function executeRenderPipeline(input) {
|
|
|
12396
12468
|
totalMemoryMb: Math.round(totalmem2() / (1024 * 1024)),
|
|
12397
12469
|
minMemoryMb: deParallelMinMemoryMb
|
|
12398
12470
|
});
|
|
12471
|
+
if (probeSession === null && (deInversionEligible || deParallelRouterEligible)) {
|
|
12472
|
+
try {
|
|
12473
|
+
probeSession = await createCaptureSession8(
|
|
12474
|
+
activeFileServer.url,
|
|
12475
|
+
framesDir,
|
|
12476
|
+
buildCaptureOptions(),
|
|
12477
|
+
null,
|
|
12478
|
+
{ ...cfg, forceScreenshot: captureForceScreenshot }
|
|
12479
|
+
);
|
|
12480
|
+
await initializeSession8(probeSession);
|
|
12481
|
+
} catch (error) {
|
|
12482
|
+
assertNotAborted();
|
|
12483
|
+
if (probeSession) {
|
|
12484
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
12485
|
+
await closeCaptureSession8(probeSession).catch(() => {
|
|
12486
|
+
});
|
|
12487
|
+
probeSession = null;
|
|
12488
|
+
}
|
|
12489
|
+
log.warn("[Render] Fast capture gate check failed; routing without it.", {
|
|
12490
|
+
error: error instanceof Error ? error.message : String(error)
|
|
12491
|
+
});
|
|
12492
|
+
}
|
|
12493
|
+
if (isProbeDeGated(probeSession)) {
|
|
12494
|
+
deInversionEligible = false;
|
|
12495
|
+
deParallelRouterEligible = false;
|
|
12496
|
+
deShortBand = void 0;
|
|
12497
|
+
}
|
|
12498
|
+
}
|
|
12499
|
+
const deProbeGateReason = isProbeDeGated(probeSession) ? probeSession.deGateReason : void 0;
|
|
12399
12500
|
let workerCount;
|
|
12400
12501
|
const captureStageObservationData = (extra = {}) => ({
|
|
12401
12502
|
stagePhase: "capturing",
|
|
@@ -12583,7 +12684,7 @@ async function executeRenderPipeline(input) {
|
|
|
12583
12684
|
}
|
|
12584
12685
|
const parallelCaptureMode = resolveParallelCaptureMode({
|
|
12585
12686
|
platform: process.platform,
|
|
12586
|
-
|
|
12687
|
+
headlessShellPath: resolveHeadlessShellPath(cfg),
|
|
12587
12688
|
forceScreenshot: captureForceScreenshot,
|
|
12588
12689
|
deviceScaleFactor: captureOptions.deviceScaleFactor
|
|
12589
12690
|
});
|
|
@@ -13355,7 +13456,9 @@ async function executeRenderPipeline(input) {
|
|
|
13355
13456
|
outputFormat,
|
|
13356
13457
|
durationSeconds: job.duration,
|
|
13357
13458
|
fps: fpsToNumber9(job.config.fps),
|
|
13358
|
-
expectedFrames: captureTotalFrames
|
|
13459
|
+
expectedFrames: captureTotalFrames,
|
|
13460
|
+
width: outputWidth,
|
|
13461
|
+
height: outputHeight
|
|
13359
13462
|
})
|
|
13360
13463
|
);
|
|
13361
13464
|
const totalElapsed = Date.now() - pipelineStart;
|
|
@@ -13391,6 +13494,7 @@ async function executeRenderPipeline(input) {
|
|
|
13391
13494
|
drawElement: {
|
|
13392
13495
|
compileGate: deCompileGate,
|
|
13393
13496
|
clampReason: deClampReason,
|
|
13497
|
+
probeGateReason: deProbeGateReason,
|
|
13394
13498
|
workerInversion: deWorkerInversion,
|
|
13395
13499
|
preInversionWorkers: deWorkerInversion ? preRoutingWorkerCount : void 0,
|
|
13396
13500
|
compositionElementCount,
|
|
@@ -13792,9 +13896,9 @@ import {
|
|
|
13792
13896
|
// src/services/distributed/planFrameCost.ts
|
|
13793
13897
|
import {
|
|
13794
13898
|
closeCaptureSession as closeCaptureSession9,
|
|
13795
|
-
createCaptureSession as
|
|
13899
|
+
createCaptureSession as createCaptureSession9,
|
|
13796
13900
|
createFrameLookupTable as createFrameLookupTable3,
|
|
13797
|
-
initializeSession as
|
|
13901
|
+
initializeSession as initializeSession9
|
|
13798
13902
|
} from "@hyperframes/engine";
|
|
13799
13903
|
|
|
13800
13904
|
// src/services/distributed/chunkFrames.ts
|
|
@@ -13853,7 +13957,7 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
13853
13957
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
13854
13958
|
|
|
13855
13959
|
// src/services/distributed/chunkAudio.ts
|
|
13856
|
-
import { appendRenderProvenanceArgs, describeFfmpegFailure as
|
|
13960
|
+
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
|
|
13857
13961
|
|
|
13858
13962
|
// src/services/distributed/mp4Stitch.ts
|
|
13859
13963
|
var SCAN_BYTES = 64 * 1024;
|
|
@@ -68,6 +68,7 @@ export declare function assemble(planDir: string, chunkOutputs: AssembleChunkPat
|
|
|
68
68
|
*/
|
|
69
69
|
cfr?: boolean;
|
|
70
70
|
}): Promise<AssembleResult>;
|
|
71
|
-
/** Chunks
|
|
71
|
+
/** Chunks carrying audio, or with it as a separate file, stitch under one index like S3's. */
|
|
72
72
|
export declare function chunksAreStitchedMp4(planDir: string, cfr: boolean): boolean;
|
|
73
|
+
export declare function stitchesSeparateAudio(planDir: string): boolean;
|
|
73
74
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, type EngineConfig, type ExtractedFrames, type FrameLookupTable } from "@hyperframes/engine";
|
|
1
|
+
import { type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, type EngineConfig, type ExtractedFrames, type FrameLookupTable, type ShownBox } from "@hyperframes/engine";
|
|
2
2
|
import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
|
|
3
3
|
import type { PlanV2RemoteSources } from "./planV2.js";
|
|
4
4
|
import { type DistributedFormat, INVALID_VIDEO_METADATA, type PlanVideosJson } from "./shared.js";
|
|
@@ -39,6 +39,10 @@ export declare function buildChunkCaptureOptions(input: {
|
|
|
39
39
|
videoMetadataHints?: CaptureOptions["videoMetadataHints"];
|
|
40
40
|
}): CaptureOptions;
|
|
41
41
|
/** Extracts the given frames of deferred videos under the names a full extraction gives them. */
|
|
42
|
+
export declare function shownDeferredFrameIndexes(planVideos: PlanVideosJson, frames: Iterable<number>, fps: {
|
|
43
|
+
num: number;
|
|
44
|
+
den: number;
|
|
45
|
+
}): Map<string, Set<number>>;
|
|
42
46
|
export declare function extractDeferredVideoFramesForChunk(input: {
|
|
43
47
|
planDir: string;
|
|
44
48
|
planVideos: PlanVideosJson;
|
|
@@ -50,4 +54,5 @@ export declare function extractDeferredVideoFramesForChunk(input: {
|
|
|
50
54
|
outputRoot: string;
|
|
51
55
|
cfg: Pick<EngineConfig, "ffmpegProcessTimeout">;
|
|
52
56
|
remoteSources?: PlanV2RemoteSources;
|
|
57
|
+
shownBoxes?: ReadonlyMap<string, ShownBox>;
|
|
53
58
|
}): Promise<ExtractedFrames[]>;
|