/** * captureStage — SDR disk-capture path of `executeRenderJob`. * * Handles both branches of the SDR / DOM-only-HDR disk-capture flow: * - `workerCount > 1`: parallel capture with adaptive retry via * `executeDiskCaptureWithAdaptiveRetry`. * - `workerCount === 1`: sequential capture in the orchestrator process, * reusing `probeSession` when available. * * The HDR layered branch (`useLayeredComposite === true`) and the streaming * encode fusion path (`useStreamingEncode === true` with successful encoder * spawn) live in separate stages. * * Hard constraints preserved verbatim: * - `probeSession` is closed (and the local binding nulled) once the * stage no longer needs it. The sequencer's `let probeSession` is * updated via the returned result. * - `captureAttempts` is mutated in place — the parallel path appends * each retry attempt to the array the sequencer owns. * - `workerCount` may be reduced by an adaptive retry; the returned * value reflects the final worker count for the perf summary. * - `lastBrowserConsole` is set to the buffer of whichever session was * active last (probe session in the parallel close path; sequential * session in the sequential path). * - `job.framesRendered` is updated at the same per-frame / per-progress * points; the same `Capturing frame N/M` `updateJobStatus` payloads * fire at 30-frame and completion checkpoints (parallel) or every * frame (sequential). * * Known follow-up: this stage imports `executeDiskCaptureWithAdaptiveRetry` * from `renderOrchestrator.ts`, which itself imports the stage — a runtime * cycle that resolves at module-init time because no stage function is * invoked during load. A subsequent PR will consolidate the capture * helpers (`executeDiskCaptureWithAdaptiveRetry`, `countFrameRanges`, * `safeCleanup`, `sampleDirectoryBytes`, etc.) into a shared module so * the stages can import them without reaching back into the orchestrator. */ import { type BeforeCaptureHook, type CaptureOptions, type CaptureSession, type EngineConfig } from "@hyperframes/engine"; import type { FileServerHandle } from "../../fileServer.js"; import type { ProducerLogger } from "../../../logger.js"; import { type CaptureAttemptSummary, type ProgressCallback, type RenderJob } from "../../renderOrchestrator.js"; export interface CaptureStageInput { fileServer: FileServerHandle; workDir: string; framesDir: string; job: RenderJob; /** * `job.totalFrames` is `number | undefined` in the public type — the * sequencer narrows it to a `number` via the probeStage result before * calling this stage. Passed in explicitly here so the stage doesn't * have to re-narrow on every reference. */ totalFrames: number; cfg: EngineConfig; /** * Capture-mode flag threaded from `compileStage`. The stage derives a * local copy of `cfg` with this value applied to `forceScreenshot` * before any engine call, so the caller-owned `cfg` is never mutated. * The sequencer may override `compileResult.forceScreenshot` after a * BeginFrame calibration timeout — passing the override through this * parameter keeps the decision visible at the call site instead of * hiding it inside a shared mutable config. */ forceScreenshot: boolean; log: ProducerLogger; /** Initial worker count from `resolveRenderWorkerCount`; adaptive retry may reduce it. */ workerCount: number; /** Reused for the sequential path's first session if non-null. */ probeSession: CaptureSession | null; /** True for webm / mov / png-sequence (controls capture format + extension). */ needsAlpha: boolean; /** Mutated in place — each parallel retry attempt is appended. */ captureAttempts: CaptureAttemptSummary[]; buildCaptureOptions: () => CaptureOptions; createRenderVideoFrameInjector: () => BeforeCaptureHook | null; abortSignal: AbortSignal | undefined; assertNotAborted: () => void; onProgress?: ProgressCallback; /** * Capture a sub-range `[startFrame, endFrame)` of the composition's * timeline. Used by distributed `renderChunk` to render only its chunk. * Captured file names are 0-indexed within the range; per-frame TIMES use * the absolute frame index so the page's virtual clock matches an * in-process render at that frame. Supported on both the sequential and * parallel branches; the parallel branch threads `frameRange.startFrame` * through as `frameRangeStart`. See `WorkerTask.outputFrameOffset`. * * Default `undefined`: capture `[0, totalFrames)` (in-process contract). * When set, `endFrame - startFrame` MUST equal `totalFrames`. */ frameRange?: { startFrame: number; endFrame: number; }; } export interface CaptureStageResult { /** Final worker count after any adaptive retry. */ workerCount: number; /** Always `null` after the stage — the probe session is closed before the stage returns. */ probeSession: CaptureSession | null; /** Browser console buffer from whichever session was active last. */ lastBrowserConsole: string[]; } export declare function runCaptureStage(input: CaptureStageInput): Promise; //# sourceMappingURL=captureStage.d.ts.map