@hyperframes/producer 0.8.208 → 0.8.210
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 +59 -11
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +64 -64
- package/dist/index.js +83 -16
- package/dist/public-server.js +53 -12
- package/dist/renderRequest.d.ts +2 -1
- package/dist/services/distributed/plan.d.ts +3 -1
- package/dist/services/render/deliveryEncoder.d.ts +1 -0
- package/dist/services/render/hdrMode.d.ts +1 -0
- package/dist/services/render/stages/freezePlan.d.ts +2 -0
- package/dist/services/renderOrchestrator.d.ts +3 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -775,7 +775,7 @@ function createFileServer(options) {
|
|
|
775
775
|
if (!streamOwnsFd) closeSync(fd);
|
|
776
776
|
}
|
|
777
777
|
});
|
|
778
|
-
return new Promise((resolve13) => {
|
|
778
|
+
return new Promise((resolve13, reject) => {
|
|
779
779
|
const connections = /* @__PURE__ */ new Set();
|
|
780
780
|
const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
|
|
781
781
|
resolve13({
|
|
@@ -795,6 +795,7 @@ function createFileServer(options) {
|
|
|
795
795
|
connections.add(socket);
|
|
796
796
|
socket.on("close", () => connections.delete(socket));
|
|
797
797
|
});
|
|
798
|
+
server.on("error", reject);
|
|
798
799
|
});
|
|
799
800
|
}
|
|
800
801
|
|
|
@@ -846,9 +847,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
|
|
|
846
847
|
}
|
|
847
848
|
|
|
848
849
|
// src/services/render/deliveryEncoder.ts
|
|
849
|
-
import {
|
|
850
|
+
import {
|
|
851
|
+
PRORES_PROFILE_NAMES,
|
|
852
|
+
appendRateCapArgs,
|
|
853
|
+
isProresProfile,
|
|
854
|
+
keyframeIntervalToGopSize as keyframeIntervalToGopSize2
|
|
855
|
+
} from "@hyperframes/engine";
|
|
856
|
+
function validateProresProfile(format, proresProfile) {
|
|
857
|
+
if (proresProfile === void 0) return;
|
|
858
|
+
if (!isProresProfile(proresProfile)) {
|
|
859
|
+
throw new Error(
|
|
860
|
+
`proresProfile must be one of ${PRORES_PROFILE_NAMES.join(", ")}; got ${JSON.stringify(proresProfile)}`
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
if (format !== void 0 && format !== "mov") {
|
|
864
|
+
throw new Error(`proresProfile applies to mov renders, not ${format}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
850
867
|
function validateDeliveryEncoderConfig(config) {
|
|
851
|
-
const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
|
|
868
|
+
const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
|
|
869
|
+
validateProresProfile(format, proresProfile);
|
|
852
870
|
if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
|
|
853
871
|
return;
|
|
854
872
|
if (keyframeIntervalSeconds !== void 0) {
|
|
@@ -3229,14 +3247,15 @@ async function extractHdrVideoFrames(args) {
|
|
|
3229
3247
|
}
|
|
3230
3248
|
first.release();
|
|
3231
3249
|
if (video.loop) (await windows.lease(totalFrames - 1))?.release();
|
|
3250
|
+
const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
|
|
3232
3251
|
out.set(videoId, {
|
|
3233
3252
|
dir: frameDir,
|
|
3234
|
-
windows,
|
|
3253
|
+
windows: held,
|
|
3235
3254
|
width: dims.width,
|
|
3236
3255
|
height: dims.height,
|
|
3237
3256
|
frameSize,
|
|
3238
3257
|
get frameCount() {
|
|
3239
|
-
return
|
|
3258
|
+
return held.totalFrames;
|
|
3240
3259
|
},
|
|
3241
3260
|
scratch: Buffer.allocUnsafe(frameSize),
|
|
3242
3261
|
loop: video.loop,
|
|
@@ -3265,9 +3284,18 @@ function hdrWindowFfmpegArgs(a) {
|
|
|
3265
3284
|
if (trim > 0) filters.push(`trim=start_frame=${trim}`);
|
|
3266
3285
|
return [
|
|
3267
3286
|
...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
|
|
3268
|
-
...["-vf", filters.join(","), "-frames:v", String(a.frames)]
|
|
3287
|
+
...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
|
|
3269
3288
|
];
|
|
3270
3289
|
}
|
|
3290
|
+
function holdFirstFrameUntilStreamStarts(windows, firstTick) {
|
|
3291
|
+
return {
|
|
3292
|
+
get totalFrames() {
|
|
3293
|
+
return firstTick + windows.totalFrames;
|
|
3294
|
+
},
|
|
3295
|
+
lease: (frameIndex) => windows.lease(Math.max(0, frameIndex - firstTick)),
|
|
3296
|
+
close: () => windows.close()
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3271
3299
|
function decodeHdrImageBuffers(args) {
|
|
3272
3300
|
const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
|
|
3273
3301
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -4833,6 +4861,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
|
|
|
4833
4861
|
`[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
4834
4862
|
);
|
|
4835
4863
|
}
|
|
4864
|
+
function formatCarriesHdr(format) {
|
|
4865
|
+
return format === "mp4";
|
|
4866
|
+
}
|
|
4867
|
+
function rendersSdrOnly(config) {
|
|
4868
|
+
return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
|
|
4869
|
+
}
|
|
4836
4870
|
function resolveEffectiveHdrMode(input) {
|
|
4837
4871
|
const hdrMode = input.hdrMode ?? "auto";
|
|
4838
4872
|
const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
|
|
@@ -4854,7 +4888,7 @@ function resolveEffectiveHdrMode(input) {
|
|
|
4854
4888
|
} else if (info?.hasHdr && info.dominantTransfer) {
|
|
4855
4889
|
effectiveHdr = { transfer: info.dominantTransfer };
|
|
4856
4890
|
}
|
|
4857
|
-
if (effectiveHdr && input.outputFormat
|
|
4891
|
+
if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
|
|
4858
4892
|
input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
|
|
4859
4893
|
effectiveHdr = void 0;
|
|
4860
4894
|
}
|
|
@@ -5668,7 +5702,7 @@ import { gsapCdnDist, normalizeGsapBaseUrl } from "@hyperframes/core/gsap-cdn";
|
|
|
5668
5702
|
import {
|
|
5669
5703
|
assignBundledRuntimeCompositionIds,
|
|
5670
5704
|
assignMediaRenderIds,
|
|
5671
|
-
|
|
5705
|
+
emitVariablesByCompScript,
|
|
5672
5706
|
inlineSubCompositions as inlineSubCompositionsShared,
|
|
5673
5707
|
ensureExternalLinkTag,
|
|
5674
5708
|
ensureExternalScriptTag,
|
|
@@ -7240,9 +7274,9 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
7240
7274
|
}
|
|
7241
7275
|
}
|
|
7242
7276
|
}
|
|
7243
|
-
|
|
7277
|
+
emitVariablesByCompScript(document2, result.variablesByComp);
|
|
7244
7278
|
if (body) {
|
|
7245
|
-
let pending =
|
|
7279
|
+
let pending = [];
|
|
7246
7280
|
const flushInline = () => {
|
|
7247
7281
|
if (!pending.length) return;
|
|
7248
7282
|
const joined = pending.join("\n;\n");
|
|
@@ -9476,7 +9510,7 @@ async function runExtractVideosStage(input) {
|
|
|
9476
9510
|
fps: job.config.fps,
|
|
9477
9511
|
outputDir: join18(compiledDir, "__hyperframes_video_frames"),
|
|
9478
9512
|
format: job.config.videoFrameFormat ?? "auto",
|
|
9479
|
-
toneMapHdrToSdr: job.config
|
|
9513
|
+
toneMapHdrToSdr: rendersSdrOnly(job.config),
|
|
9480
9514
|
timelineEnd: composition.duration,
|
|
9481
9515
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
9482
9516
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
@@ -14060,7 +14094,12 @@ async function executeRenderPipeline(input) {
|
|
|
14060
14094
|
});
|
|
14061
14095
|
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
14062
14096
|
const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
|
|
14063
|
-
const preset = getEncoderPreset3(
|
|
14097
|
+
const preset = getEncoderPreset3(
|
|
14098
|
+
job.config.quality,
|
|
14099
|
+
presetFormat,
|
|
14100
|
+
encoderHdr,
|
|
14101
|
+
job.config.proresProfile
|
|
14102
|
+
);
|
|
14064
14103
|
if (job.config.crf != null && job.config.videoBitrate) {
|
|
14065
14104
|
log.warn(
|
|
14066
14105
|
`[Render] Both crf=${job.config.crf} and videoBitrate=${job.config.videoBitrate} were set. These are mutually exclusive; honoring crf and ignoring videoBitrate. Set only one to silence this warning.`
|
|
@@ -14875,6 +14914,7 @@ async function executeRenderPipeline(input) {
|
|
|
14875
14914
|
|
|
14876
14915
|
// src/renderRequest.ts
|
|
14877
14916
|
import {
|
|
14917
|
+
PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
|
|
14878
14918
|
isVideoFrameFormat as isVideoFrameFormat2,
|
|
14879
14919
|
resolveConfig as resolveConfig4,
|
|
14880
14920
|
validateEngineConfigSnapshot as validateEngineConfigSnapshot2
|
|
@@ -16892,6 +16932,8 @@ function buildLockedRenderConfig(input) {
|
|
|
16892
16932
|
closedGop: true,
|
|
16893
16933
|
forceKeyframes: "n=0",
|
|
16894
16934
|
pixelFormat,
|
|
16935
|
+
// Omitted for the default so existing 4444 plans keep their planHash.
|
|
16936
|
+
...config.proresProfile && config.proresProfile !== "4444" ? { proresProfile: config.proresProfile } : {},
|
|
16895
16937
|
chunkSize: input.effectiveChunkSize,
|
|
16896
16938
|
chunkCount: input.chunkCount,
|
|
16897
16939
|
runtimeEnv: input.runtimeEnv,
|
|
@@ -16913,7 +16955,11 @@ function resolveEncoderTriple(config) {
|
|
|
16913
16955
|
`[plan] DistributedRenderConfig.codec is only valid for format="mp4"; received codec=${JSON.stringify(config.codec)} with format=${JSON.stringify(config.format)}. Omit codec for non-mp4 formats: mov is always ProRes 4444, webm is always libvpx-vp9, and png-sequence has no encoder.`
|
|
16914
16956
|
);
|
|
16915
16957
|
}
|
|
16916
|
-
return resolveNonMp4EncoderTriple(
|
|
16958
|
+
return resolveNonMp4EncoderTriple(
|
|
16959
|
+
config.format,
|
|
16960
|
+
config.quality ?? "standard",
|
|
16961
|
+
config.proresProfile
|
|
16962
|
+
);
|
|
16917
16963
|
}
|
|
16918
16964
|
function resolveMp4EncoderTriple(codec) {
|
|
16919
16965
|
const c = codec ?? "h264";
|
|
@@ -16927,9 +16973,10 @@ function resolveMp4EncoderTriple(codec) {
|
|
|
16927
16973
|
}
|
|
16928
16974
|
return { encoder: "libx264-software", pixelFormat: "yuv420p", preset: "medium" };
|
|
16929
16975
|
}
|
|
16930
|
-
function resolveNonMp4EncoderTriple(format, quality) {
|
|
16976
|
+
function resolveNonMp4EncoderTriple(format, quality, proresProfile) {
|
|
16931
16977
|
if (format === "mov") {
|
|
16932
|
-
|
|
16978
|
+
const { pixelFormat, preset } = getEncoderPreset4(quality, "mov", void 0, proresProfile);
|
|
16979
|
+
return { encoder: "prores-software", pixelFormat, preset };
|
|
16933
16980
|
}
|
|
16934
16981
|
if (format === "webm") {
|
|
16935
16982
|
const enginePreset = getEncoderPreset4(quality, "webm");
|
|
@@ -17584,6 +17631,18 @@ function validateDistributedRenderConfig(config) {
|
|
|
17584
17631
|
error instanceof Error ? error.message : String(error)
|
|
17585
17632
|
);
|
|
17586
17633
|
}
|
|
17634
|
+
try {
|
|
17635
|
+
validateDeliveryEncoderConfig({
|
|
17636
|
+
format: config.format,
|
|
17637
|
+
fps: { num: config.fps, den: 1 },
|
|
17638
|
+
proresProfile: config.proresProfile
|
|
17639
|
+
});
|
|
17640
|
+
} catch (error) {
|
|
17641
|
+
throw new InvalidConfigError(
|
|
17642
|
+
"config.proresProfile",
|
|
17643
|
+
error instanceof Error ? error.message : String(error)
|
|
17644
|
+
);
|
|
17645
|
+
}
|
|
17587
17646
|
if (config.chunkSize !== void 0) {
|
|
17588
17647
|
if (!Number.isInteger(config.chunkSize) || config.chunkSize < 1) {
|
|
17589
17648
|
throw new InvalidConfigError(
|
|
@@ -17823,6 +17882,7 @@ function assertRequestOptionScalars(options) {
|
|
|
17823
17882
|
for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
|
|
17824
17883
|
assertOptionalString(options, field);
|
|
17825
17884
|
}
|
|
17885
|
+
assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
|
|
17826
17886
|
assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
|
|
17827
17887
|
assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
|
|
17828
17888
|
assertOptionalEnum(options, "pageMotionBlur", ["off"]);
|
|
@@ -17924,6 +17984,7 @@ function distributedConfigFromRequest(request, runtime = {}) {
|
|
|
17924
17984
|
pageMotionBlur: options.pageMotionBlur,
|
|
17925
17985
|
bitrate: options.videoBitrate,
|
|
17926
17986
|
keyframeIntervalSeconds: options.keyframeIntervalSeconds,
|
|
17987
|
+
proresProfile: options.proresProfile,
|
|
17927
17988
|
maxBitrate: options.maxBitrate,
|
|
17928
17989
|
bufferSize: options.bufferSize,
|
|
17929
17990
|
videoFrameFormat: options.videoFrameFormat,
|
|
@@ -17974,6 +18035,7 @@ function renderRequestFromDistributedConfig(input) {
|
|
|
17974
18035
|
...optionalProperty("pageMotionBlur", config.pageMotionBlur),
|
|
17975
18036
|
...optionalProperty("videoBitrate", config.bitrate),
|
|
17976
18037
|
...optionalProperty("keyframeIntervalSeconds", config.keyframeIntervalSeconds),
|
|
18038
|
+
...optionalProperty("proresProfile", config.proresProfile),
|
|
17977
18039
|
...optionalProperty("maxBitrate", config.maxBitrate),
|
|
17978
18040
|
...optionalProperty("bufferSize", config.bufferSize),
|
|
17979
18041
|
...optionalProperty("videoFrameFormat", config.videoFrameFormat),
|
|
@@ -20596,7 +20658,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
20596
20658
|
const isPngSequence = plan2.dimensions.format === "png-sequence";
|
|
20597
20659
|
outputKind = isPngSequence ? "frame-dir" : "file";
|
|
20598
20660
|
const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
|
|
20599
|
-
const basePreset = getEncoderPreset6(
|
|
20661
|
+
const basePreset = getEncoderPreset6(
|
|
20662
|
+
job.config.quality,
|
|
20663
|
+
presetFormat,
|
|
20664
|
+
void 0,
|
|
20665
|
+
encoder.proresProfile
|
|
20666
|
+
);
|
|
20600
20667
|
const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
|
|
20601
20668
|
const effectiveQuality = encoder.crf ?? preset.quality;
|
|
20602
20669
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
package/dist/public-server.js
CHANGED
|
@@ -796,7 +796,7 @@ function createFileServer(options) {
|
|
|
796
796
|
if (!streamOwnsFd) closeSync(fd);
|
|
797
797
|
}
|
|
798
798
|
});
|
|
799
|
-
return new Promise((resolve11) => {
|
|
799
|
+
return new Promise((resolve11, reject) => {
|
|
800
800
|
const connections = /* @__PURE__ */ new Set();
|
|
801
801
|
const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
|
|
802
802
|
resolve11({
|
|
@@ -816,6 +816,7 @@ function createFileServer(options) {
|
|
|
816
816
|
connections.add(socket);
|
|
817
817
|
socket.on("close", () => connections.delete(socket));
|
|
818
818
|
});
|
|
819
|
+
server.on("error", reject);
|
|
819
820
|
});
|
|
820
821
|
}
|
|
821
822
|
|
|
@@ -867,9 +868,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
|
|
|
867
868
|
}
|
|
868
869
|
|
|
869
870
|
// src/services/render/deliveryEncoder.ts
|
|
870
|
-
import {
|
|
871
|
+
import {
|
|
872
|
+
PRORES_PROFILE_NAMES,
|
|
873
|
+
appendRateCapArgs,
|
|
874
|
+
isProresProfile,
|
|
875
|
+
keyframeIntervalToGopSize as keyframeIntervalToGopSize2
|
|
876
|
+
} from "@hyperframes/engine";
|
|
877
|
+
function validateProresProfile(format, proresProfile) {
|
|
878
|
+
if (proresProfile === void 0) return;
|
|
879
|
+
if (!isProresProfile(proresProfile)) {
|
|
880
|
+
throw new Error(
|
|
881
|
+
`proresProfile must be one of ${PRORES_PROFILE_NAMES.join(", ")}; got ${JSON.stringify(proresProfile)}`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
if (format !== void 0 && format !== "mov") {
|
|
885
|
+
throw new Error(`proresProfile applies to mov renders, not ${format}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
871
888
|
function validateDeliveryEncoderConfig(config) {
|
|
872
|
-
const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
|
|
889
|
+
const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
|
|
890
|
+
validateProresProfile(format, proresProfile);
|
|
873
891
|
if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
|
|
874
892
|
return;
|
|
875
893
|
if (keyframeIntervalSeconds !== void 0) {
|
|
@@ -3246,14 +3264,15 @@ async function extractHdrVideoFrames(args) {
|
|
|
3246
3264
|
}
|
|
3247
3265
|
first.release();
|
|
3248
3266
|
if (video.loop) (await windows.lease(totalFrames - 1))?.release();
|
|
3267
|
+
const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
|
|
3249
3268
|
out.set(videoId, {
|
|
3250
3269
|
dir: frameDir,
|
|
3251
|
-
windows,
|
|
3270
|
+
windows: held,
|
|
3252
3271
|
width: dims.width,
|
|
3253
3272
|
height: dims.height,
|
|
3254
3273
|
frameSize,
|
|
3255
3274
|
get frameCount() {
|
|
3256
|
-
return
|
|
3275
|
+
return held.totalFrames;
|
|
3257
3276
|
},
|
|
3258
3277
|
scratch: Buffer.allocUnsafe(frameSize),
|
|
3259
3278
|
loop: video.loop,
|
|
@@ -3282,9 +3301,18 @@ function hdrWindowFfmpegArgs(a) {
|
|
|
3282
3301
|
if (trim > 0) filters.push(`trim=start_frame=${trim}`);
|
|
3283
3302
|
return [
|
|
3284
3303
|
...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
|
|
3285
|
-
...["-vf", filters.join(","), "-frames:v", String(a.frames)]
|
|
3304
|
+
...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
|
|
3286
3305
|
];
|
|
3287
3306
|
}
|
|
3307
|
+
function holdFirstFrameUntilStreamStarts(windows, firstTick) {
|
|
3308
|
+
return {
|
|
3309
|
+
get totalFrames() {
|
|
3310
|
+
return firstTick + windows.totalFrames;
|
|
3311
|
+
},
|
|
3312
|
+
lease: (frameIndex) => windows.lease(Math.max(0, frameIndex - firstTick)),
|
|
3313
|
+
close: () => windows.close()
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3288
3316
|
function decodeHdrImageBuffers(args) {
|
|
3289
3317
|
const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
|
|
3290
3318
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -4850,6 +4878,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
|
|
|
4850
4878
|
`[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
4851
4879
|
);
|
|
4852
4880
|
}
|
|
4881
|
+
function formatCarriesHdr(format) {
|
|
4882
|
+
return format === "mp4";
|
|
4883
|
+
}
|
|
4884
|
+
function rendersSdrOnly(config) {
|
|
4885
|
+
return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
|
|
4886
|
+
}
|
|
4853
4887
|
function resolveEffectiveHdrMode(input) {
|
|
4854
4888
|
const hdrMode = input.hdrMode ?? "auto";
|
|
4855
4889
|
const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
|
|
@@ -4871,7 +4905,7 @@ function resolveEffectiveHdrMode(input) {
|
|
|
4871
4905
|
} else if (info?.hasHdr && info.dominantTransfer) {
|
|
4872
4906
|
effectiveHdr = { transfer: info.dominantTransfer };
|
|
4873
4907
|
}
|
|
4874
|
-
if (effectiveHdr && input.outputFormat
|
|
4908
|
+
if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
|
|
4875
4909
|
input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
|
|
4876
4910
|
effectiveHdr = void 0;
|
|
4877
4911
|
}
|
|
@@ -5685,7 +5719,7 @@ import { gsapCdnDist, normalizeGsapBaseUrl } from "@hyperframes/core/gsap-cdn";
|
|
|
5685
5719
|
import {
|
|
5686
5720
|
assignBundledRuntimeCompositionIds,
|
|
5687
5721
|
assignMediaRenderIds,
|
|
5688
|
-
|
|
5722
|
+
emitVariablesByCompScript,
|
|
5689
5723
|
inlineSubCompositions as inlineSubCompositionsShared,
|
|
5690
5724
|
ensureExternalLinkTag,
|
|
5691
5725
|
ensureExternalScriptTag,
|
|
@@ -7257,9 +7291,9 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
7257
7291
|
}
|
|
7258
7292
|
}
|
|
7259
7293
|
}
|
|
7260
|
-
|
|
7294
|
+
emitVariablesByCompScript(document2, result.variablesByComp);
|
|
7261
7295
|
if (body) {
|
|
7262
|
-
let pending =
|
|
7296
|
+
let pending = [];
|
|
7263
7297
|
const flushInline = () => {
|
|
7264
7298
|
if (!pending.length) return;
|
|
7265
7299
|
const joined = pending.join("\n;\n");
|
|
@@ -9457,7 +9491,7 @@ async function runExtractVideosStage(input) {
|
|
|
9457
9491
|
fps: job.config.fps,
|
|
9458
9492
|
outputDir: join18(compiledDir, "__hyperframes_video_frames"),
|
|
9459
9493
|
format: job.config.videoFrameFormat ?? "auto",
|
|
9460
|
-
toneMapHdrToSdr: job.config
|
|
9494
|
+
toneMapHdrToSdr: rendersSdrOnly(job.config),
|
|
9461
9495
|
timelineEnd: composition.duration,
|
|
9462
9496
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
9463
9497
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
@@ -14041,7 +14075,12 @@ async function executeRenderPipeline(input) {
|
|
|
14041
14075
|
});
|
|
14042
14076
|
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
14043
14077
|
const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
|
|
14044
|
-
const preset = getEncoderPreset3(
|
|
14078
|
+
const preset = getEncoderPreset3(
|
|
14079
|
+
job.config.quality,
|
|
14080
|
+
presetFormat,
|
|
14081
|
+
encoderHdr,
|
|
14082
|
+
job.config.proresProfile
|
|
14083
|
+
);
|
|
14045
14084
|
if (job.config.crf != null && job.config.videoBitrate) {
|
|
14046
14085
|
log.warn(
|
|
14047
14086
|
`[Render] Both crf=${job.config.crf} and videoBitrate=${job.config.videoBitrate} were set. These are mutually exclusive; honoring crf and ignoring videoBitrate. Set only one to silence this warning.`
|
|
@@ -15071,6 +15110,7 @@ import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options"
|
|
|
15071
15110
|
|
|
15072
15111
|
// src/renderRequest.ts
|
|
15073
15112
|
import {
|
|
15113
|
+
PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
|
|
15074
15114
|
isVideoFrameFormat as isVideoFrameFormat2,
|
|
15075
15115
|
resolveConfig as resolveConfig4,
|
|
15076
15116
|
validateEngineConfigSnapshot as validateEngineConfigSnapshot2
|
|
@@ -15353,6 +15393,7 @@ function assertRequestOptionScalars(options) {
|
|
|
15353
15393
|
for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
|
|
15354
15394
|
assertOptionalString(options, field);
|
|
15355
15395
|
}
|
|
15396
|
+
assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
|
|
15356
15397
|
assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
|
|
15357
15398
|
assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
|
|
15358
15399
|
assertOptionalEnum(options, "pageMotionBlur", ["off"]);
|
package/dist/renderRequest.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EngineConfig, type VideoFrameFormat } from "@hyperframes/engine";
|
|
1
|
+
import { type EngineConfig, type ProresProfile, type VideoFrameFormat } from "@hyperframes/engine";
|
|
2
2
|
import { type CanvasResolution, type Fps } from "@hyperframes/core";
|
|
3
3
|
import type { ProducerLogger } from "./logger.js";
|
|
4
4
|
import type { RenderConfig } from "./services/renderOrchestrator.js";
|
|
@@ -38,6 +38,7 @@ export interface RenderRequestOptions {
|
|
|
38
38
|
crf?: number;
|
|
39
39
|
videoBitrate?: string;
|
|
40
40
|
keyframeIntervalSeconds?: number;
|
|
41
|
+
proresProfile?: ProresProfile;
|
|
41
42
|
maxBitrate?: string;
|
|
42
43
|
bufferSize?: string;
|
|
43
44
|
videoFrameFormat?: VideoFrameFormat;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CanvasResolution, type DistributedFps } from "@hyperframes/core";
|
|
2
|
-
import { type EngineConfig, type VideoFrameFormat, type AudioProcessingFailure } from "@hyperframes/engine";
|
|
2
|
+
import { type EngineConfig, type VideoFrameFormat, type ProresProfile, type AudioProcessingFailure } from "@hyperframes/engine";
|
|
3
3
|
import { type ProducerLogger } from "../../logger.js";
|
|
4
4
|
import { type RenderJob, type RenderStrictness } from "../renderOrchestrator.js";
|
|
5
5
|
import { type ChunkSliceJson, type PlanFileDigests } from "../render/stages/freezePlan.js";
|
|
@@ -49,6 +49,8 @@ export interface DistributedRenderConfig extends Pick<RenderRequestOptions, "pag
|
|
|
49
49
|
bitrate?: string;
|
|
50
50
|
/** Longest gap between keyframes, in seconds (mp4). Chunk joins add keyframes, so this is a maximum. */
|
|
51
51
|
keyframeIntervalSeconds?: number;
|
|
52
|
+
/** ProRes profile for `format: "mov"`; the 422 profiles have no alpha. Defaults to `4444`. */
|
|
53
|
+
proresProfile?: ProresProfile;
|
|
52
54
|
/** Peak bitrate cap (mp4), per chunk. */
|
|
53
55
|
maxBitrate?: string;
|
|
54
56
|
/** Rate-cap buffer; defaults to twice `maxBitrate`. */
|
|
@@ -7,6 +7,7 @@ export interface DeliveryEncoderConfig {
|
|
|
7
7
|
keyframeIntervalSeconds?: number;
|
|
8
8
|
maxBitrate?: string;
|
|
9
9
|
bufferSize?: string;
|
|
10
|
+
proresProfile?: string;
|
|
10
11
|
}
|
|
11
12
|
/** The `EncoderOptions` fields the delivery config sets, ready to spread into any encoder input. */
|
|
12
13
|
export interface DeliveryEncoderOptions {
|
|
@@ -34,5 +34,6 @@ export declare function findRenderHdrAutoPromotionTrigger(input: {
|
|
|
34
34
|
}[];
|
|
35
35
|
nativeHdrImageIds: ReadonlySet<string>;
|
|
36
36
|
}): string | undefined;
|
|
37
|
+
export declare function rendersSdrOnly(config: Pick<RenderConfig, "hdrMode" | "format">): boolean;
|
|
37
38
|
export declare function resolveEffectiveHdrMode(input: HdrModeInput): EffectiveHdr;
|
|
38
39
|
export {};
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* has in memory.
|
|
11
11
|
*/
|
|
12
12
|
import type { Fps } from "@hyperframes/core";
|
|
13
|
+
import type { ProresProfile } from "@hyperframes/engine";
|
|
13
14
|
import type { RenderRequestOptions } from "../../../renderRequest.js";
|
|
14
15
|
import { type PlanDimensions } from "./planHash.js";
|
|
15
16
|
/**
|
|
@@ -32,6 +33,7 @@ export interface LockedRenderConfig extends Pick<RenderRequestOptions, "pageMoti
|
|
|
32
33
|
*/
|
|
33
34
|
quality: "draft" | "standard" | "high";
|
|
34
35
|
ffmpegVersion: string;
|
|
36
|
+
proresProfile?: ProresProfile;
|
|
35
37
|
preset: string;
|
|
36
38
|
crf?: number;
|
|
37
39
|
bitrate?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CanvasResolution, type Fps, type FpsInput, type HfVfxCapture } from "@hyperframes/core";
|
|
2
|
-
import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
|
|
2
|
+
import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ProresProfile, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
|
|
3
3
|
import { type ProducerLogger } from "../logger.js";
|
|
4
4
|
import { type RenderOutputFormat } from "./render/renderFormat.js";
|
|
5
5
|
import { type RenderCaptureObservability, type RenderObservabilitySummary, type OpenStallAttempt, type StageStallRule } from "./render/observability.js";
|
|
@@ -103,6 +103,8 @@ export interface RenderConfig {
|
|
|
103
103
|
videoBitrate?: string;
|
|
104
104
|
/** Keyframe every N seconds (mp4). A maximum where chunks join, as on Lambda. */
|
|
105
105
|
keyframeIntervalSeconds?: number;
|
|
106
|
+
/** MOV only: ProRes profile. Default `4444` keeps alpha; the 422 profiles drop it. */
|
|
107
|
+
proresProfile?: ProresProfile;
|
|
106
108
|
/** Peak bitrate cap (e.g. "8M", mp4) alongside CRF. */
|
|
107
109
|
maxBitrate?: string;
|
|
108
110
|
/** Rate-cap buffer; defaults to twice `maxBitrate`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.210",
|
|
4
4
|
"description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"postcss": "^8.4.0",
|
|
36
36
|
"puppeteer": "^25.2.1",
|
|
37
37
|
"puppeteer-core": "^25.2.1",
|
|
38
|
-
"@hyperframes/core": "0.8.
|
|
39
|
-
"@hyperframes/
|
|
40
|
-
"@hyperframes/
|
|
41
|
-
"@hyperframes/studio-server": "0.8.
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/core": "0.8.210",
|
|
39
|
+
"@hyperframes/engine": "0.8.210",
|
|
40
|
+
"@hyperframes/lint": "0.8.210",
|
|
41
|
+
"@hyperframes/studio-server": "0.8.210",
|
|
42
|
+
"@hyperframes/parsers": "0.8.210"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.0.10",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.11",
|
|
51
|
-
"@hyperframes/aws-lambda": "0.8.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.8.210"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|