@hyperframes/producer 0.9.16 → 0.9.17

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.
@@ -38,8 +38,7 @@ export declare function buildChunkCaptureOptions(input: {
38
38
  skipReadinessVideoIds?: string[];
39
39
  videoMetadataHints?: CaptureOptions["videoMetadataHints"];
40
40
  }): CaptureOptions;
41
- /** Extracts the given frames of deferred videos under the names a full extraction gives them. */
42
- export declare function shownDeferredFrameIndexes(planVideos: PlanVideosJson, frames: Iterable<number>, fps: {
41
+ export declare function shownPlanFrameIndexes(planVideos: PlanVideosJson, frames: Iterable<number>, fps: {
43
42
  num: number;
44
43
  den: number;
45
44
  }): Map<string, Set<number>>;
@@ -0,0 +1,42 @@
1
+ import { type EngineConfig, type ExtractedFrames, type ShownBox, type VideoElement } from "@hyperframes/engine";
2
+ /** The frame indexes each deferred video shows across `frames`, by video id. */
3
+ export declare function shownDeferredFrameIndexes(source: {
4
+ videos: VideoElement[];
5
+ extracted: ExtractedFrames[];
6
+ }, frames: Iterable<number>, fps: {
7
+ num: number;
8
+ den: number;
9
+ }): Map<string, Set<number>>;
10
+ /** Extracts the given frames of deferred videos under the names a full extraction gives them. */
11
+ export declare function extractDeferredFrames(input: {
12
+ videos: VideoElement[];
13
+ extracted: ExtractedFrames[];
14
+ frames: Iterable<number>;
15
+ fps: {
16
+ num: number;
17
+ den: number;
18
+ };
19
+ outputRoot: string;
20
+ cfg: Pick<EngineConfig, "ffmpegProcessTimeout">;
21
+ signal?: AbortSignal;
22
+ /** Where the video's source is read from: a local path or a URL. May throw. */
23
+ sourceOf: (video: ExtractedFrames) => string;
24
+ shownBoxes?: ReadonlyMap<string, ShownBox>;
25
+ /** The error to throw when the extractor returns no frame for an index the video shows. */
26
+ onMissingFrame: (video: ExtractedFrames, index: number) => Error;
27
+ }): Promise<ExtractedFrames[]>;
28
+ /**
29
+ * Fills every deferred video in place with the frames a full extraction writes, into the folder it
30
+ * already has. For a route that cannot read frames per segment.
31
+ */
32
+ export declare function extractAllDeferredFrames(extracted: ExtractedFrames[], input: {
33
+ fps: {
34
+ num: number;
35
+ den: number;
36
+ };
37
+ cfg: Pick<EngineConfig, "ffmpegProcessTimeout">;
38
+ signal?: AbortSignal;
39
+ sourceOf?: (video: ExtractedFrames) => string;
40
+ maxTransientRetries?: number;
41
+ onProgress?: (framesDone: number, framesTotal: number) => void;
42
+ }): Promise<void>;
@@ -200,6 +200,8 @@ export interface RenderCaptureObservability {
200
200
  /** Segmented capture only (Phase 2): current segment and retries so far. */
201
201
  segmentIndex?: number;
202
202
  segmentRetries?: number;
203
+ videoFrameWindow?: "on" | "off";
204
+ videoFrameWindowPeakBytes?: number;
203
205
  }
204
206
  export interface RenderExtractionObservability {
205
207
  videoCount: number;
@@ -0,0 +1,41 @@
1
+ import { createVideoFrameInjector, type BeforeCaptureHook, type EngineConfig, type ExtractedFrames, type VideoElement } from "@hyperframes/engine";
2
+ import { extractDeferredFrames } from "./deferredVideoFrames.js";
3
+ import type { SegmentSlice } from "./segmentPlan.js";
4
+ export interface SegmentWorkerFrames {
5
+ /**
6
+ * One hook for the worker's whole life, so a recycled session keeps it. Inside a
7
+ * prepared window it injects from the window; outside one it extracts the frames
8
+ * it is asked for and removes them again.
9
+ */
10
+ hook: BeforeCaptureHook;
11
+ /** Extracts the frames `segment` shows and points the hook at them. */
12
+ prepare(segment: SegmentSlice): Promise<void>;
13
+ /** Deletes the segment's frames. Safe to call again. */
14
+ release(): void;
15
+ }
16
+ export interface SegmentVideoFrames {
17
+ forWorker(): SegmentWorkerFrames;
18
+ /** Extracts the given output frames into `segment-frames/<name>/` ahead of time; `release()` is idempotent. */
19
+ forFrames(frames: readonly number[], name: string): Promise<{
20
+ injector(): BeforeCaptureHook | null;
21
+ release(): void;
22
+ }>;
23
+ }
24
+ type FrameExtractor = typeof extractDeferredFrames;
25
+ /** Each worker reads only the video frames its current segment shows, and deletes them when the segment closes. */
26
+ export declare function createSegmentVideoFrames(input: {
27
+ videos: VideoElement[];
28
+ extracted: ExtractedFrames[];
29
+ fps: {
30
+ num: number;
31
+ den: number;
32
+ };
33
+ /** The render's work dir; frames go under `segment-frames/segment_NNNNN/`. */
34
+ rootDir: string;
35
+ cfg: EngineConfig;
36
+ injectorOptions: Parameters<typeof createVideoFrameInjector>[1];
37
+ abortSignal?: AbortSignal;
38
+ sourceOf?: (video: ExtractedFrames) => string;
39
+ extractFrames?: FrameExtractor;
40
+ }): SegmentVideoFrames;
41
+ export {};
@@ -3,6 +3,7 @@ import type { FileServerHandle } from "../../fileServer.js";
3
3
  import type { ProducerLogger } from "../../../logger.js";
4
4
  import type { ProgressCallback, RenderJob } from "../../renderOrchestrator.js";
5
5
  import type { SdrSegmentedCapturePlan } from "../capturePlan.js";
6
+ import type { SegmentVideoFrames } from "../segmentVideoFrames.js";
6
7
  import { type StreamingEncoderOptions } from "./captureStreamingStage.js";
7
8
  export interface SegmentedStageDeps {
8
9
  spawnEncoder: typeof spawnStreamingEncoder;
@@ -12,9 +13,9 @@ export interface SegmentedStageDeps {
12
13
  /** Delete a partial segment so a retry cannot leave a stale file behind. */
13
14
  removeFile: (path: string) => void;
14
15
  }
15
- /** Opens a fresh, initialized capture session with the video injector attached. */
16
+ /** Opens a fresh, initialized capture session with the video injector attached; a custom factory may ignore the override. */
16
17
  export interface SessionFactory {
17
- create: () => Promise<CaptureSession>;
18
+ create: (injectorOverride?: () => BeforeCaptureHook | null) => Promise<CaptureSession>;
18
19
  }
19
20
  export interface CaptureSegmentedStageInput {
20
21
  fileServer: FileServerHandle;
@@ -32,6 +33,8 @@ export interface CaptureSegmentedStageInput {
32
33
  streamingEncoderOptions: StreamingEncoderOptions;
33
34
  buildCaptureOptions: () => CaptureOptions;
34
35
  createRenderVideoFrameInjector: () => BeforeCaptureHook | null;
36
+ /** Windowed frames: extracted per segment, deleted when it closes; segment files are deleted once joined. */
37
+ segmentVideoFrames?: SegmentVideoFrames;
35
38
  abortSignal: AbortSignal | undefined;
36
39
  assertNotAborted: () => void;
37
40
  onProgress?: ProgressCallback;
@@ -127,5 +127,12 @@ export type CaptureStreamingStageResult = {
127
127
  /** Spawn failed (non-abort) — sequencer should fall back to the disk path. */
128
128
  success: false;
129
129
  };
130
+ /** At an armed sample index, a frame below the PSNR floor vs its pre-injection screenshot throws. */
131
+ export declare function verifyDrawElementSample(session: CaptureSession, idx: number, buf: Buffer, ctx: {
132
+ log: CaptureStreamingStageInput["log"];
133
+ /** Telemetry counters; the stages that report none pass nothing. */
134
+ stats?: Pick<DeDrainStats, "verifyChecked" | "verifyMinDb">;
135
+ verifyMinDb: number;
136
+ }): Promise<void>;
130
137
  export declare function runCaptureStreamingStage(input: CaptureStreamingStageInput): Promise<CaptureStreamingStageResult>;
131
138
  export {};
@@ -0,0 +1,12 @@
1
+ import type { SegmentVideoFrames } from "./segmentVideoFrames.js";
2
+ /**
3
+ * Whether source-video frames are read one capture segment at a time. Off unless
4
+ * `HF_VIDEO_FRAME_WINDOW` reads `true`, and only for a render that segments, because only
5
+ * the segmented stage knows which frames a segment shows and when it is done with them.
6
+ */
7
+ export declare function shouldWindowVideoFrames(args: {
8
+ env: Readonly<Record<string, string | undefined>>;
9
+ segmented: boolean;
10
+ }): boolean;
11
+ /** Reports the most bytes `segment-frames` has held at once, sampled each time a segment's frames land. */
12
+ export declare function trackSegmentFramePeak(frames: SegmentVideoFrames, rootDir: string, onPeak: (bytes: number) => void): SegmentVideoFrames;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.9.16",
3
+ "version": "0.9.17",
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.11.0",
37
37
  "puppeteer-core": "^25.11.0",
38
- "@hyperframes/lint": "0.9.16",
39
- "@hyperframes/engine": "0.9.16",
40
- "@hyperframes/core": "0.9.16",
41
- "@hyperframes/parsers": "0.9.16",
42
- "@hyperframes/studio-server": "0.9.16"
38
+ "@hyperframes/engine": "0.9.17",
39
+ "@hyperframes/studio-server": "0.9.17",
40
+ "@hyperframes/lint": "0.9.17",
41
+ "@hyperframes/parsers": "0.9.17",
42
+ "@hyperframes/core": "0.9.17"
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.9.16"
51
+ "@hyperframes/aws-lambda": "0.9.17"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"