@hyperframes/producer 0.8.183 → 0.8.185
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.js +75 -42
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -65
- package/dist/index.d.ts +1 -0
- package/dist/index.js +203 -73
- package/dist/public-server.js +158 -58
- package/dist/services/distributed/chunkFrames.d.ts +1 -1
- package/dist/services/distributed/renderChunk.d.ts +9 -2
- package/dist/services/render/stages/captureStage.d.ts +2 -1
- package/dist/services/render/stages/encodeStage.d.ts +26 -2
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { type BrowserDiagnosticSummary, type RenderCaptureObservability, type Re
|
|
|
12
12
|
export { localizeRemoteMediaSources, localizeRemoteImageSources, localizeRemoteFontFaces, } from "./services/htmlCompiler.js";
|
|
13
13
|
export { createCaptureSession, initializeSession, closeCaptureSession, captureFrame, captureFrameToBuffer, getCompositionDuration, getCapturePerfSummary, isTransientBrowserError, prepareCaptureSessionForReuse, type CaptureOptions, type CaptureSession, type CaptureResult, type CapturePerfSummary, type BeforeCaptureHook, } from "./services/frameCapture.js";
|
|
14
14
|
export { createFileServer, type FileServerOptions, type FileServerHandle, } from "./services/fileServer.js";
|
|
15
|
+
export { prepareAnimatedGifInputs } from "./services/animatedGifPrep.js";
|
|
15
16
|
export { createVideoFrameInjector } from "@hyperframes/engine";
|
|
16
17
|
export { resolveConfig, DEFAULT_CONFIG, type ProducerConfig } from "./config.js";
|
|
17
18
|
export { type ProducerLogger, type LogLevel, createConsoleLogger, defaultLogger, } from "./logger.js";
|
package/dist/index.js
CHANGED
|
@@ -8982,6 +8982,7 @@ async function runCaptureStage(input) {
|
|
|
8982
8982
|
if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
|
|
8983
8983
|
return;
|
|
8984
8984
|
}
|
|
8985
|
+
if (progress.landedFrame) input.onFrameLanded?.(progress.landedFrame);
|
|
8985
8986
|
job.framesRendered = progress.capturedFrames;
|
|
8986
8987
|
checkDiskProjection(progress.capturedFrames);
|
|
8987
8988
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
@@ -10151,6 +10152,7 @@ import {
|
|
|
10151
10152
|
statSync as statSync5,
|
|
10152
10153
|
writeFileSync as writeFileSync4
|
|
10153
10154
|
} from "node:fs";
|
|
10155
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
10154
10156
|
import { dirname as dirname8, join as join24 } from "node:path";
|
|
10155
10157
|
import {
|
|
10156
10158
|
encodeFramesChunkedConcat,
|
|
@@ -10158,7 +10160,8 @@ import {
|
|
|
10158
10160
|
formatFfmpegError,
|
|
10159
10161
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
|
|
10160
10162
|
resolveConfig,
|
|
10161
|
-
runFfmpeg as runFfmpeg3
|
|
10163
|
+
runFfmpeg as runFfmpeg3,
|
|
10164
|
+
spawnStreamingEncoder as spawnStreamingEncoder4
|
|
10162
10165
|
} from "@hyperframes/engine";
|
|
10163
10166
|
import {
|
|
10164
10167
|
clearGifFramesBeforeNext,
|
|
@@ -10295,6 +10298,124 @@ function startEncodeProgress(job, stage, onProgress) {
|
|
|
10295
10298
|
});
|
|
10296
10299
|
return (frames = total) => reportEncodeProgress(job, frames, total, onProgress, 75);
|
|
10297
10300
|
}
|
|
10301
|
+
function resolveEngineConfig(input) {
|
|
10302
|
+
return input.engineConfig ?? input.job.config.producerConfig ?? resolveConfig();
|
|
10303
|
+
}
|
|
10304
|
+
function videoEncoderOptions(input, engineCfg) {
|
|
10305
|
+
const { job, preset } = input;
|
|
10306
|
+
return {
|
|
10307
|
+
fps: job.config.fps,
|
|
10308
|
+
width: input.width,
|
|
10309
|
+
height: input.height,
|
|
10310
|
+
codec: preset.codec,
|
|
10311
|
+
preset: preset.preset,
|
|
10312
|
+
quality: input.effectiveQuality,
|
|
10313
|
+
bitrate: input.effectiveBitrate,
|
|
10314
|
+
pixelFormat: preset.pixelFormat,
|
|
10315
|
+
vp9CpuUsed: engineCfg.vp9CpuUsed,
|
|
10316
|
+
useGpu: job.config.useGpu,
|
|
10317
|
+
hdr: preset.hdr,
|
|
10318
|
+
// Distributed chunk renders pass these so the encoder writes closed-GOP
|
|
10319
|
+
// keyframes that survive `-f concat -c copy` at assemble time. In-process
|
|
10320
|
+
// renders leave both undefined → preserves the existing open-GOP output.
|
|
10321
|
+
lockGopForChunkConcat: input.lockGopForChunkConcat === true,
|
|
10322
|
+
gopSize: input.gopSize
|
|
10323
|
+
};
|
|
10324
|
+
}
|
|
10325
|
+
function canEncodeWhileCapturing(input, workerCount, cpuCount) {
|
|
10326
|
+
return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg" && input.lockGopForChunkConcat !== true;
|
|
10327
|
+
}
|
|
10328
|
+
async function readLandedFrame(workerPath, mergedPath) {
|
|
10329
|
+
if (workerPath !== void 0) {
|
|
10330
|
+
try {
|
|
10331
|
+
return await readFile2(workerPath);
|
|
10332
|
+
} catch (error) {
|
|
10333
|
+
if (error.code !== "ENOENT") throw error;
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10336
|
+
return readFile2(mergedPath);
|
|
10337
|
+
}
|
|
10338
|
+
function encodeWhileCapturing(input, totalFrames) {
|
|
10339
|
+
const engineCfg = resolveEngineConfig(input);
|
|
10340
|
+
const stop = new AbortController();
|
|
10341
|
+
const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, stop.signal]) : stop.signal;
|
|
10342
|
+
const landed = /* @__PURE__ */ new Map();
|
|
10343
|
+
let captureDone = false;
|
|
10344
|
+
let framesFed = 0;
|
|
10345
|
+
let wake = null;
|
|
10346
|
+
const nudge = () => {
|
|
10347
|
+
wake?.();
|
|
10348
|
+
wake = null;
|
|
10349
|
+
};
|
|
10350
|
+
signal.addEventListener("abort", nudge, { once: true });
|
|
10351
|
+
let reportFed = null;
|
|
10352
|
+
const encoder = spawnStreamingEncoder4(
|
|
10353
|
+
input.videoOnlyPath,
|
|
10354
|
+
{ ...videoEncoderOptions(input, engineCfg), imageFormat: "jpeg" },
|
|
10355
|
+
signal,
|
|
10356
|
+
engineCfg
|
|
10357
|
+
);
|
|
10358
|
+
const fed = (async () => {
|
|
10359
|
+
const stream = await encoder;
|
|
10360
|
+
for (let next = 0; next < totalFrames; next++) {
|
|
10361
|
+
while (!landed.has(next) && !captureDone && !signal.aborted) {
|
|
10362
|
+
await new Promise((resolve12) => wake = resolve12);
|
|
10363
|
+
}
|
|
10364
|
+
if (signal.aborted) return;
|
|
10365
|
+
const mergedPath = join24(input.framesDir, formatCaptureFrameName(next, "jpg"));
|
|
10366
|
+
const frame = await readLandedFrame(landed.get(next), mergedPath);
|
|
10367
|
+
landed.delete(next);
|
|
10368
|
+
if (!await stream.writeFrame(frame)) {
|
|
10369
|
+
throw new Error(stream.getExitError() ?? "encoder exited before the last frame");
|
|
10370
|
+
}
|
|
10371
|
+
framesFed++;
|
|
10372
|
+
reportFed?.(framesFed);
|
|
10373
|
+
}
|
|
10374
|
+
})().then(
|
|
10375
|
+
() => void 0,
|
|
10376
|
+
(error) => error ?? new Error("encode failed")
|
|
10377
|
+
);
|
|
10378
|
+
const closeEncoder = async () => (await encoder.catch(() => null))?.close();
|
|
10379
|
+
return {
|
|
10380
|
+
onFrameLanded: (frame) => {
|
|
10381
|
+
landed.set(frame.index, frame.path);
|
|
10382
|
+
nudge();
|
|
10383
|
+
},
|
|
10384
|
+
finish: async () => {
|
|
10385
|
+
const start = Date.now();
|
|
10386
|
+
captureDone = true;
|
|
10387
|
+
input.log.info("[Render] Encoded frames while capturing", {
|
|
10388
|
+
framesFed,
|
|
10389
|
+
totalFrames
|
|
10390
|
+
});
|
|
10391
|
+
const encoded = startEncodeProgress(input.job, "Encoding video", input.onProgress);
|
|
10392
|
+
reportFed = encoded;
|
|
10393
|
+
nudge();
|
|
10394
|
+
const failure = await fed;
|
|
10395
|
+
const result = await closeEncoder();
|
|
10396
|
+
input.assertNotAborted();
|
|
10397
|
+
if (failure === void 0 && result?.success) {
|
|
10398
|
+
encoded();
|
|
10399
|
+
return { encodeMs: Date.now() - start };
|
|
10400
|
+
}
|
|
10401
|
+
input.log.warn(
|
|
10402
|
+
"[Render] Encoding while capturing failed; encoding the frames after capture",
|
|
10403
|
+
{
|
|
10404
|
+
error: String(failure ?? result?.error)
|
|
10405
|
+
}
|
|
10406
|
+
);
|
|
10407
|
+
return runEncodeStage(input);
|
|
10408
|
+
},
|
|
10409
|
+
abort: async () => {
|
|
10410
|
+
stop.abort();
|
|
10411
|
+
await fed;
|
|
10412
|
+
await closeEncoder();
|
|
10413
|
+
},
|
|
10414
|
+
get framesFed() {
|
|
10415
|
+
return framesFed;
|
|
10416
|
+
}
|
|
10417
|
+
};
|
|
10418
|
+
}
|
|
10298
10419
|
async function runEncodeStage(input) {
|
|
10299
10420
|
const {
|
|
10300
10421
|
job,
|
|
@@ -10302,16 +10423,11 @@ async function runEncodeStage(input) {
|
|
|
10302
10423
|
outputPath,
|
|
10303
10424
|
framesDir,
|
|
10304
10425
|
videoOnlyPath,
|
|
10305
|
-
width,
|
|
10306
|
-
height,
|
|
10307
10426
|
needsAlpha,
|
|
10308
10427
|
hasAudio,
|
|
10309
10428
|
audioOutputPath,
|
|
10310
10429
|
isPngSequence,
|
|
10311
10430
|
isGif,
|
|
10312
|
-
preset,
|
|
10313
|
-
effectiveQuality,
|
|
10314
|
-
effectiveBitrate,
|
|
10315
10431
|
enableChunkedEncode,
|
|
10316
10432
|
chunkedEncodeSize,
|
|
10317
10433
|
abortSignal,
|
|
@@ -10341,7 +10457,7 @@ async function runEncodeStage(input) {
|
|
|
10341
10457
|
encoded();
|
|
10342
10458
|
return { encodeMs: Date.now() - stage5Start };
|
|
10343
10459
|
}
|
|
10344
|
-
const engineCfg = input
|
|
10460
|
+
const engineCfg = resolveEngineConfig(input);
|
|
10345
10461
|
if (isGif) {
|
|
10346
10462
|
const encoded = startEncodeProgress(job, "Encoding GIF", onProgress);
|
|
10347
10463
|
if (hasAudio) {
|
|
@@ -10371,24 +10487,7 @@ async function runEncodeStage(input) {
|
|
|
10371
10487
|
const videoEngineCfg = scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout ? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout } : engineCfg;
|
|
10372
10488
|
const frameExt = frameFileExtension2(input.captureImageFormat);
|
|
10373
10489
|
const framePattern = `frame_%06d.${frameExt}`;
|
|
10374
|
-
const encoderOpts =
|
|
10375
|
-
fps: job.config.fps,
|
|
10376
|
-
width,
|
|
10377
|
-
height,
|
|
10378
|
-
codec: preset.codec,
|
|
10379
|
-
preset: preset.preset,
|
|
10380
|
-
quality: effectiveQuality,
|
|
10381
|
-
bitrate: effectiveBitrate,
|
|
10382
|
-
pixelFormat: preset.pixelFormat,
|
|
10383
|
-
vp9CpuUsed: engineCfg.vp9CpuUsed,
|
|
10384
|
-
useGpu: job.config.useGpu,
|
|
10385
|
-
hdr: preset.hdr,
|
|
10386
|
-
// Distributed chunk renders pass these so the encoder writes closed-GOP
|
|
10387
|
-
// keyframes that survive `-f concat -c copy` at assemble time. In-process
|
|
10388
|
-
// renders leave both undefined → preserves the existing open-GOP output.
|
|
10389
|
-
lockGopForChunkConcat: input.lockGopForChunkConcat === true,
|
|
10390
|
-
gopSize: input.gopSize
|
|
10391
|
-
};
|
|
10490
|
+
const encoderOpts = videoEncoderOptions(input, engineCfg);
|
|
10392
10491
|
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
10393
10492
|
framesDir,
|
|
10394
10493
|
framePattern,
|
|
@@ -10672,7 +10771,6 @@ async function enforceAacTruePeak(input) {
|
|
|
10672
10771
|
try {
|
|
10673
10772
|
scratchDir = mkdtempSync3(join25(dirname9(input.audioPath), ".true-peak-"));
|
|
10674
10773
|
const correctedPath = join25(scratchDir, "audio.m4a");
|
|
10675
|
-
const correctionTargetDbfs = AAC_DELIVERY_TRUE_PEAK_DBFS - AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB;
|
|
10676
10774
|
const measurements = [];
|
|
10677
10775
|
let attenuationDb = 0;
|
|
10678
10776
|
let measuredPath = input.audioPath;
|
|
@@ -10690,7 +10788,8 @@ async function enforceAacTruePeak(input) {
|
|
|
10690
10788
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
10691
10789
|
}
|
|
10692
10790
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
10693
|
-
|
|
10791
|
+
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
10792
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
|
|
10694
10793
|
const result = await input.encode(
|
|
10695
10794
|
input.audioPath,
|
|
10696
10795
|
correctedPath,
|
|
@@ -13313,6 +13412,34 @@ async function executeRenderPipeline(input) {
|
|
|
13313
13412
|
throw new Error(`Disk capture requires sdr_disk plan; got ${capturePlan.kind}`);
|
|
13314
13413
|
}
|
|
13315
13414
|
resetCaptureAttemptProgress(job);
|
|
13415
|
+
const encodeInput = {
|
|
13416
|
+
job,
|
|
13417
|
+
log,
|
|
13418
|
+
outputPath: stagedOutputPath,
|
|
13419
|
+
framesDir,
|
|
13420
|
+
videoOnlyPath,
|
|
13421
|
+
width,
|
|
13422
|
+
height,
|
|
13423
|
+
needsAlpha,
|
|
13424
|
+
captureImageFormat: captureOptions.format ?? "jpeg",
|
|
13425
|
+
hasAudio,
|
|
13426
|
+
audioOutputPath,
|
|
13427
|
+
isPngSequence,
|
|
13428
|
+
isGif,
|
|
13429
|
+
preset,
|
|
13430
|
+
effectiveQuality,
|
|
13431
|
+
effectiveBitrate,
|
|
13432
|
+
enableChunkedEncode,
|
|
13433
|
+
chunkedEncodeSize,
|
|
13434
|
+
engineConfig: cfg,
|
|
13435
|
+
// Same value the streaming encoder above spreads — the disk and
|
|
13436
|
+
// chunked-concat encoders need an identical GOP lock for HLS.
|
|
13437
|
+
...hlsEncoderGopLock,
|
|
13438
|
+
abortSignal: executionSignal,
|
|
13439
|
+
assertNotAborted,
|
|
13440
|
+
onProgress
|
|
13441
|
+
};
|
|
13442
|
+
const overlap = !cfg.useDrawElement && workerSizing !== void 0 && canEncodeWhileCapturing(encodeInput, capturePlan.workerCount, workerSizing.cpuCount) ? encodeWhileCapturing(encodeInput, totalFrames) : null;
|
|
13316
13443
|
const captureFrameStart = Date.now();
|
|
13317
13444
|
const invokeDiskCapture = (diskPlan) => observeRenderStage(
|
|
13318
13445
|
observability,
|
|
@@ -13334,13 +13461,15 @@ async function executeRenderPipeline(input) {
|
|
|
13334
13461
|
createRenderVideoFrameInjector,
|
|
13335
13462
|
abortSignal: executionSignal,
|
|
13336
13463
|
assertNotAborted,
|
|
13337
|
-
onProgress
|
|
13464
|
+
onProgress,
|
|
13465
|
+
onFrameLanded: (frame) => overlap?.onFrameLanded(frame)
|
|
13338
13466
|
})
|
|
13339
13467
|
);
|
|
13340
13468
|
let captureRes;
|
|
13341
13469
|
try {
|
|
13342
13470
|
captureRes = await invokeDiskCapture(capturePlan);
|
|
13343
13471
|
} catch (err) {
|
|
13472
|
+
await overlap?.abort();
|
|
13344
13473
|
if (!isDrawElementVerificationError(err) && !isDrawElementCaptureError(err) || err instanceof RenderCancelledError || executionSignal?.aborted === true) {
|
|
13345
13474
|
throw err;
|
|
13346
13475
|
}
|
|
@@ -13409,33 +13538,7 @@ async function executeRenderPipeline(input) {
|
|
|
13409
13538
|
isGif,
|
|
13410
13539
|
chunkedEncode: enableChunkedEncode
|
|
13411
13540
|
}),
|
|
13412
|
-
() => runEncodeStage(
|
|
13413
|
-
job,
|
|
13414
|
-
log,
|
|
13415
|
-
outputPath: stagedOutputPath,
|
|
13416
|
-
framesDir,
|
|
13417
|
-
videoOnlyPath,
|
|
13418
|
-
width,
|
|
13419
|
-
height,
|
|
13420
|
-
needsAlpha,
|
|
13421
|
-
captureImageFormat: captureOptions.format ?? "jpeg",
|
|
13422
|
-
hasAudio,
|
|
13423
|
-
audioOutputPath,
|
|
13424
|
-
isPngSequence,
|
|
13425
|
-
isGif,
|
|
13426
|
-
preset,
|
|
13427
|
-
effectiveQuality,
|
|
13428
|
-
effectiveBitrate,
|
|
13429
|
-
enableChunkedEncode,
|
|
13430
|
-
chunkedEncodeSize,
|
|
13431
|
-
engineConfig: cfg,
|
|
13432
|
-
// Same value the streaming encoder above spreads — the disk and
|
|
13433
|
-
// chunked-concat encoders need an identical GOP lock for HLS.
|
|
13434
|
-
...hlsEncoderGopLock,
|
|
13435
|
-
abortSignal: executionSignal,
|
|
13436
|
-
assertNotAborted,
|
|
13437
|
-
onProgress
|
|
13438
|
-
})
|
|
13541
|
+
() => overlap ? overlap.finish() : runEncodeStage(encodeInput)
|
|
13439
13542
|
);
|
|
13440
13543
|
perfStages.encodeMs = encodeRes.encodeMs;
|
|
13441
13544
|
}
|
|
@@ -14042,7 +14145,14 @@ var RenderChunkValidationError = class extends Error {
|
|
|
14042
14145
|
}
|
|
14043
14146
|
};
|
|
14044
14147
|
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
14045
|
-
return () => createVideoFrameInjector2(frameLookup);
|
|
14148
|
+
if (!(frameLookup instanceof Promise)) return () => createVideoFrameInjector2(frameLookup);
|
|
14149
|
+
return () => {
|
|
14150
|
+
let hook;
|
|
14151
|
+
return async (page, time, heldVideoTime) => {
|
|
14152
|
+
hook ??= createVideoFrameInjector2(await frameLookup);
|
|
14153
|
+
await hook?.(page, time, heldVideoTime);
|
|
14154
|
+
};
|
|
14155
|
+
};
|
|
14046
14156
|
}
|
|
14047
14157
|
function frameNumberFromFileName(name) {
|
|
14048
14158
|
const match = /(\d+)(?=\.[^.]+$)/.exec(name);
|
|
@@ -17063,9 +17173,6 @@ function validateOutputResolutionOverride(body) {
|
|
|
17063
17173
|
if (body.outputResolution.trim().length > 0 && normalized === void 0) {
|
|
17064
17174
|
return `Invalid outputResolution "${body.outputResolution}". Must be one of: landscape, portrait, landscape-4k, portrait-4k, square, square-4k (aliases: 1080p, 4k, \u2026).`;
|
|
17065
17175
|
}
|
|
17066
|
-
if (normalized !== void 0 && (body.format === "webm" || body.format === "mov")) {
|
|
17067
|
-
return `outputResolution is not supported with format "${body.format}": the alpha (webm/mov) capture path can't supersample. Use format "mp4", or omit outputResolution to render at the composition's native dimensions.`;
|
|
17068
|
-
}
|
|
17069
17176
|
return void 0;
|
|
17070
17177
|
}
|
|
17071
17178
|
function prepareProjectDirectory(projectDir, options) {
|
|
@@ -18794,6 +18901,12 @@ function validatePlanVideosForChunk(value) {
|
|
|
18794
18901
|
);
|
|
18795
18902
|
}
|
|
18796
18903
|
}
|
|
18904
|
+
var MIN_FRAMES_PER_CHUNK_BROWSER = 20;
|
|
18905
|
+
function resolveChunkCaptureWorkers(framesInChunk, machineSized, maxCaptureWorkers) {
|
|
18906
|
+
if (maxCaptureWorkers === void 0) return machineSized;
|
|
18907
|
+
const byFrames = Math.floor(framesInChunk / MIN_FRAMES_PER_CHUNK_BROWSER);
|
|
18908
|
+
return Math.max(machineSized, Math.floor(Math.min(maxCaptureWorkers, byFrames)));
|
|
18909
|
+
}
|
|
18797
18910
|
function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
|
|
18798
18911
|
if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
|
|
18799
18912
|
const { startFrame, endFrame } = frameRange;
|
|
@@ -19030,7 +19143,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19030
19143
|
deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
|
|
19031
19144
|
)
|
|
19032
19145
|
});
|
|
19033
|
-
const chunkWorkerCount =
|
|
19146
|
+
const chunkWorkerCount = resolveChunkCaptureWorkers(
|
|
19147
|
+
framesInChunk,
|
|
19148
|
+
calculateOptimalWorkers2(framesInChunk, void 0, cfg),
|
|
19149
|
+
options?.maxCaptureWorkers
|
|
19150
|
+
);
|
|
19034
19151
|
let session = null;
|
|
19035
19152
|
let outputKind;
|
|
19036
19153
|
let framesEncoded = 0;
|
|
@@ -19042,6 +19159,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19042
19159
|
const captureAttempts = [];
|
|
19043
19160
|
let forceScreenshotForChunk = encoder.forceScreenshot;
|
|
19044
19161
|
let shownBoxes;
|
|
19162
|
+
let extraction;
|
|
19045
19163
|
try {
|
|
19046
19164
|
if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
|
|
19047
19165
|
const bootStarted = Date.now();
|
|
@@ -19098,7 +19216,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19098
19216
|
session = null;
|
|
19099
19217
|
}
|
|
19100
19218
|
}
|
|
19101
|
-
const deferredVideoFrames = planVideos ?
|
|
19219
|
+
const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
|
|
19102
19220
|
planDir,
|
|
19103
19221
|
planVideos,
|
|
19104
19222
|
frames: chunkFrameNumbers,
|
|
@@ -19107,19 +19225,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19107
19225
|
cfg,
|
|
19108
19226
|
remoteSources,
|
|
19109
19227
|
shownBoxes
|
|
19110
|
-
}) : [];
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
|
|
19116
|
-
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19228
|
+
}) : Promise.resolve([]);
|
|
19229
|
+
extraction = deferredVideoFrames;
|
|
19230
|
+
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
|
|
19231
|
+
(deferred) => createFrameLookupTable5(
|
|
19232
|
+
planVideos.videos,
|
|
19233
|
+
[
|
|
19234
|
+
...rebuildExtractedFramesFromPlanDir(
|
|
19235
|
+
planDir,
|
|
19236
|
+
planVideos.extracted.filter((video) => !video.deferredRange),
|
|
19237
|
+
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
19238
|
+
),
|
|
19239
|
+
...deferred
|
|
19240
|
+
],
|
|
19241
|
+
resolveRenderFpsConfig(job.config.fps).value
|
|
19242
|
+
)
|
|
19122
19243
|
) : null;
|
|
19244
|
+
videoFrameLookup?.catch(() => {
|
|
19245
|
+
});
|
|
19123
19246
|
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
|
|
19124
19247
|
const captureStarted = Date.now();
|
|
19125
19248
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
@@ -19202,7 +19325,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19202
19325
|
}
|
|
19203
19326
|
);
|
|
19204
19327
|
}
|
|
19328
|
+
}).catch(async (captureError) => {
|
|
19329
|
+
await deferredVideoFrames;
|
|
19330
|
+
throw captureError;
|
|
19205
19331
|
});
|
|
19332
|
+
await deferredVideoFrames;
|
|
19206
19333
|
captureStageMs = Date.now() - captureStarted;
|
|
19207
19334
|
applyRenderWarningPolicy(
|
|
19208
19335
|
job,
|
|
@@ -19281,6 +19408,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19281
19408
|
}
|
|
19282
19409
|
encodeStageMs = Date.now() - encodeStarted;
|
|
19283
19410
|
} finally {
|
|
19411
|
+
await extraction?.catch(() => {
|
|
19412
|
+
});
|
|
19284
19413
|
if (session) {
|
|
19285
19414
|
try {
|
|
19286
19415
|
await closeCaptureSession11(session);
|
|
@@ -19447,6 +19576,7 @@ export {
|
|
|
19447
19576
|
plan,
|
|
19448
19577
|
planV2,
|
|
19449
19578
|
planV2WithPublisher,
|
|
19579
|
+
prepareAnimatedGifInputs,
|
|
19450
19580
|
prepareCaptureSessionForReuse4 as prepareCaptureSessionForReuse,
|
|
19451
19581
|
prepareHyperframeLintBody,
|
|
19452
19582
|
publishPlanV2FromExecutionPlan,
|