@hyperframes/producer 0.8.197 → 0.8.199

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.
@@ -26,6 +26,7 @@ export interface RenderRequestOptions {
26
26
  gifLoop?: number;
27
27
  hlsSegmentSeconds?: number;
28
28
  workers?: number;
29
+ maxRenderMs?: number;
29
30
  useGpu?: boolean;
30
31
  debug?: boolean;
31
32
  /** Segmented capture: reuse segments recorded in a matching manifest. */
package/dist/server.d.ts CHANGED
@@ -44,6 +44,7 @@ interface RenderInput {
44
44
  pageMotionBlur?: RenderConfig["pageMotionBlur"];
45
45
  outputDynamicRange?: "auto" | "hdr" | "sdr";
46
46
  workers?: number;
47
+ maxRenderMs?: number;
47
48
  crf?: number;
48
49
  videoBitrate?: string;
49
50
  useGpu: boolean;
@@ -0,0 +1,16 @@
1
+ import type { ChunkSliceJson } from "../render/stages/freezePlan.js";
2
+ import type { PlanVideosJson } from "./shared.js";
3
+ /** Estimated capture cost of every frame: page base plus each active video's pixels. */
4
+ export declare function planFrameWeights(input: {
5
+ videos: PlanVideosJson["videos"];
6
+ extracted: PlanVideosJson["extracted"];
7
+ totalFrames: number;
8
+ fps: number;
9
+ width: number;
10
+ height: number;
11
+ }): Float64Array;
12
+ /**
13
+ * Cut `[0, weights.length)` into `chunkCount` ranges of ~equal summed weight, each within
14
+ * `[minFrames, maxFrames]` where the frame count allows. `null` when the cost is uniform.
15
+ */
16
+ export declare function buildWeightedChunkSlices(weights: Float64Array, chunkCount: number, minFrames: number, maxFrames: number): ChunkSliceJson[] | null;
@@ -271,6 +271,8 @@ export declare const DEFAULT_MAX_PARALLEL_CHUNKS = 16;
271
271
  * outweighs the parallelism gain on tiny renders.
272
272
  */
273
273
  export declare const MIN_CHUNK_SIZE = 10;
274
+ /** Largest `chunkSize`; `validateDistributedRenderConfig` enforces it, and cost-weighted chunks stay under it. */
275
+ export declare const MAX_CHUNK_SIZE = 3600;
274
276
  /** Most chunks one render may plan; `validateDistributedRenderConfig` enforces it on `maxParallelChunks`. */
275
277
  export declare const MAX_PARALLEL_CHUNKS_CEILING = 256;
276
278
  /** Predicted seconds for one chunk of `frames` frames at `megapixels` output pixels per frame. */
@@ -402,6 +404,11 @@ export declare function resolveChunkPlan(totalFrames: number, configChunkSize: n
402
404
  * upper bound — chunk workers iterate `i in [startFrame, endFrame)`.
403
405
  */
404
406
  export declare function buildChunkSlices(totalFrames: number, chunkCount: number, effectiveChunkSize: number): ChunkSliceJson[];
407
+ /**
408
+ * The plan's chunk cut: auto-sized plans re-cut the same chunk count by estimated capture cost;
409
+ * explicit `chunkSize` or uniform cost keeps {@link buildChunkSlices}; `targetChunkFrames` and the time limit cap it.
410
+ */
411
+ export declare function buildPlanChunkSlices(frameWeights: Float64Array, config: Pick<DistributedRenderConfig, "chunkSize" | "targetChunkFrames">, chunkCount: number, effectiveChunkSize: number, maxFramesPerChunk?: number): ChunkSliceJson[];
405
412
  /** Test-visible construction of the engine config used by distributed planning. */
406
413
  export declare function resolveDistributedEngineConfig(config: DistributedRenderConfig): EngineConfig;
407
414
  /**
@@ -10,6 +10,7 @@
10
10
  * `captureHdrHybridLoop.ts`, `captureHdrFrameShared.ts`,
11
11
  * `captureHdrResources.ts`.
12
12
  */
13
+ import type { HdrFrameWindows } from "./hdrFrameWindows.js";
13
14
  import { type CaptureSession, type BeforeCaptureHook, type HdrTransfer, type ElementStackingInfo, type HfTransitionMeta } from "@hyperframes/engine";
14
15
  import type { ProducerLogger } from "../logger.js";
15
16
  import { type HdrImageTransferCache } from "./hdrImageTransferCache.js";
@@ -34,21 +35,20 @@ export interface TransitionRange extends HdrTransitionMeta {
34
35
  */
35
36
  export interface HdrVideoFrameSource {
36
37
  dir: string;
37
- rawPath: string;
38
- fd: number;
38
+ windows: HdrFrameWindows;
39
39
  width: number;
40
40
  height: number;
41
41
  frameSize: number;
42
- frameCount: number;
42
+ readonly frameCount: number;
43
43
  scratch: Buffer;
44
- /** The raw file contains one playable source cycle and must wrap at EOF. */
44
+ /** The playable range is one source cycle and must wrap at its end. */
45
45
  loop?: boolean;
46
46
  mediaStart?: number;
47
47
  videoStreamLeadSeconds?: number;
48
48
  }
49
49
  export declare function closeHdrVideoFrameSource(source: HdrVideoFrameSource, log?: ProducerLogger): void;
50
50
  export declare function resolveHdrVideoFrameIndex(time: number, startTime: number, fps: number, frameCount: number, loop?: boolean): number | null;
51
- export declare function blitHdrVideoLayer(canvas: Buffer, el: ElementStackingInfo, time: number, fps: number, hdrVideoFrameSources: Map<string, HdrVideoFrameSource>, hdrStartTimes: Map<string, number>, width: number, height: number, log?: ProducerLogger, sourceTransfer?: HdrTransfer, targetTransfer?: HdrTransfer, hdrPerf?: HdrPerfCollector): void;
51
+ export declare function blitHdrVideoLayer(canvas: Buffer, el: ElementStackingInfo, time: number, fps: number, hdrVideoFrameSources: Map<string, HdrVideoFrameSource>, hdrStartTimes: Map<string, number>, width: number, height: number, log?: ProducerLogger, sourceTransfer?: HdrTransfer, targetTransfer?: HdrTransfer, hdrPerf?: HdrPerfCollector): Promise<void>;
52
52
  /**
53
53
  * Pre-decoded HDR image buffer with its native pixel dimensions.
54
54
  *
@@ -0,0 +1,23 @@
1
+ export interface HdrFrameLease {
2
+ fd: number;
3
+ offset: number;
4
+ release(): void;
5
+ }
6
+ export interface HdrWindowSpec {
7
+ firstFrame: number;
8
+ frames: number;
9
+ path: string;
10
+ }
11
+ export interface HdrFrameWindows {
12
+ readonly totalFrames: number;
13
+ lease(frameIndex: number): Promise<HdrFrameLease | null>;
14
+ close(): void;
15
+ }
16
+ export declare function createHdrFrameWindows(opts: {
17
+ dir: string;
18
+ frameSize: number;
19
+ totalFrames: number;
20
+ windowFrames: number;
21
+ maxResident: number;
22
+ extract: (spec: HdrWindowSpec) => Promise<number>;
23
+ }): HdrFrameWindows;
@@ -0,0 +1,35 @@
1
+ interface RenderEtaInput {
2
+ /** Absent or 0 means no guard. */
3
+ maxRenderMs?: number;
4
+ /** Wall time since the render started, so time already spent counts. */
5
+ elapsedMs: number;
6
+ framesCompleted: number;
7
+ totalFrames: number;
8
+ /** Wall ms per frame across all workers. */
9
+ msPerFrame: number;
10
+ }
11
+ export declare class RenderTooSlowError extends Error {
12
+ readonly code: "RENDER_TOO_SLOW";
13
+ readonly owner: "user";
14
+ readonly retryable: false;
15
+ readonly publicMetadata: Readonly<Record<string, unknown>>;
16
+ constructor(stage: string, input: Required<RenderEtaInput> & {
17
+ etaMs: number;
18
+ });
19
+ }
20
+ /** The one ETA decision, shared by the post-calibration and the measured-rate checks. */
21
+ export declare function checkRenderEta(stage: string, input: RenderEtaInput): RenderTooSlowError | undefined;
22
+ interface RenderEtaGuardOptions {
23
+ maxRenderMs?: number;
24
+ job: {
25
+ framesRendered?: number;
26
+ };
27
+ totalFrames: number;
28
+ pipelineStartMs: number;
29
+ onTooSlow: (error: RenderTooSlowError) => void;
30
+ now?: () => number;
31
+ pollMs?: number;
32
+ }
33
+ /** Polls `job.framesRendered`, which every capture path updates, and trips once. Returns stop(). */
34
+ export declare function startRenderEtaGuard(options: RenderEtaGuardOptions): () => void;
35
+ export {};
@@ -58,19 +58,6 @@ export declare function probeHdrExtractionDims(args: {
58
58
  composition: CompositionMetadata;
59
59
  prep: HdrResourcePrep;
60
60
  }): Promise<void>;
61
- /**
62
- * Total bytes the raw rgb48le pre-extraction below will write: 6 bytes/px ×
63
- * frame area × frame count, summed across HDR videos. Exposed for the
64
- * disk-headroom gate — a plain iPhone HLG clip auto-detected as HDR filled a
65
- * near-full disk with 16-bit raw frames before the user found --sdr (wild
66
- * report, CLI 0.7.52), so the commitment must be checked and surfaced BEFORE
67
- * FFmpeg starts writing.
68
- */
69
- export declare function estimateHdrExtractionBytes(videos: Array<{
70
- durationSeconds: number;
71
- width: number;
72
- height: number;
73
- }>, fps: number): number;
74
61
  export declare function resolveHdrExtractionBudgetBytes(raw: string | undefined, cgroupLimitMb?: number | null): number | undefined;
75
62
  export declare function resolveHdrExtractionActiveBudgetBytes(configuredBudgetBytes: number | undefined, freeBytes: number): number;
76
63
  export declare function reserveHdrExtractionBytes(estimatedBytes: number, budgetBytes: number | undefined): () => void;
@@ -91,10 +78,9 @@ export interface HdrVideoExtractionResult {
91
78
  releaseReservation: () => void;
92
79
  }
93
80
  /**
94
- * Extract each HDR video into a raw rgb48le frame file via a single FFmpeg
95
- * pass per video, and open a file descriptor for each. The caller owns both
96
- * source teardown and the aggregate scratch reservation, which intentionally
97
- * remains held until the capture-stage finally block.
81
+ * Plan each HDR video's frame windows; a source extracts a window with one FFmpeg pass when the
82
+ * capture first reaches it. The caller owns teardown and the scratch reservation (resident
83
+ * windows only), held until the capture-stage finally block.
98
84
  */
99
85
  export declare function extractHdrVideoFrames(args: {
100
86
  job: RenderJob;
@@ -106,6 +92,8 @@ export declare function extractHdrVideoFrames(args: {
106
92
  height: number;
107
93
  abortSignal: AbortSignal | undefined;
108
94
  hdrDiagnostics: HdrDiagnostics;
95
+ maxResidentWindows?: number;
96
+ windowBytes?: number;
109
97
  runFfmpegImpl?: typeof runFfmpeg;
110
98
  extractMediaMetadataImpl?: typeof extractMediaMetadata;
111
99
  resolveFinalFrameExtractionWindowImpl?: typeof resolveFinalFrameExtractionWindow;
@@ -96,6 +96,8 @@ export interface RenderConfig {
96
96
  motionBlur?: MotionBlurOptions;
97
97
  pageMotionBlur?: "off";
98
98
  workers?: number;
99
+ /** Wall-time budget in ms for the whole render. Absent or 0 disables the ETA guard. */
100
+ maxRenderMs?: number;
99
101
  useGpu?: boolean;
100
102
  debug?: boolean;
101
103
  /** Strict rejects correctness warnings; best-effort returns a qualified outcome. */
@@ -822,19 +824,17 @@ export declare function resolveDeShortBand(args: {
822
824
  elementCountSource: "live" | "static";
823
825
  }): "applied" | "skipped_elements" | "unmeasured" | undefined;
824
826
  /**
825
- * DE priority inversion predicate: should an AUTO-resolved multi-worker render
826
- * drop to single-worker verified drawElement streaming?
827
+ * DE priority inversion predicate: should a multi-worker render drop to
828
+ * single-worker verified drawElement streaming?
827
829
  *
828
830
  * Benchmarked 2026-07-08: above ~900 frames DE-single beats screenshot-parallel
829
831
  * at every worker count (2,380f: 66s vs 109–127s at W2–W5); below it DE's fixed
830
832
  * init cost (verify + dedup arming) loses by a small margin. Only fires for the
831
833
  * exact benchmarked configuration: default-on DE, mp4, streaming-eligible,
832
- * no compile gate, no forced screenshot, workers not explicitly requested.
834
+ * no compile gate, no forced screenshot.
833
835
  */
834
836
  export declare function shouldPreferSingleWorkerDrawElement(args: {
835
837
  workerCount: number;
836
- /** job.config.workers — a number means the user explicitly chose. */
837
- requestedWorkers: number | "auto" | undefined;
838
838
  useDrawElement: boolean;
839
839
  deCompileGate: string | undefined;
840
840
  forceScreenshot: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.197",
3
+ "version": "0.8.199",
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/engine": "0.8.197",
39
- "@hyperframes/lint": "0.8.197",
40
- "@hyperframes/core": "0.8.197",
41
- "@hyperframes/parsers": "0.8.197",
42
- "@hyperframes/studio-server": "0.8.197"
38
+ "@hyperframes/core": "0.8.199",
39
+ "@hyperframes/lint": "0.8.199",
40
+ "@hyperframes/parsers": "0.8.199",
41
+ "@hyperframes/studio-server": "0.8.199",
42
+ "@hyperframes/engine": "0.8.199"
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.197"
51
+ "@hyperframes/aws-lambda": "0.8.199"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"