@hyperframes/producer 0.8.209 → 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/index.js CHANGED
@@ -847,9 +847,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
847
847
  }
848
848
 
849
849
  // src/services/render/deliveryEncoder.ts
850
- import { appendRateCapArgs, keyframeIntervalToGopSize as keyframeIntervalToGopSize2 } from "@hyperframes/engine";
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
+ }
851
867
  function validateDeliveryEncoderConfig(config) {
852
- const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
868
+ const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
869
+ validateProresProfile(format, proresProfile);
853
870
  if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
854
871
  return;
855
872
  if (keyframeIntervalSeconds !== void 0) {
@@ -3230,14 +3247,15 @@ async function extractHdrVideoFrames(args) {
3230
3247
  }
3231
3248
  first.release();
3232
3249
  if (video.loop) (await windows.lease(totalFrames - 1))?.release();
3250
+ const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
3233
3251
  out.set(videoId, {
3234
3252
  dir: frameDir,
3235
- windows,
3253
+ windows: held,
3236
3254
  width: dims.width,
3237
3255
  height: dims.height,
3238
3256
  frameSize,
3239
3257
  get frameCount() {
3240
- return windows.totalFrames;
3258
+ return held.totalFrames;
3241
3259
  },
3242
3260
  scratch: Buffer.allocUnsafe(frameSize),
3243
3261
  loop: video.loop,
@@ -3266,9 +3284,18 @@ function hdrWindowFfmpegArgs(a) {
3266
3284
  if (trim > 0) filters.push(`trim=start_frame=${trim}`);
3267
3285
  return [
3268
3286
  ...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
3269
- ...["-vf", filters.join(","), "-frames:v", String(a.frames)]
3287
+ ...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
3270
3288
  ];
3271
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
+ }
3272
3299
  function decodeHdrImageBuffers(args) {
3273
3300
  const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
3274
3301
  const out = /* @__PURE__ */ new Map();
@@ -4834,6 +4861,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4834
4861
  `[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
4835
4862
  );
4836
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
+ }
4837
4870
  function resolveEffectiveHdrMode(input) {
4838
4871
  const hdrMode = input.hdrMode ?? "auto";
4839
4872
  const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
@@ -4855,7 +4888,7 @@ function resolveEffectiveHdrMode(input) {
4855
4888
  } else if (info?.hasHdr && info.dominantTransfer) {
4856
4889
  effectiveHdr = { transfer: info.dominantTransfer };
4857
4890
  }
4858
- if (effectiveHdr && input.outputFormat !== "mp4") {
4891
+ if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
4859
4892
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4860
4893
  effectiveHdr = void 0;
4861
4894
  }
@@ -9477,7 +9510,7 @@ async function runExtractVideosStage(input) {
9477
9510
  fps: job.config.fps,
9478
9511
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9479
9512
  format: job.config.videoFrameFormat ?? "auto",
9480
- toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
9513
+ toneMapHdrToSdr: rendersSdrOnly(job.config),
9481
9514
  timelineEnd: composition.duration,
9482
9515
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9483
9516
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -14061,7 +14094,12 @@ async function executeRenderPipeline(input) {
14061
14094
  });
14062
14095
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
14063
14096
  const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
14064
- const preset = getEncoderPreset3(job.config.quality, presetFormat, encoderHdr);
14097
+ const preset = getEncoderPreset3(
14098
+ job.config.quality,
14099
+ presetFormat,
14100
+ encoderHdr,
14101
+ job.config.proresProfile
14102
+ );
14065
14103
  if (job.config.crf != null && job.config.videoBitrate) {
14066
14104
  log.warn(
14067
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.`
@@ -14876,6 +14914,7 @@ async function executeRenderPipeline(input) {
14876
14914
 
14877
14915
  // src/renderRequest.ts
14878
14916
  import {
14917
+ PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
14879
14918
  isVideoFrameFormat as isVideoFrameFormat2,
14880
14919
  resolveConfig as resolveConfig4,
14881
14920
  validateEngineConfigSnapshot as validateEngineConfigSnapshot2
@@ -16893,6 +16932,8 @@ function buildLockedRenderConfig(input) {
16893
16932
  closedGop: true,
16894
16933
  forceKeyframes: "n=0",
16895
16934
  pixelFormat,
16935
+ // Omitted for the default so existing 4444 plans keep their planHash.
16936
+ ...config.proresProfile && config.proresProfile !== "4444" ? { proresProfile: config.proresProfile } : {},
16896
16937
  chunkSize: input.effectiveChunkSize,
16897
16938
  chunkCount: input.chunkCount,
16898
16939
  runtimeEnv: input.runtimeEnv,
@@ -16914,7 +16955,11 @@ function resolveEncoderTriple(config) {
16914
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.`
16915
16956
  );
16916
16957
  }
16917
- return resolveNonMp4EncoderTriple(config.format, config.quality ?? "standard");
16958
+ return resolveNonMp4EncoderTriple(
16959
+ config.format,
16960
+ config.quality ?? "standard",
16961
+ config.proresProfile
16962
+ );
16918
16963
  }
16919
16964
  function resolveMp4EncoderTriple(codec) {
16920
16965
  const c = codec ?? "h264";
@@ -16928,9 +16973,10 @@ function resolveMp4EncoderTriple(codec) {
16928
16973
  }
16929
16974
  return { encoder: "libx264-software", pixelFormat: "yuv420p", preset: "medium" };
16930
16975
  }
16931
- function resolveNonMp4EncoderTriple(format, quality) {
16976
+ function resolveNonMp4EncoderTriple(format, quality, proresProfile) {
16932
16977
  if (format === "mov") {
16933
- return { encoder: "prores-software", pixelFormat: "yuva444p10le", preset: "4444" };
16978
+ const { pixelFormat, preset } = getEncoderPreset4(quality, "mov", void 0, proresProfile);
16979
+ return { encoder: "prores-software", pixelFormat, preset };
16934
16980
  }
16935
16981
  if (format === "webm") {
16936
16982
  const enginePreset = getEncoderPreset4(quality, "webm");
@@ -17585,6 +17631,18 @@ function validateDistributedRenderConfig(config) {
17585
17631
  error instanceof Error ? error.message : String(error)
17586
17632
  );
17587
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
+ }
17588
17646
  if (config.chunkSize !== void 0) {
17589
17647
  if (!Number.isInteger(config.chunkSize) || config.chunkSize < 1) {
17590
17648
  throw new InvalidConfigError(
@@ -17824,6 +17882,7 @@ function assertRequestOptionScalars(options) {
17824
17882
  for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
17825
17883
  assertOptionalString(options, field);
17826
17884
  }
17885
+ assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
17827
17886
  assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
17828
17887
  assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
17829
17888
  assertOptionalEnum(options, "pageMotionBlur", ["off"]);
@@ -17925,6 +17984,7 @@ function distributedConfigFromRequest(request, runtime = {}) {
17925
17984
  pageMotionBlur: options.pageMotionBlur,
17926
17985
  bitrate: options.videoBitrate,
17927
17986
  keyframeIntervalSeconds: options.keyframeIntervalSeconds,
17987
+ proresProfile: options.proresProfile,
17928
17988
  maxBitrate: options.maxBitrate,
17929
17989
  bufferSize: options.bufferSize,
17930
17990
  videoFrameFormat: options.videoFrameFormat,
@@ -17975,6 +18035,7 @@ function renderRequestFromDistributedConfig(input) {
17975
18035
  ...optionalProperty("pageMotionBlur", config.pageMotionBlur),
17976
18036
  ...optionalProperty("videoBitrate", config.bitrate),
17977
18037
  ...optionalProperty("keyframeIntervalSeconds", config.keyframeIntervalSeconds),
18038
+ ...optionalProperty("proresProfile", config.proresProfile),
17978
18039
  ...optionalProperty("maxBitrate", config.maxBitrate),
17979
18040
  ...optionalProperty("bufferSize", config.bufferSize),
17980
18041
  ...optionalProperty("videoFrameFormat", config.videoFrameFormat),
@@ -20597,7 +20658,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20597
20658
  const isPngSequence = plan2.dimensions.format === "png-sequence";
20598
20659
  outputKind = isPngSequence ? "frame-dir" : "file";
20599
20660
  const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
20600
- const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
20661
+ const basePreset = getEncoderPreset6(
20662
+ job.config.quality,
20663
+ presetFormat,
20664
+ void 0,
20665
+ encoder.proresProfile
20666
+ );
20601
20667
  const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
20602
20668
  const effectiveQuality = encoder.crf ?? preset.quality;
20603
20669
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
@@ -868,9 +868,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
868
868
  }
869
869
 
870
870
  // src/services/render/deliveryEncoder.ts
871
- import { appendRateCapArgs, keyframeIntervalToGopSize as keyframeIntervalToGopSize2 } from "@hyperframes/engine";
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
+ }
872
888
  function validateDeliveryEncoderConfig(config) {
873
- const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
889
+ const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
890
+ validateProresProfile(format, proresProfile);
874
891
  if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
875
892
  return;
876
893
  if (keyframeIntervalSeconds !== void 0) {
@@ -3247,14 +3264,15 @@ async function extractHdrVideoFrames(args) {
3247
3264
  }
3248
3265
  first.release();
3249
3266
  if (video.loop) (await windows.lease(totalFrames - 1))?.release();
3267
+ const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
3250
3268
  out.set(videoId, {
3251
3269
  dir: frameDir,
3252
- windows,
3270
+ windows: held,
3253
3271
  width: dims.width,
3254
3272
  height: dims.height,
3255
3273
  frameSize,
3256
3274
  get frameCount() {
3257
- return windows.totalFrames;
3275
+ return held.totalFrames;
3258
3276
  },
3259
3277
  scratch: Buffer.allocUnsafe(frameSize),
3260
3278
  loop: video.loop,
@@ -3283,9 +3301,18 @@ function hdrWindowFfmpegArgs(a) {
3283
3301
  if (trim > 0) filters.push(`trim=start_frame=${trim}`);
3284
3302
  return [
3285
3303
  ...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
3286
- ...["-vf", filters.join(","), "-frames:v", String(a.frames)]
3304
+ ...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
3287
3305
  ];
3288
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
+ }
3289
3316
  function decodeHdrImageBuffers(args) {
3290
3317
  const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
3291
3318
  const out = /* @__PURE__ */ new Map();
@@ -4851,6 +4878,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4851
4878
  `[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
4852
4879
  );
4853
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
+ }
4854
4887
  function resolveEffectiveHdrMode(input) {
4855
4888
  const hdrMode = input.hdrMode ?? "auto";
4856
4889
  const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
@@ -4872,7 +4905,7 @@ function resolveEffectiveHdrMode(input) {
4872
4905
  } else if (info?.hasHdr && info.dominantTransfer) {
4873
4906
  effectiveHdr = { transfer: info.dominantTransfer };
4874
4907
  }
4875
- if (effectiveHdr && input.outputFormat !== "mp4") {
4908
+ if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
4876
4909
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4877
4910
  effectiveHdr = void 0;
4878
4911
  }
@@ -9458,7 +9491,7 @@ async function runExtractVideosStage(input) {
9458
9491
  fps: job.config.fps,
9459
9492
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9460
9493
  format: job.config.videoFrameFormat ?? "auto",
9461
- toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
9494
+ toneMapHdrToSdr: rendersSdrOnly(job.config),
9462
9495
  timelineEnd: composition.duration,
9463
9496
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9464
9497
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -14042,7 +14075,12 @@ async function executeRenderPipeline(input) {
14042
14075
  });
14043
14076
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
14044
14077
  const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
14045
- const preset = getEncoderPreset3(job.config.quality, presetFormat, encoderHdr);
14078
+ const preset = getEncoderPreset3(
14079
+ job.config.quality,
14080
+ presetFormat,
14081
+ encoderHdr,
14082
+ job.config.proresProfile
14083
+ );
14046
14084
  if (job.config.crf != null && job.config.videoBitrate) {
14047
14085
  log.warn(
14048
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.`
@@ -15072,6 +15110,7 @@ import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options"
15072
15110
 
15073
15111
  // src/renderRequest.ts
15074
15112
  import {
15113
+ PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
15075
15114
  isVideoFrameFormat as isVideoFrameFormat2,
15076
15115
  resolveConfig as resolveConfig4,
15077
15116
  validateEngineConfigSnapshot as validateEngineConfigSnapshot2
@@ -15354,6 +15393,7 @@ function assertRequestOptionScalars(options) {
15354
15393
  for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
15355
15394
  assertOptionalString(options, field);
15356
15395
  }
15396
+ assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
15357
15397
  assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
15358
15398
  assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
15359
15399
  assertOptionalEnum(options, "pageMotionBlur", ["off"]);
@@ -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.209",
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.209",
39
- "@hyperframes/engine": "0.8.209",
40
- "@hyperframes/lint": "0.8.209",
41
- "@hyperframes/parsers": "0.8.209",
42
- "@hyperframes/studio-server": "0.8.209"
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.209"
51
+ "@hyperframes/aws-lambda": "0.8.210"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"