@hyperframes/producer 0.8.199 → 0.8.200
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 +42 -21
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -62
- package/dist/index.js +109 -30
- package/dist/public-server.js +77 -26
- package/dist/services/distributed/chunkWeights.d.ts +2 -0
- package/dist/services/htmlCompiler.d.ts +0 -2
- package/dist/services/render/audioPadTrim.d.ts +2 -0
- package/dist/services/render/stages/compileStage.d.ts +1 -1
- package/dist/services/render/stages/freezePlan.d.ts +1 -0
- package/dist/services/renderOrchestrator.d.ts +2 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -2094,6 +2094,7 @@ function aggregateDrawElement(perfs, de) {
|
|
|
2094
2094
|
blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
|
|
2095
2095
|
blankRecaptures: drain?.blankRecaptures ?? 0,
|
|
2096
2096
|
boundaryFrames: perfs.reduce((sum2, p) => sum2 + (p.deBoundaryFrames ?? 0), 0),
|
|
2097
|
+
riskFrames: perfs.reduce((sum2, p) => sum2 + (p.deRiskFrames ?? 0), 0),
|
|
2097
2098
|
ncprFallbacks: perfs.reduce((sum2, p) => sum2 + (p.deNcprFallbacks ?? 0), 0),
|
|
2098
2099
|
frameTimeouts: perfs.reduce((sum2, p) => sum2 + (p.deFrameTimeouts ?? 0), 0)
|
|
2099
2100
|
};
|
|
@@ -3730,11 +3731,21 @@ function createDrainFrameGuard(args) {
|
|
|
3730
3731
|
return buf;
|
|
3731
3732
|
};
|
|
3732
3733
|
}
|
|
3733
|
-
async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEncoder, reorderBuffer, assertNotAborted, onProgress, log, stats, abortSignal) {
|
|
3734
|
+
async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEncoder, reorderBuffer, assertNotAborted, onProgress, log, stats, abortSignal, risk) {
|
|
3734
3735
|
let prev = null;
|
|
3735
3736
|
const frameTime = (i) => i * job.config.fps.den / job.config.fps.num;
|
|
3736
3737
|
const guard = createDrainFrameGuard({ log, stats, frameTime });
|
|
3737
|
-
const guardFrame = (
|
|
3738
|
+
const guardFrame = (item, buf) => item.trusted ? Promise.resolve(buf) : guard(session, item.idx, buf);
|
|
3739
|
+
const captureRisk = async (idx) => {
|
|
3740
|
+
await risk.open();
|
|
3741
|
+
lastProgressAt = Date.now();
|
|
3742
|
+
return raceAgainstStall(
|
|
3743
|
+
risk.capture(idx, frameTime(idx)),
|
|
3744
|
+
stallTimeoutMs - (Date.now() - lastProgressAt),
|
|
3745
|
+
{ session, frameIndex: idx, totalFrames, stallTimeoutMs },
|
|
3746
|
+
abortSignal
|
|
3747
|
+
);
|
|
3748
|
+
};
|
|
3738
3749
|
const stallTimeoutMs = resolveCaptureStallTimeoutMs();
|
|
3739
3750
|
let lastProgressAt = Date.now();
|
|
3740
3751
|
const captureWithStallGuard = (idx, promise) => raceAgainstStall(
|
|
@@ -3746,7 +3757,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3746
3757
|
const drainPrev = async () => {
|
|
3747
3758
|
if (!prev) return;
|
|
3748
3759
|
assertNotAborted();
|
|
3749
|
-
const buf = await guardFrame(prev
|
|
3760
|
+
const buf = await guardFrame(prev, await prev.encodeResult);
|
|
3750
3761
|
await reorderBuffer.waitForFrame(prev.idx);
|
|
3751
3762
|
ensureFrameWritten(await currentEncoder.writeFrame(buf), prev.idx, currentEncoder);
|
|
3752
3763
|
reorderBuffer.advanceTo(prev.idx + 1);
|
|
@@ -3766,7 +3777,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3766
3777
|
const drainBatch = async (batch) => {
|
|
3767
3778
|
for (const item of batch) {
|
|
3768
3779
|
assertNotAborted();
|
|
3769
|
-
const buf = await guardFrame(item
|
|
3780
|
+
const buf = await guardFrame(item, await item.encodeResult);
|
|
3770
3781
|
await reorderBuffer.waitForFrame(item.idx);
|
|
3771
3782
|
ensureFrameWritten(await currentEncoder.writeFrame(buf), item.idx, currentEncoder);
|
|
3772
3783
|
reorderBuffer.advanceTo(item.idx + 1);
|
|
@@ -3784,7 +3795,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3784
3795
|
};
|
|
3785
3796
|
if (batchN > 1 && !session.onBeforeCapture) {
|
|
3786
3797
|
const boundarySS = process.env.HF_FAST_CAPTURE_BOUNDARY_SS === "true";
|
|
3787
|
-
const batchable = (f) => !session.staticFrames?.has(f) && !(boundarySS && session.clipBoundaryFrames?.has(f));
|
|
3798
|
+
const batchable = (f) => !session.staticFrames?.has(f) && !(boundarySS && session.clipBoundaryFrames?.has(f)) && !risk?.frames.has(f);
|
|
3788
3799
|
let prevBatch = [];
|
|
3789
3800
|
let i = 0;
|
|
3790
3801
|
while (i < totalFrames) {
|
|
@@ -3801,6 +3812,11 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3801
3812
|
);
|
|
3802
3813
|
await drainBatch(prevBatch);
|
|
3803
3814
|
prevBatch = results.map((r) => ({ idx: r.frameIndex, encodeResult: r.encodeResult }));
|
|
3815
|
+
} else if (risk?.frames.has(i)) {
|
|
3816
|
+
const buffer = await captureRisk(i);
|
|
3817
|
+
await drainBatch(prevBatch);
|
|
3818
|
+
prevBatch = [{ idx: i, encodeResult: Promise.resolve(buffer), trusted: true }];
|
|
3819
|
+
i++;
|
|
3804
3820
|
} else {
|
|
3805
3821
|
const { encodeResult } = await captureWithStallGuard(
|
|
3806
3822
|
i,
|
|
@@ -3816,6 +3832,12 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3816
3832
|
}
|
|
3817
3833
|
for (let i = 0; i < totalFrames; i++) {
|
|
3818
3834
|
assertNotAborted();
|
|
3835
|
+
if (risk?.frames.has(i)) {
|
|
3836
|
+
const buffer = await captureRisk(i);
|
|
3837
|
+
await drainPrev();
|
|
3838
|
+
prev = { idx: i, encodeResult: Promise.resolve(buffer), trusted: true };
|
|
3839
|
+
continue;
|
|
3840
|
+
}
|
|
3819
3841
|
const time = frameTime(i);
|
|
3820
3842
|
const { encodeResult } = await captureWithStallGuard(
|
|
3821
3843
|
i,
|
|
@@ -4062,6 +4084,24 @@ async function runCaptureStreamingStage(input) {
|
|
|
4062
4084
|
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
4063
4085
|
probeSession = null;
|
|
4064
4086
|
}
|
|
4087
|
+
let riskSession = null;
|
|
4088
|
+
const openRiskSession = async () => {
|
|
4089
|
+
const opened = await createCaptureSession(
|
|
4090
|
+
fileServer.url,
|
|
4091
|
+
framesDir,
|
|
4092
|
+
buildCaptureOptions(),
|
|
4093
|
+
createRenderVideoFrameInjector(),
|
|
4094
|
+
{
|
|
4095
|
+
...captureCfg,
|
|
4096
|
+
useDrawElement: false,
|
|
4097
|
+
staticFrameDedup: false,
|
|
4098
|
+
enableBrowserPool: false
|
|
4099
|
+
}
|
|
4100
|
+
);
|
|
4101
|
+
riskSession = opened;
|
|
4102
|
+
await initializeSession(opened);
|
|
4103
|
+
return opened;
|
|
4104
|
+
};
|
|
4065
4105
|
try {
|
|
4066
4106
|
if (!session.isInitialized) {
|
|
4067
4107
|
await initializeSession(session);
|
|
@@ -4076,6 +4116,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4076
4116
|
blankDeterministicAccepts: 0,
|
|
4077
4117
|
blankRecaptures: 0
|
|
4078
4118
|
};
|
|
4119
|
+
const riskFrames = session.deRiskFrames;
|
|
4079
4120
|
await runWorkerEncodePipelineLoop(
|
|
4080
4121
|
session,
|
|
4081
4122
|
totalFrames,
|
|
@@ -4086,7 +4127,16 @@ async function runCaptureStreamingStage(input) {
|
|
|
4086
4127
|
onProgress,
|
|
4087
4128
|
log,
|
|
4088
4129
|
deDrainStats,
|
|
4089
|
-
abortSignal
|
|
4130
|
+
abortSignal,
|
|
4131
|
+
riskFrames && riskFrames.size > 0 ? {
|
|
4132
|
+
frames: riskFrames,
|
|
4133
|
+
// Opened on the first risk frame, in a browser of its own: pages sharing a
|
|
4134
|
+
// browser share a compositor, which starves the drawElement page's paint events.
|
|
4135
|
+
open: async () => {
|
|
4136
|
+
riskSession ??= await openRiskSession();
|
|
4137
|
+
},
|
|
4138
|
+
capture: async (frameIndex, time) => (await captureFrameToBuffer(riskSession, frameIndex, time)).buffer
|
|
4139
|
+
} : void 0
|
|
4090
4140
|
);
|
|
4091
4141
|
} else {
|
|
4092
4142
|
const stallTimeoutMs = resolveCaptureStallTimeoutMs();
|
|
@@ -4124,6 +4174,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4124
4174
|
} finally {
|
|
4125
4175
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
4126
4176
|
await closeCaptureSession(session);
|
|
4177
|
+
if (riskSession) await closeCaptureSession(riskSession);
|
|
4127
4178
|
}
|
|
4128
4179
|
}
|
|
4129
4180
|
const encodeFrom = job.progress;
|
|
@@ -4283,7 +4334,7 @@ function openSegmentedSessionReuse(input, session) {
|
|
|
4283
4334
|
async function openSegmentedSession(input) {
|
|
4284
4335
|
const captureCfg = input.cfg.forceScreenshot === input.plan.forceScreenshot ? input.cfg : { ...input.cfg, forceScreenshot: input.plan.forceScreenshot };
|
|
4285
4336
|
const videoInjector = input.createRenderVideoFrameInjector();
|
|
4286
|
-
const captureOptions = input.buildCaptureOptions();
|
|
4337
|
+
const captureOptions = { ...input.buildCaptureOptions(), deFrameFallback: false };
|
|
4287
4338
|
const ownBrowser = shouldDisableBrowserPoolForParallelWorker({
|
|
4288
4339
|
parallel: (input.workerCount ?? 1) > 1
|
|
4289
4340
|
});
|
|
@@ -6299,10 +6350,6 @@ var THREE_D_CONTEXT_PATTERN = /transform-style\s*:\s*preserve-3d|backface-visibi
|
|
|
6299
6350
|
function detectThreeDTransformUsage(html) {
|
|
6300
6351
|
return THREE_D_CONTEXT_PATTERN.test(html);
|
|
6301
6352
|
}
|
|
6302
|
-
var MIX_BLEND_MODE_PATTERN = /mix-blend-mode\s*:/i;
|
|
6303
|
-
function detectMixBlendModeUsage(html) {
|
|
6304
|
-
return MIX_BLEND_MODE_PATTERN.test(html);
|
|
6305
|
-
}
|
|
6306
6353
|
var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
|
|
6307
6354
|
function detectAncestorBackgroundImage(html) {
|
|
6308
6355
|
const { document: document2 } = parseHTML4(html);
|
|
@@ -7315,7 +7362,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7315
7362
|
const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
|
|
7316
7363
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
7317
7364
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
7318
|
-
const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
|
|
7319
7365
|
const hasAncestorBackgroundImage = detectAncestorBackgroundImage(sanitizedHtml);
|
|
7320
7366
|
const normalizedFontHtml = normalizeSystemFontPrimaryFamilies(
|
|
7321
7367
|
injectTextRenderingRule(
|
|
@@ -7429,7 +7475,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7429
7475
|
renderModeHints,
|
|
7430
7476
|
hasShaderTransitions,
|
|
7431
7477
|
usesThreeDTransforms,
|
|
7432
|
-
usesMixBlendMode,
|
|
7433
7478
|
hasAncestorBackgroundImage
|
|
7434
7479
|
};
|
|
7435
7480
|
}
|
|
@@ -7836,13 +7881,6 @@ async function runCompileStage(input) {
|
|
|
7836
7881
|
"[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7837
7882
|
);
|
|
7838
7883
|
}
|
|
7839
|
-
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
|
|
7840
|
-
cfg.useDrawElement = false;
|
|
7841
|
-
deCompileGate = "mix_blend_mode";
|
|
7842
|
-
log.info(
|
|
7843
|
-
"[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7844
|
-
);
|
|
7845
|
-
}
|
|
7846
7884
|
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
|
|
7847
7885
|
cfg.useDrawElement = false;
|
|
7848
7886
|
deCompileGate = "ancestor_background_image";
|
|
@@ -9273,10 +9311,11 @@ async function constrainAacTruePeak(input) {
|
|
|
9273
9311
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
9274
9312
|
return enforceAacTruePeak({
|
|
9275
9313
|
audioPath: input.audioPath,
|
|
9314
|
+
sourcePath: input.sourcePath,
|
|
9276
9315
|
targetDurationSeconds: input.targetDurationSeconds,
|
|
9277
9316
|
signal: input.signal,
|
|
9278
|
-
probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
|
|
9279
|
-
encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
9317
|
+
probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
|
|
9318
|
+
encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
9280
9319
|
});
|
|
9281
9320
|
}
|
|
9282
9321
|
async function enforceAacTruePeak(input) {
|
|
@@ -9286,6 +9325,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9286
9325
|
const correctedPath = join18(scratchDir, "audio.m4a");
|
|
9287
9326
|
const measurements = [];
|
|
9288
9327
|
let attenuationDb = 0;
|
|
9328
|
+
let sourcePeakDbfs;
|
|
9289
9329
|
let measuredPath = input.audioPath;
|
|
9290
9330
|
for (let pass = 0; pass <= MAX_TRUE_PEAK_CORRECTION_PASSES; pass += 1) {
|
|
9291
9331
|
const truePeakDbfs = await input.probeTruePeak(measuredPath, input.signal);
|
|
@@ -9302,7 +9342,11 @@ async function enforceAacTruePeak(input) {
|
|
|
9302
9342
|
}
|
|
9303
9343
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
9304
9344
|
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
9305
|
-
|
|
9345
|
+
if (input.sourcePath && sourcePeakDbfs === void 0) {
|
|
9346
|
+
sourcePeakDbfs = await input.probeTruePeak(input.sourcePath, input.signal);
|
|
9347
|
+
}
|
|
9348
|
+
const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
|
|
9349
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
|
|
9306
9350
|
const result = await input.encode(
|
|
9307
9351
|
input.audioPath,
|
|
9308
9352
|
correctedPath,
|
|
@@ -9738,7 +9782,7 @@ async function runCaptureStage(input) {
|
|
|
9738
9782
|
);
|
|
9739
9783
|
}
|
|
9740
9784
|
}
|
|
9741
|
-
const captureOptions = buildCaptureOptions();
|
|
9785
|
+
const captureOptions = { ...buildCaptureOptions(), deFrameFallback: false };
|
|
9742
9786
|
const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
|
|
9743
9787
|
const checkDiskProjection = createDiskCaptureProjection({
|
|
9744
9788
|
framesDir,
|
|
@@ -12192,6 +12236,9 @@ function resolveDeShortBand(args) {
|
|
|
12192
12236
|
function shouldPreferSingleWorkerDrawElement(args) {
|
|
12193
12237
|
return args.workerCount > 1 && 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;
|
|
12194
12238
|
}
|
|
12239
|
+
function probeNeedsScreenshotFrames(session) {
|
|
12240
|
+
return (session?.deRiskFrames?.size ?? 0) > 0;
|
|
12241
|
+
}
|
|
12195
12242
|
function isProbeDeGated(session) {
|
|
12196
12243
|
return session !== null && session.captureMode !== "drawelement" && !session.deInitDeferred;
|
|
12197
12244
|
}
|
|
@@ -12870,6 +12917,7 @@ async function executeRenderPipeline(input) {
|
|
|
12870
12917
|
// Probe-resolved duration: drawElement self-verification derives its
|
|
12871
12918
|
// sample frame indices from this so they land inside the drained range.
|
|
12872
12919
|
compositionDurationSeconds: job.duration,
|
|
12920
|
+
deFrameFallback: true,
|
|
12873
12921
|
requiresWebGpu: compositionRequiresWebGpu2(compiled.html),
|
|
12874
12922
|
// Live route for Chrome memory (spec §5 Phase −1). The aggregate route
|
|
12875
12923
|
// through CapturePerfSummary only exists on success; a mid-capture
|
|
@@ -12970,7 +13018,7 @@ async function executeRenderPipeline(input) {
|
|
|
12970
13018
|
minFrames: deParallelMinFrames,
|
|
12971
13019
|
layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
|
|
12972
13020
|
supersampling: deviceScaleFactor !== 1,
|
|
12973
|
-
probeDeGated: isProbeDeGated(probeSession),
|
|
13021
|
+
probeDeGated: isProbeDeGated(probeSession) || probeNeedsScreenshotFrames(probeSession),
|
|
12974
13022
|
experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
|
|
12975
13023
|
routerEnabled: deParallelRouterEnabled,
|
|
12976
13024
|
// Router pins 3 workers for the streaming path; don't pin when a
|
|
@@ -13012,6 +13060,8 @@ async function executeRenderPipeline(input) {
|
|
|
13012
13060
|
deInversionEligible = false;
|
|
13013
13061
|
deParallelRouterEligible = false;
|
|
13014
13062
|
deShortBand = void 0;
|
|
13063
|
+
} else if (probeNeedsScreenshotFrames(probeSession)) {
|
|
13064
|
+
deParallelRouterEligible = false;
|
|
13015
13065
|
}
|
|
13016
13066
|
}
|
|
13017
13067
|
const deProbeGateReason = isProbeDeGated(probeSession) ? probeSession.deGateReason : void 0;
|
|
@@ -13203,7 +13253,8 @@ async function executeRenderPipeline(input) {
|
|
|
13203
13253
|
deParallelStreamForced
|
|
13204
13254
|
);
|
|
13205
13255
|
const deParallelStreamVerified = (deParallelStreamForced || process.env.HF_DE_PARALLEL_STREAM === "true") && useStreamingEncode && workerCount > 1;
|
|
13206
|
-
|
|
13256
|
+
const routeDropsScreenshotFrames = cfg.useDrawElement && probeNeedsScreenshotFrames(probeSession) && (!useStreamingEncode || workerCount !== 1);
|
|
13257
|
+
if (routeDropsScreenshotFrames || shouldClampDefaultDrawElement({
|
|
13207
13258
|
useDrawElement: cfg.useDrawElement,
|
|
13208
13259
|
fastCaptureExplicitOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true",
|
|
13209
13260
|
useStreamingEncode,
|
|
@@ -14827,6 +14878,18 @@ function planFrameWeights(input) {
|
|
|
14827
14878
|
}
|
|
14828
14879
|
return weights;
|
|
14829
14880
|
}
|
|
14881
|
+
function withChunkCaptureCost(slices, weights) {
|
|
14882
|
+
const lightest = weights.reduce((least, weight) => Math.min(least, weight), Infinity);
|
|
14883
|
+
return slices.map((slice) => {
|
|
14884
|
+
let sum2 = 0;
|
|
14885
|
+
for (let frame = slice.startFrame; frame < slice.endFrame; frame++) sum2 += weights[frame];
|
|
14886
|
+
const cost = Math.round(sum2 / (slice.endFrame - slice.startFrame) / lightest * 100) / 100;
|
|
14887
|
+
return cost > 1 ? { ...slice, captureCost: cost } : slice;
|
|
14888
|
+
});
|
|
14889
|
+
}
|
|
14890
|
+
function effectiveChunkFrames(frames, captureCost = 1) {
|
|
14891
|
+
return Math.round(frames * captureCost);
|
|
14892
|
+
}
|
|
14830
14893
|
function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
14831
14894
|
if (isUniformFrameCost(weights)) return null;
|
|
14832
14895
|
const totalFrames = weights.length;
|
|
@@ -16147,7 +16210,10 @@ function buildPlanChunkSlices(frameWeights, config, chunkCount, effectiveChunkSi
|
|
|
16147
16210
|
MIN_CHUNK_SIZE,
|
|
16148
16211
|
Math.min(config.targetChunkFrames ?? MAX_CHUNK_SIZE, MAX_CHUNK_SIZE, maxFramesPerChunk)
|
|
16149
16212
|
) : null;
|
|
16150
|
-
return
|
|
16213
|
+
return withChunkCaptureCost(
|
|
16214
|
+
weighted ?? buildChunkSlices(frameWeights.length, chunkCount, effectiveChunkSize),
|
|
16215
|
+
frameWeights
|
|
16216
|
+
);
|
|
16151
16217
|
}
|
|
16152
16218
|
async function readFontSnapshotSha() {
|
|
16153
16219
|
const module = await import("@hyperframes/core/fonts/embedded-data");
|
|
@@ -18514,7 +18580,12 @@ function parseChunkSlices(value) {
|
|
|
18514
18580
|
throw new PlanV2IntegrityError(`meta/chunks.json contains duplicate chunk index ${index}`);
|
|
18515
18581
|
}
|
|
18516
18582
|
indexes.add(index);
|
|
18517
|
-
return { index, startFrame, endFrame };
|
|
18583
|
+
if (chunk.captureCost === void 0) return { index, startFrame, endFrame };
|
|
18584
|
+
const captureCost = chunk.captureCost;
|
|
18585
|
+
if (typeof captureCost !== "number" || !Number.isFinite(captureCost) || captureCost <= 1) {
|
|
18586
|
+
throw new PlanV2IntegrityError(`${field}.captureCost must be a number above 1`);
|
|
18587
|
+
}
|
|
18588
|
+
return { index, startFrame, endFrame, captureCost };
|
|
18518
18589
|
});
|
|
18519
18590
|
}
|
|
18520
18591
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
@@ -19691,15 +19762,17 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19691
19762
|
deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
|
|
19692
19763
|
)
|
|
19693
19764
|
});
|
|
19765
|
+
const captureFrames = effectiveChunkFrames(framesInChunk, slice.captureCost);
|
|
19694
19766
|
const chunkWorkerCount = resolveChunkCaptureWorkers(
|
|
19695
|
-
|
|
19696
|
-
calculateOptimalWorkers2(
|
|
19767
|
+
captureFrames,
|
|
19768
|
+
calculateOptimalWorkers2(captureFrames, void 0, cfg),
|
|
19697
19769
|
options?.maxCaptureWorkers
|
|
19698
19770
|
);
|
|
19699
19771
|
let session = null;
|
|
19700
19772
|
let outputKind;
|
|
19701
19773
|
let framesEncoded = 0;
|
|
19702
19774
|
let sessionBootMs = 0;
|
|
19775
|
+
let videoExtractMs = 0;
|
|
19703
19776
|
let captureStageMs = 0;
|
|
19704
19777
|
let encodeStageMs = 0;
|
|
19705
19778
|
let captureMode;
|
|
@@ -19765,6 +19838,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19765
19838
|
session = null;
|
|
19766
19839
|
}
|
|
19767
19840
|
}
|
|
19841
|
+
const extractStarted = Date.now();
|
|
19768
19842
|
const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
|
|
19769
19843
|
planDir,
|
|
19770
19844
|
planVideos,
|
|
@@ -19774,6 +19848,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19774
19848
|
cfg,
|
|
19775
19849
|
remoteSources,
|
|
19776
19850
|
shownBoxes
|
|
19851
|
+
}).then((frames) => {
|
|
19852
|
+
videoExtractMs = Date.now() - extractStarted;
|
|
19853
|
+
return frames;
|
|
19777
19854
|
}) : Promise.resolve([]);
|
|
19778
19855
|
extraction = deferredVideoFrames;
|
|
19779
19856
|
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
|
|
@@ -19991,6 +20068,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19991
20068
|
sessionBootMs,
|
|
19992
20069
|
captureStageMs,
|
|
19993
20070
|
encodeStageMs,
|
|
20071
|
+
// Wall time of the chunk's own video frame extraction; it overlaps captureStageMs, so do not add them.
|
|
20072
|
+
videoExtractMs,
|
|
19994
20073
|
workers: chunkWorkerCount,
|
|
19995
20074
|
captureMode,
|
|
19996
20075
|
// Device-pixel box each video's frames were extracted to cover; a video not listed kept full size.
|
package/dist/public-server.js
CHANGED
|
@@ -2114,6 +2114,7 @@ function aggregateDrawElement(perfs, de) {
|
|
|
2114
2114
|
blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
|
|
2115
2115
|
blankRecaptures: drain?.blankRecaptures ?? 0,
|
|
2116
2116
|
boundaryFrames: perfs.reduce((sum, p) => sum + (p.deBoundaryFrames ?? 0), 0),
|
|
2117
|
+
riskFrames: perfs.reduce((sum, p) => sum + (p.deRiskFrames ?? 0), 0),
|
|
2117
2118
|
ncprFallbacks: perfs.reduce((sum, p) => sum + (p.deNcprFallbacks ?? 0), 0),
|
|
2118
2119
|
frameTimeouts: perfs.reduce((sum, p) => sum + (p.deFrameTimeouts ?? 0), 0)
|
|
2119
2120
|
};
|
|
@@ -3750,11 +3751,21 @@ function createDrainFrameGuard(args) {
|
|
|
3750
3751
|
return buf;
|
|
3751
3752
|
};
|
|
3752
3753
|
}
|
|
3753
|
-
async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEncoder, reorderBuffer, assertNotAborted, onProgress, log, stats, abortSignal) {
|
|
3754
|
+
async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEncoder, reorderBuffer, assertNotAborted, onProgress, log, stats, abortSignal, risk) {
|
|
3754
3755
|
let prev = null;
|
|
3755
3756
|
const frameTime = (i) => i * job.config.fps.den / job.config.fps.num;
|
|
3756
3757
|
const guard = createDrainFrameGuard({ log, stats, frameTime });
|
|
3757
|
-
const guardFrame = (
|
|
3758
|
+
const guardFrame = (item, buf) => item.trusted ? Promise.resolve(buf) : guard(session, item.idx, buf);
|
|
3759
|
+
const captureRisk = async (idx) => {
|
|
3760
|
+
await risk.open();
|
|
3761
|
+
lastProgressAt = Date.now();
|
|
3762
|
+
return raceAgainstStall(
|
|
3763
|
+
risk.capture(idx, frameTime(idx)),
|
|
3764
|
+
stallTimeoutMs - (Date.now() - lastProgressAt),
|
|
3765
|
+
{ session, frameIndex: idx, totalFrames, stallTimeoutMs },
|
|
3766
|
+
abortSignal
|
|
3767
|
+
);
|
|
3768
|
+
};
|
|
3758
3769
|
const stallTimeoutMs = resolveCaptureStallTimeoutMs();
|
|
3759
3770
|
let lastProgressAt = Date.now();
|
|
3760
3771
|
const captureWithStallGuard = (idx, promise) => raceAgainstStall(
|
|
@@ -3766,7 +3777,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3766
3777
|
const drainPrev = async () => {
|
|
3767
3778
|
if (!prev) return;
|
|
3768
3779
|
assertNotAborted();
|
|
3769
|
-
const buf = await guardFrame(prev
|
|
3780
|
+
const buf = await guardFrame(prev, await prev.encodeResult);
|
|
3770
3781
|
await reorderBuffer.waitForFrame(prev.idx);
|
|
3771
3782
|
ensureFrameWritten(await currentEncoder.writeFrame(buf), prev.idx, currentEncoder);
|
|
3772
3783
|
reorderBuffer.advanceTo(prev.idx + 1);
|
|
@@ -3786,7 +3797,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3786
3797
|
const drainBatch = async (batch) => {
|
|
3787
3798
|
for (const item of batch) {
|
|
3788
3799
|
assertNotAborted();
|
|
3789
|
-
const buf = await guardFrame(item
|
|
3800
|
+
const buf = await guardFrame(item, await item.encodeResult);
|
|
3790
3801
|
await reorderBuffer.waitForFrame(item.idx);
|
|
3791
3802
|
ensureFrameWritten(await currentEncoder.writeFrame(buf), item.idx, currentEncoder);
|
|
3792
3803
|
reorderBuffer.advanceTo(item.idx + 1);
|
|
@@ -3804,7 +3815,7 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3804
3815
|
};
|
|
3805
3816
|
if (batchN > 1 && !session.onBeforeCapture) {
|
|
3806
3817
|
const boundarySS = process.env.HF_FAST_CAPTURE_BOUNDARY_SS === "true";
|
|
3807
|
-
const batchable = (f) => !session.staticFrames?.has(f) && !(boundarySS && session.clipBoundaryFrames?.has(f));
|
|
3818
|
+
const batchable = (f) => !session.staticFrames?.has(f) && !(boundarySS && session.clipBoundaryFrames?.has(f)) && !risk?.frames.has(f);
|
|
3808
3819
|
let prevBatch = [];
|
|
3809
3820
|
let i = 0;
|
|
3810
3821
|
while (i < totalFrames) {
|
|
@@ -3821,6 +3832,11 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3821
3832
|
);
|
|
3822
3833
|
await drainBatch(prevBatch);
|
|
3823
3834
|
prevBatch = results.map((r) => ({ idx: r.frameIndex, encodeResult: r.encodeResult }));
|
|
3835
|
+
} else if (risk?.frames.has(i)) {
|
|
3836
|
+
const buffer = await captureRisk(i);
|
|
3837
|
+
await drainBatch(prevBatch);
|
|
3838
|
+
prevBatch = [{ idx: i, encodeResult: Promise.resolve(buffer), trusted: true }];
|
|
3839
|
+
i++;
|
|
3824
3840
|
} else {
|
|
3825
3841
|
const { encodeResult } = await captureWithStallGuard(
|
|
3826
3842
|
i,
|
|
@@ -3836,6 +3852,12 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3836
3852
|
}
|
|
3837
3853
|
for (let i = 0; i < totalFrames; i++) {
|
|
3838
3854
|
assertNotAborted();
|
|
3855
|
+
if (risk?.frames.has(i)) {
|
|
3856
|
+
const buffer = await captureRisk(i);
|
|
3857
|
+
await drainPrev();
|
|
3858
|
+
prev = { idx: i, encodeResult: Promise.resolve(buffer), trusted: true };
|
|
3859
|
+
continue;
|
|
3860
|
+
}
|
|
3839
3861
|
const time = frameTime(i);
|
|
3840
3862
|
const { encodeResult } = await captureWithStallGuard(
|
|
3841
3863
|
i,
|
|
@@ -4082,6 +4104,24 @@ async function runCaptureStreamingStage(input) {
|
|
|
4082
4104
|
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
4083
4105
|
probeSession = null;
|
|
4084
4106
|
}
|
|
4107
|
+
let riskSession = null;
|
|
4108
|
+
const openRiskSession = async () => {
|
|
4109
|
+
const opened = await createCaptureSession(
|
|
4110
|
+
fileServer.url,
|
|
4111
|
+
framesDir,
|
|
4112
|
+
buildCaptureOptions(),
|
|
4113
|
+
createRenderVideoFrameInjector(),
|
|
4114
|
+
{
|
|
4115
|
+
...captureCfg,
|
|
4116
|
+
useDrawElement: false,
|
|
4117
|
+
staticFrameDedup: false,
|
|
4118
|
+
enableBrowserPool: false
|
|
4119
|
+
}
|
|
4120
|
+
);
|
|
4121
|
+
riskSession = opened;
|
|
4122
|
+
await initializeSession(opened);
|
|
4123
|
+
return opened;
|
|
4124
|
+
};
|
|
4085
4125
|
try {
|
|
4086
4126
|
if (!session.isInitialized) {
|
|
4087
4127
|
await initializeSession(session);
|
|
@@ -4096,6 +4136,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4096
4136
|
blankDeterministicAccepts: 0,
|
|
4097
4137
|
blankRecaptures: 0
|
|
4098
4138
|
};
|
|
4139
|
+
const riskFrames = session.deRiskFrames;
|
|
4099
4140
|
await runWorkerEncodePipelineLoop(
|
|
4100
4141
|
session,
|
|
4101
4142
|
totalFrames,
|
|
@@ -4106,7 +4147,16 @@ async function runCaptureStreamingStage(input) {
|
|
|
4106
4147
|
onProgress,
|
|
4107
4148
|
log,
|
|
4108
4149
|
deDrainStats,
|
|
4109
|
-
abortSignal
|
|
4150
|
+
abortSignal,
|
|
4151
|
+
riskFrames && riskFrames.size > 0 ? {
|
|
4152
|
+
frames: riskFrames,
|
|
4153
|
+
// Opened on the first risk frame, in a browser of its own: pages sharing a
|
|
4154
|
+
// browser share a compositor, which starves the drawElement page's paint events.
|
|
4155
|
+
open: async () => {
|
|
4156
|
+
riskSession ??= await openRiskSession();
|
|
4157
|
+
},
|
|
4158
|
+
capture: async (frameIndex, time) => (await captureFrameToBuffer(riskSession, frameIndex, time)).buffer
|
|
4159
|
+
} : void 0
|
|
4110
4160
|
);
|
|
4111
4161
|
} else {
|
|
4112
4162
|
const stallTimeoutMs = resolveCaptureStallTimeoutMs();
|
|
@@ -4144,6 +4194,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4144
4194
|
} finally {
|
|
4145
4195
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
4146
4196
|
await closeCaptureSession(session);
|
|
4197
|
+
if (riskSession) await closeCaptureSession(riskSession);
|
|
4147
4198
|
}
|
|
4148
4199
|
}
|
|
4149
4200
|
const encodeFrom = job.progress;
|
|
@@ -4303,7 +4354,7 @@ function openSegmentedSessionReuse(input, session) {
|
|
|
4303
4354
|
async function openSegmentedSession(input) {
|
|
4304
4355
|
const captureCfg = input.cfg.forceScreenshot === input.plan.forceScreenshot ? input.cfg : { ...input.cfg, forceScreenshot: input.plan.forceScreenshot };
|
|
4305
4356
|
const videoInjector = input.createRenderVideoFrameInjector();
|
|
4306
|
-
const captureOptions = input.buildCaptureOptions();
|
|
4357
|
+
const captureOptions = { ...input.buildCaptureOptions(), deFrameFallback: false };
|
|
4307
4358
|
const ownBrowser = shouldDisableBrowserPoolForParallelWorker({
|
|
4308
4359
|
parallel: (input.workerCount ?? 1) > 1
|
|
4309
4360
|
});
|
|
@@ -6319,10 +6370,6 @@ var THREE_D_CONTEXT_PATTERN = /transform-style\s*:\s*preserve-3d|backface-visibi
|
|
|
6319
6370
|
function detectThreeDTransformUsage(html) {
|
|
6320
6371
|
return THREE_D_CONTEXT_PATTERN.test(html);
|
|
6321
6372
|
}
|
|
6322
|
-
var MIX_BLEND_MODE_PATTERN = /mix-blend-mode\s*:/i;
|
|
6323
|
-
function detectMixBlendModeUsage(html) {
|
|
6324
|
-
return MIX_BLEND_MODE_PATTERN.test(html);
|
|
6325
|
-
}
|
|
6326
6373
|
var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
|
|
6327
6374
|
function detectAncestorBackgroundImage(html) {
|
|
6328
6375
|
const { document: document2 } = parseHTML4(html);
|
|
@@ -7335,7 +7382,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7335
7382
|
const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
|
|
7336
7383
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
7337
7384
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
7338
|
-
const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
|
|
7339
7385
|
const hasAncestorBackgroundImage = detectAncestorBackgroundImage(sanitizedHtml);
|
|
7340
7386
|
const normalizedFontHtml = normalizeSystemFontPrimaryFamilies(
|
|
7341
7387
|
injectTextRenderingRule(
|
|
@@ -7449,7 +7495,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7449
7495
|
renderModeHints,
|
|
7450
7496
|
hasShaderTransitions,
|
|
7451
7497
|
usesThreeDTransforms,
|
|
7452
|
-
usesMixBlendMode,
|
|
7453
7498
|
hasAncestorBackgroundImage
|
|
7454
7499
|
};
|
|
7455
7500
|
}
|
|
@@ -7856,13 +7901,6 @@ async function runCompileStage(input) {
|
|
|
7856
7901
|
"[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7857
7902
|
);
|
|
7858
7903
|
}
|
|
7859
|
-
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
|
|
7860
|
-
cfg.useDrawElement = false;
|
|
7861
|
-
deCompileGate = "mix_blend_mode";
|
|
7862
|
-
log.info(
|
|
7863
|
-
"[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7864
|
-
);
|
|
7865
|
-
}
|
|
7866
7904
|
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
|
|
7867
7905
|
cfg.useDrawElement = false;
|
|
7868
7906
|
deCompileGate = "ancestor_background_image";
|
|
@@ -9257,10 +9295,11 @@ async function constrainAacTruePeak(input) {
|
|
|
9257
9295
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
9258
9296
|
return enforceAacTruePeak({
|
|
9259
9297
|
audioPath: input.audioPath,
|
|
9298
|
+
sourcePath: input.sourcePath,
|
|
9260
9299
|
targetDurationSeconds: input.targetDurationSeconds,
|
|
9261
9300
|
signal: input.signal,
|
|
9262
|
-
probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
|
|
9263
|
-
encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
9301
|
+
probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
|
|
9302
|
+
encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
9264
9303
|
});
|
|
9265
9304
|
}
|
|
9266
9305
|
async function enforceAacTruePeak(input) {
|
|
@@ -9270,6 +9309,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9270
9309
|
const correctedPath = join18(scratchDir, "audio.m4a");
|
|
9271
9310
|
const measurements = [];
|
|
9272
9311
|
let attenuationDb = 0;
|
|
9312
|
+
let sourcePeakDbfs;
|
|
9273
9313
|
let measuredPath = input.audioPath;
|
|
9274
9314
|
for (let pass = 0; pass <= MAX_TRUE_PEAK_CORRECTION_PASSES; pass += 1) {
|
|
9275
9315
|
const truePeakDbfs = await input.probeTruePeak(measuredPath, input.signal);
|
|
@@ -9286,7 +9326,11 @@ async function enforceAacTruePeak(input) {
|
|
|
9286
9326
|
}
|
|
9287
9327
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
9288
9328
|
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
9289
|
-
|
|
9329
|
+
if (input.sourcePath && sourcePeakDbfs === void 0) {
|
|
9330
|
+
sourcePeakDbfs = await input.probeTruePeak(input.sourcePath, input.signal);
|
|
9331
|
+
}
|
|
9332
|
+
const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
|
|
9333
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
|
|
9290
9334
|
const result = await input.encode(
|
|
9291
9335
|
input.audioPath,
|
|
9292
9336
|
correctedPath,
|
|
@@ -9722,7 +9766,7 @@ async function runCaptureStage(input) {
|
|
|
9722
9766
|
);
|
|
9723
9767
|
}
|
|
9724
9768
|
}
|
|
9725
|
-
const captureOptions = buildCaptureOptions();
|
|
9769
|
+
const captureOptions = { ...buildCaptureOptions(), deFrameFallback: false };
|
|
9726
9770
|
const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
|
|
9727
9771
|
const checkDiskProjection = createDiskCaptureProjection({
|
|
9728
9772
|
framesDir,
|
|
@@ -12176,6 +12220,9 @@ function resolveDeShortBand(args) {
|
|
|
12176
12220
|
function shouldPreferSingleWorkerDrawElement(args) {
|
|
12177
12221
|
return args.workerCount > 1 && 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;
|
|
12178
12222
|
}
|
|
12223
|
+
function probeNeedsScreenshotFrames(session) {
|
|
12224
|
+
return (session?.deRiskFrames?.size ?? 0) > 0;
|
|
12225
|
+
}
|
|
12179
12226
|
function isProbeDeGated(session) {
|
|
12180
12227
|
return session !== null && session.captureMode !== "drawelement" && !session.deInitDeferred;
|
|
12181
12228
|
}
|
|
@@ -12854,6 +12901,7 @@ async function executeRenderPipeline(input) {
|
|
|
12854
12901
|
// Probe-resolved duration: drawElement self-verification derives its
|
|
12855
12902
|
// sample frame indices from this so they land inside the drained range.
|
|
12856
12903
|
compositionDurationSeconds: job.duration,
|
|
12904
|
+
deFrameFallback: true,
|
|
12857
12905
|
requiresWebGpu: compositionRequiresWebGpu2(compiled.html),
|
|
12858
12906
|
// Live route for Chrome memory (spec §5 Phase −1). The aggregate route
|
|
12859
12907
|
// through CapturePerfSummary only exists on success; a mid-capture
|
|
@@ -12954,7 +13002,7 @@ async function executeRenderPipeline(input) {
|
|
|
12954
13002
|
minFrames: deParallelMinFrames,
|
|
12955
13003
|
layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
|
|
12956
13004
|
supersampling: deviceScaleFactor !== 1,
|
|
12957
|
-
probeDeGated: isProbeDeGated(probeSession),
|
|
13005
|
+
probeDeGated: isProbeDeGated(probeSession) || probeNeedsScreenshotFrames(probeSession),
|
|
12958
13006
|
experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
|
|
12959
13007
|
routerEnabled: deParallelRouterEnabled,
|
|
12960
13008
|
// Router pins 3 workers for the streaming path; don't pin when a
|
|
@@ -12996,6 +13044,8 @@ async function executeRenderPipeline(input) {
|
|
|
12996
13044
|
deInversionEligible = false;
|
|
12997
13045
|
deParallelRouterEligible = false;
|
|
12998
13046
|
deShortBand = void 0;
|
|
13047
|
+
} else if (probeNeedsScreenshotFrames(probeSession)) {
|
|
13048
|
+
deParallelRouterEligible = false;
|
|
12999
13049
|
}
|
|
13000
13050
|
}
|
|
13001
13051
|
const deProbeGateReason = isProbeDeGated(probeSession) ? probeSession.deGateReason : void 0;
|
|
@@ -13187,7 +13237,8 @@ async function executeRenderPipeline(input) {
|
|
|
13187
13237
|
deParallelStreamForced
|
|
13188
13238
|
);
|
|
13189
13239
|
const deParallelStreamVerified = (deParallelStreamForced || process.env.HF_DE_PARALLEL_STREAM === "true") && useStreamingEncode && workerCount > 1;
|
|
13190
|
-
|
|
13240
|
+
const routeDropsScreenshotFrames = cfg.useDrawElement && probeNeedsScreenshotFrames(probeSession) && (!useStreamingEncode || workerCount !== 1);
|
|
13241
|
+
if (routeDropsScreenshotFrames || shouldClampDefaultDrawElement({
|
|
13191
13242
|
useDrawElement: cfg.useDrawElement,
|
|
13192
13243
|
fastCaptureExplicitOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true",
|
|
13193
13244
|
useStreamingEncode,
|
|
@@ -9,6 +9,8 @@ export declare function planFrameWeights(input: {
|
|
|
9
9
|
width: number;
|
|
10
10
|
height: number;
|
|
11
11
|
}): Float64Array;
|
|
12
|
+
export declare function withChunkCaptureCost(slices: ChunkSliceJson[], weights: Float64Array): ChunkSliceJson[];
|
|
13
|
+
export declare function effectiveChunkFrames(frames: number, captureCost?: number): number;
|
|
12
14
|
/**
|
|
13
15
|
* Cut `[0, weights.length)` into `chunkCount` ranges of ~equal summed weight, each within
|
|
14
16
|
* `[minFrames, maxFrames]` where the frame count allows. `null` when the cost is uniform.
|
|
@@ -20,8 +20,6 @@ export interface CompiledComposition {
|
|
|
20
20
|
hasShaderTransitions: boolean;
|
|
21
21
|
/** Author HTML/CSS/scripts use a CSS 3D rendering context (pre-CDN-inline scan). */
|
|
22
22
|
usesThreeDTransforms: boolean;
|
|
23
|
-
/** Author HTML/CSS use mix-blend-mode (pre-CDN-inline scan). */
|
|
24
|
-
usesMixBlendMode: boolean;
|
|
25
23
|
/** Ancestors of the composition root carry a background-image (gradient/url). */
|
|
26
24
|
hasAncestorBackgroundImage: boolean;
|
|
27
25
|
}
|
|
@@ -124,6 +124,8 @@ export declare function constrainAacTruePeak(input: {
|
|
|
124
124
|
targetDurationSeconds: number;
|
|
125
125
|
signal?: AbortSignal;
|
|
126
126
|
timeoutMs?: number;
|
|
127
|
+
probeTruePeakDbfs?: (audioPath: string, signal?: AbortSignal) => Promise<number>;
|
|
128
|
+
encodeAac?: AacEncoder;
|
|
127
129
|
}): Promise<{
|
|
128
130
|
success: boolean;
|
|
129
131
|
error?: string;
|