@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/distributed.js
CHANGED
|
@@ -5233,6 +5233,7 @@ async function runCaptureStage(input) {
|
|
|
5233
5233
|
if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
|
|
5234
5234
|
return;
|
|
5235
5235
|
}
|
|
5236
|
+
if (progress.landedFrame) input.onFrameLanded?.(progress.landedFrame);
|
|
5236
5237
|
job.framesRendered = progress.capturedFrames;
|
|
5237
5238
|
checkDiskProjection(progress.capturedFrames);
|
|
5238
5239
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
@@ -5473,7 +5474,8 @@ import {
|
|
|
5473
5474
|
formatFfmpegError,
|
|
5474
5475
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
|
|
5475
5476
|
resolveConfig,
|
|
5476
|
-
runFfmpeg as runFfmpeg3
|
|
5477
|
+
runFfmpeg as runFfmpeg3,
|
|
5478
|
+
spawnStreamingEncoder as spawnStreamingEncoder4
|
|
5477
5479
|
} from "@hyperframes/engine";
|
|
5478
5480
|
import {
|
|
5479
5481
|
clearGifFramesBeforeNext,
|
|
@@ -5610,6 +5612,30 @@ function startEncodeProgress(job, stage, onProgress) {
|
|
|
5610
5612
|
});
|
|
5611
5613
|
return (frames = total) => reportEncodeProgress(job, frames, total, onProgress, 75);
|
|
5612
5614
|
}
|
|
5615
|
+
function resolveEngineConfig(input) {
|
|
5616
|
+
return input.engineConfig ?? input.job.config.producerConfig ?? resolveConfig();
|
|
5617
|
+
}
|
|
5618
|
+
function videoEncoderOptions(input, engineCfg) {
|
|
5619
|
+
const { job, preset } = input;
|
|
5620
|
+
return {
|
|
5621
|
+
fps: job.config.fps,
|
|
5622
|
+
width: input.width,
|
|
5623
|
+
height: input.height,
|
|
5624
|
+
codec: preset.codec,
|
|
5625
|
+
preset: preset.preset,
|
|
5626
|
+
quality: input.effectiveQuality,
|
|
5627
|
+
bitrate: input.effectiveBitrate,
|
|
5628
|
+
pixelFormat: preset.pixelFormat,
|
|
5629
|
+
vp9CpuUsed: engineCfg.vp9CpuUsed,
|
|
5630
|
+
useGpu: job.config.useGpu,
|
|
5631
|
+
hdr: preset.hdr,
|
|
5632
|
+
// Distributed chunk renders pass these so the encoder writes closed-GOP
|
|
5633
|
+
// keyframes that survive `-f concat -c copy` at assemble time. In-process
|
|
5634
|
+
// renders leave both undefined → preserves the existing open-GOP output.
|
|
5635
|
+
lockGopForChunkConcat: input.lockGopForChunkConcat === true,
|
|
5636
|
+
gopSize: input.gopSize
|
|
5637
|
+
};
|
|
5638
|
+
}
|
|
5613
5639
|
async function runEncodeStage(input) {
|
|
5614
5640
|
const {
|
|
5615
5641
|
job,
|
|
@@ -5617,16 +5643,11 @@ async function runEncodeStage(input) {
|
|
|
5617
5643
|
outputPath,
|
|
5618
5644
|
framesDir,
|
|
5619
5645
|
videoOnlyPath,
|
|
5620
|
-
width,
|
|
5621
|
-
height,
|
|
5622
5646
|
needsAlpha,
|
|
5623
5647
|
hasAudio,
|
|
5624
5648
|
audioOutputPath,
|
|
5625
5649
|
isPngSequence,
|
|
5626
5650
|
isGif,
|
|
5627
|
-
preset,
|
|
5628
|
-
effectiveQuality,
|
|
5629
|
-
effectiveBitrate,
|
|
5630
5651
|
enableChunkedEncode,
|
|
5631
5652
|
chunkedEncodeSize,
|
|
5632
5653
|
abortSignal,
|
|
@@ -5656,7 +5677,7 @@ async function runEncodeStage(input) {
|
|
|
5656
5677
|
encoded();
|
|
5657
5678
|
return { encodeMs: Date.now() - stage5Start };
|
|
5658
5679
|
}
|
|
5659
|
-
const engineCfg = input
|
|
5680
|
+
const engineCfg = resolveEngineConfig(input);
|
|
5660
5681
|
if (isGif) {
|
|
5661
5682
|
const encoded = startEncodeProgress(job, "Encoding GIF", onProgress);
|
|
5662
5683
|
if (hasAudio) {
|
|
@@ -5686,24 +5707,7 @@ async function runEncodeStage(input) {
|
|
|
5686
5707
|
const videoEngineCfg = scaledEncodeTimeout > engineCfg.ffmpegEncodeTimeout ? { ...engineCfg, ffmpegEncodeTimeout: scaledEncodeTimeout } : engineCfg;
|
|
5687
5708
|
const frameExt = frameFileExtension2(input.captureImageFormat);
|
|
5688
5709
|
const framePattern = `frame_%06d.${frameExt}`;
|
|
5689
|
-
const encoderOpts =
|
|
5690
|
-
fps: job.config.fps,
|
|
5691
|
-
width,
|
|
5692
|
-
height,
|
|
5693
|
-
codec: preset.codec,
|
|
5694
|
-
preset: preset.preset,
|
|
5695
|
-
quality: effectiveQuality,
|
|
5696
|
-
bitrate: effectiveBitrate,
|
|
5697
|
-
pixelFormat: preset.pixelFormat,
|
|
5698
|
-
vp9CpuUsed: engineCfg.vp9CpuUsed,
|
|
5699
|
-
useGpu: job.config.useGpu,
|
|
5700
|
-
hdr: preset.hdr,
|
|
5701
|
-
// Distributed chunk renders pass these so the encoder writes closed-GOP
|
|
5702
|
-
// keyframes that survive `-f concat -c copy` at assemble time. In-process
|
|
5703
|
-
// renders leave both undefined → preserves the existing open-GOP output.
|
|
5704
|
-
lockGopForChunkConcat: input.lockGopForChunkConcat === true,
|
|
5705
|
-
gopSize: input.gopSize
|
|
5706
|
-
};
|
|
5710
|
+
const encoderOpts = videoEncoderOptions(input, engineCfg);
|
|
5707
5711
|
const encodeResult = enableChunkedEncode ? await encodeFramesChunkedConcat(
|
|
5708
5712
|
framesDir,
|
|
5709
5713
|
framePattern,
|
|
@@ -5986,7 +5990,6 @@ async function enforceAacTruePeak(input) {
|
|
|
5986
5990
|
try {
|
|
5987
5991
|
scratchDir = mkdtempSync2(join13(dirname6(input.audioPath), ".true-peak-"));
|
|
5988
5992
|
const correctedPath = join13(scratchDir, "audio.m4a");
|
|
5989
|
-
const correctionTargetDbfs = AAC_DELIVERY_TRUE_PEAK_DBFS - AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB;
|
|
5990
5993
|
const measurements = [];
|
|
5991
5994
|
let attenuationDb = 0;
|
|
5992
5995
|
let measuredPath = input.audioPath;
|
|
@@ -6004,7 +6007,8 @@ async function enforceAacTruePeak(input) {
|
|
|
6004
6007
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
6005
6008
|
}
|
|
6006
6009
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
6007
|
-
|
|
6010
|
+
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
6011
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
|
|
6008
6012
|
const result = await input.encode(
|
|
6009
6013
|
input.audioPath,
|
|
6010
6014
|
correctedPath,
|
|
@@ -6847,7 +6851,14 @@ var RenderChunkValidationError = class extends Error {
|
|
|
6847
6851
|
}
|
|
6848
6852
|
};
|
|
6849
6853
|
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
6850
|
-
return () => createVideoFrameInjector2(frameLookup);
|
|
6854
|
+
if (!(frameLookup instanceof Promise)) return () => createVideoFrameInjector2(frameLookup);
|
|
6855
|
+
return () => {
|
|
6856
|
+
let hook;
|
|
6857
|
+
return async (page, time, heldVideoTime) => {
|
|
6858
|
+
hook ??= createVideoFrameInjector2(await frameLookup);
|
|
6859
|
+
await hook?.(page, time, heldVideoTime);
|
|
6860
|
+
};
|
|
6861
|
+
};
|
|
6851
6862
|
}
|
|
6852
6863
|
function frameNumberFromFileName(name) {
|
|
6853
6864
|
const match = /(\d+)(?=\.[^.]+$)/.exec(name);
|
|
@@ -10245,6 +10256,12 @@ function validatePlanVideosForChunk(value) {
|
|
|
10245
10256
|
);
|
|
10246
10257
|
}
|
|
10247
10258
|
}
|
|
10259
|
+
var MIN_FRAMES_PER_CHUNK_BROWSER = 20;
|
|
10260
|
+
function resolveChunkCaptureWorkers(framesInChunk, machineSized, maxCaptureWorkers) {
|
|
10261
|
+
if (maxCaptureWorkers === void 0) return machineSized;
|
|
10262
|
+
const byFrames = Math.floor(framesInChunk / MIN_FRAMES_PER_CHUNK_BROWSER);
|
|
10263
|
+
return Math.max(machineSized, Math.floor(Math.min(maxCaptureWorkers, byFrames)));
|
|
10264
|
+
}
|
|
10248
10265
|
function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
|
|
10249
10266
|
if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
|
|
10250
10267
|
const { startFrame, endFrame } = frameRange;
|
|
@@ -10481,7 +10498,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10481
10498
|
deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
|
|
10482
10499
|
)
|
|
10483
10500
|
});
|
|
10484
|
-
const chunkWorkerCount =
|
|
10501
|
+
const chunkWorkerCount = resolveChunkCaptureWorkers(
|
|
10502
|
+
framesInChunk,
|
|
10503
|
+
calculateOptimalWorkers2(framesInChunk, void 0, cfg),
|
|
10504
|
+
options?.maxCaptureWorkers
|
|
10505
|
+
);
|
|
10485
10506
|
let session = null;
|
|
10486
10507
|
let outputKind;
|
|
10487
10508
|
let framesEncoded = 0;
|
|
@@ -10493,6 +10514,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10493
10514
|
const captureAttempts = [];
|
|
10494
10515
|
let forceScreenshotForChunk = encoder.forceScreenshot;
|
|
10495
10516
|
let shownBoxes;
|
|
10517
|
+
let extraction;
|
|
10496
10518
|
try {
|
|
10497
10519
|
if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
|
|
10498
10520
|
const bootStarted = Date.now();
|
|
@@ -10549,7 +10571,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10549
10571
|
session = null;
|
|
10550
10572
|
}
|
|
10551
10573
|
}
|
|
10552
|
-
const deferredVideoFrames = planVideos ?
|
|
10574
|
+
const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
|
|
10553
10575
|
planDir,
|
|
10554
10576
|
planVideos,
|
|
10555
10577
|
frames: chunkFrameNumbers,
|
|
@@ -10558,19 +10580,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10558
10580
|
cfg,
|
|
10559
10581
|
remoteSources,
|
|
10560
10582
|
shownBoxes
|
|
10561
|
-
}) : [];
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10583
|
+
}) : Promise.resolve([]);
|
|
10584
|
+
extraction = deferredVideoFrames;
|
|
10585
|
+
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
|
|
10586
|
+
(deferred) => createFrameLookupTable5(
|
|
10587
|
+
planVideos.videos,
|
|
10588
|
+
[
|
|
10589
|
+
...rebuildExtractedFramesFromPlanDir(
|
|
10590
|
+
planDir,
|
|
10591
|
+
planVideos.extracted.filter((video) => !video.deferredRange),
|
|
10592
|
+
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
10593
|
+
),
|
|
10594
|
+
...deferred
|
|
10595
|
+
],
|
|
10596
|
+
resolveRenderFpsConfig(job.config.fps).value
|
|
10597
|
+
)
|
|
10573
10598
|
) : null;
|
|
10599
|
+
videoFrameLookup?.catch(() => {
|
|
10600
|
+
});
|
|
10574
10601
|
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
|
|
10575
10602
|
const captureStarted = Date.now();
|
|
10576
10603
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
@@ -10653,7 +10680,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10653
10680
|
}
|
|
10654
10681
|
);
|
|
10655
10682
|
}
|
|
10683
|
+
}).catch(async (captureError) => {
|
|
10684
|
+
await deferredVideoFrames;
|
|
10685
|
+
throw captureError;
|
|
10656
10686
|
});
|
|
10687
|
+
await deferredVideoFrames;
|
|
10657
10688
|
captureStageMs = Date.now() - captureStarted;
|
|
10658
10689
|
applyRenderWarningPolicy(
|
|
10659
10690
|
job,
|
|
@@ -10732,6 +10763,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10732
10763
|
}
|
|
10733
10764
|
encodeStageMs = Date.now() - encodeStarted;
|
|
10734
10765
|
} finally {
|
|
10766
|
+
await extraction?.catch(() => {
|
|
10767
|
+
});
|
|
10735
10768
|
if (session) {
|
|
10736
10769
|
try {
|
|
10737
10770
|
await closeCaptureSession10(session);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "6b080917689b34014c8f3884d89beea5dcd39986c93335d117c29fa055e0ecd8",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|