/**
 * Activity B of the distributed render pipeline.
 *
 * `renderChunk(planDir, chunkIndex, outputChunkPath)` validates the planDir
 * against the worker's environment, captures the chunk's frame range, and
 * encodes a single closed-GOP video chunk (or, for png-sequence, a directory
 * of PNGs). The output is byte-identical across retries on the same worker
 * and PSNR-equivalent across workers — that contract is what makes Temporal
 * activity retries safe.
 *
 * Pure function over local paths. No networking. Spins up its own headless
 * Chrome + file server scoped to the chunk; tears them down before
 * returning. The caller is responsible for moving `outputChunkPath` to its
 * orchestration-level storage (S3 / GCS / EFS / …).
 *
 * Hard contracts:
 *   - The worker re-applies `meta/encoder.json.runtimeEnv` into
 *     `process.env` BEFORE the file server starts so the served HTML's
 *     `RENDER_MODE_SCRIPT` sees the same env it would have seen on the
 *     controller.
 *   - Browser is launched with `browserGpuMode: "software"` and verified
 *     against `chrome://gpu` via `assertSwiftShader` — a non-SwiftShader
 *     backend trips a non-retryable `BROWSER_GPU_NOT_SOFTWARE`.
 *   - The file server serves with the seeded-random shim
 *     (`buildVirtualTimeShim({ seedRandomFromFrame: true })`) so any
 *     composition that uses `Math.random` / `crypto.getRandomValues`
 *     produces byte-identical pixels per `(planDir, chunkIndex)`.
 *   - No `lastFrameCache` priming: every frame seeks fresh DOM so the
 *     cache is never read, and priming would deadlock the compositor.
 *   - The chunk's encode runs with `lockGopForChunkConcat: true` and
 *     `gopSize === framesInChunk` so concat-copy at assemble time is safe.
 *
 * Every determinism toggle above is opt-in — only this primitive enables them.
 * In-process renders (`executeRenderJob`) leave them off.
 */
import { assertSwiftShader, type BeforeCaptureHook, BROWSER_GPU_NOT_SOFTWARE, type CaptureOptions, type CaptureMode, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, type ExtractedFrames, type FrameLookupTable, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
import { type LockedRenderConfig } from "../render/stages/freezePlan.js";
import { INVALID_VIDEO_METADATA, type PlanVideosJson } from "./shared.js";
/**
 * Non-retryable error codes raised when the planDir is structurally
 * malformed, semantically out of range, or fingerprints differently from
 * what the controller wrote. Each is distinct so adapter retry policies
 * can route them independently — e.g. `MISSING_PLAN_ARTIFACT` may point
 * to a partial S3 download that a retry could heal, while
 * `PLAN_HASH_MISMATCH` strictly indicates cross-version drift that
 * retries won't fix.
 */
export declare const FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
export declare const PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
export declare const MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
export declare const CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
export declare const MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
export { INVALID_VIDEO_METADATA };
export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
/**
 * Typed non-retryable error raised by `renderChunk` when the planDir is
 * malformed or the worker's runtime doesn't match the planDir's
 * controller-side fingerprint. Workflow adapters key retry policies off
 * `code` — most of these failures will not heal on retry.
 */
export declare class RenderChunkValidationError extends Error {
    readonly code: RenderChunkValidationCode;
    constructor(code: RenderChunkValidationCode, message: string);
}
/** Validate the shared video contract before any v1 chunk can inject frames. */
export declare function validatePlanVideosForChunk(value: unknown): PlanVideosJson;
/**
 * Result of {@link renderChunk}. The `sha256` field is the byte hash of the
 * primary output (the mp4/mov file, or, for png-sequence, the sorted-frame
 * fingerprint). Retries on the same `(planDir, chunkIndex)` MUST produce
 * the same `sha256` — that contract is the byte-identical-retry axis.
 */
export interface ChunkResult {
    /** Absolute path the encoded chunk was written to (file or directory). */
    outputPath: string;
    /** `"file"` for mp4/mov; `"frame-dir"` for png-sequence. */
    outputKind: "file" | "frame-dir";
    framesEncoded: number;
    sha256: string;
    durationMs: number;
    /**
     * Stage wall-clock split of `durationMs`, for separating per-chunk fixed
     * overhead from frame-proportional work in fleet cost models:
     *
     * - `planHashMs` — full planDir content-hash recomputation (validation).
     * - `sessionBootMs` — Chrome boot + SwiftShader assert + composition warmup
     *   for the reusable sequential session or parallel BeginFrame preflight.
     * - `captureStageMs` — the capture stage call; includes per-worker session
     *   boots in the parallel branch.
     * - `encodeStageMs` — the encode stage call (single ffmpeg invocation, or
     *   the frame-dir arrangement for png-sequence).
     *
     * The remainder of `durationMs` is validation + file-server setup + output
     * hashing + cleanup.
     */
    planHashMs: number;
    sessionBootMs: number;
    captureStageMs: number;
    encodeStageMs: number;
    /** Capture workers used for this chunk (`calculateOptimalWorkers` result). */
    workers: number;
    /**
     * Effective engine mode used by every worker, after any browser fallback.
     *
     * Current first-party renderers always emit this field. It remains optional
     * so adapters can accept results from older or injected chunk renderers
     * without inventing an observed mode that may be false.
     */
    captureMode?: CaptureMode;
    /**
     * Path to a sidecar JSON containing per-chunk perf counters. Adapters
     * upload this alongside the chunk so per-chunk regressions are
     * inspectable without the workflow having to carry the payload.
     */
    perfPath: string;
}
/** Result returned by the built-in renderer, which always observes its mode. */
export interface EffectiveChunkResult extends ChunkResult {
    captureMode: CaptureMode;
}
/** Compatibility-safe adapter seam for built-in or injected chunk renderers. */
export type ChunkRenderer = (planDir: string, chunkIndex: number, outputChunkPath: string) => Promise<ChunkResult>;
interface DistributedCaptureSessionDependencies {
    createCaptureSession: typeof createCaptureSession;
    assertSwiftShader: typeof assertSwiftShader;
    initializeSession: typeof initializeSession;
    closeCaptureSession: typeof closeCaptureSession;
    readWebGlVendorInfo: typeof readWebGlVendorInfoFromCanvas;
}
/**
 * Every browser that can produce distributed frames must pass the software-GL
 * assertion, including fresh screenshot browsers created after a fallback.
 */
export declare function createVerifiedDistributedCaptureSession(serverUrl: string, framesDir: string, captureOptions: CaptureOptions, cfg: EngineConfig, dependencies?: DistributedCaptureSessionDependencies): Promise<CaptureSession>;
/**
 * Build the immutable lookup table once, but keep injector state scoped to a
 * browser session. Reusing one hook across workers or a whole-chunk retry can
 * incorrectly suppress injection on a fresh page.
 */
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | null): () => BeforeCaptureHook | null;
/**
 * Only BeginFrame-specific failures are safe to retry in screenshot mode.
 * Cancellation, memory exhaustion, and unrelated authoring/IO failures must
 * keep their original classification instead of being hidden by a fallback.
 */
export declare function shouldRetryChunkCaptureWithScreenshot(error: unknown): boolean;
/**
 * Execute capture with at most one whole-chunk screenshot retry. The caller's
 * reset hook must discard every partial frame and perf record before retrying.
 */
export declare function runCaptureWithScreenshotFallback<T>(input: {
    forceScreenshot: boolean;
    run: (forceScreenshot: boolean) => Promise<T>;
    resetForScreenshotRetry: () => Promise<void> | void;
    onFallback?: (error: unknown) => void;
}): Promise<T>;
/**
 * Probe an initialized distributed session at a monotonic tick between warmup
 * and frame zero. `true` means the entire chunk should use screenshot mode.
 */
export declare function beginFrameSessionNeedsScreenshotFallback(session: Pick<CaptureSession, "page" | "launchCaptureMode" | "beginFrameTimeTicks" | "beginFrameIntervalMs">, probe?: typeof probeBeginFrameLiveness): Promise<boolean>;
/**
 * Rebuild the engine's in-memory `ExtractedFrames[]` from the on-disk
 * planDir layout. `<planDir>/video-frames/<videoId>/` holds the numbered
 * frame files plan() extracted; this lists each dir and rebuilds the
 * 0-based `framePaths` Map that `FrameLookupTable` / `videoFrameInjector`
 * both index against — the consumer is
 * `videoFrameExtractor.ts:getFrameAtTime`, which floors `localTime * fps`
 * to a 0-based index and reads `framePaths.get(frameIndex)`. Any drift
 * from that key convention silently drops every `<video>`'s first-paint
 * frame; see HF#1731 / HF#1730.
 *
 * Exported so a unit test can pin the 0-based contract without spinning
 * up the heavyweight Docker fixture — the bug surfaces only under
 * distributed mode and only at video first-paint, so this primitive is
 * the right granularity to guard.
 */
export declare function rebuildExtractedFramesFromPlanDir(planDir: string, videos: PlanVideosJson["extracted"], indexMode?: "dense-v1" | "sparse-v2"): ExtractedFrames[];
/**
 * Re-export the runtime-env apply helper so adapters that import only
 * this subpath can prime `process.env` before instantiating their own
 * file server. Returns a `{ restore }` handle — adapters that fan out
 * multiple chunks per process MUST call `restore()` between chunks.
 */
export { applyRuntimeEnvSnapshot } from "../render/runtimeEnvSnapshot.js";
export { readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
/**
 * Apply the planDir's locked-encoder choice on top of an
 * `EncoderPreset` from `getEncoderPreset`. `getEncoderPreset` returns
 * h265 only on the HDR branch, but distributed mode is SDR-only — for
 * an `libx265-software` planDir we still need to flip the preset's
 * codec to h265 so `runEncodeStage` invokes libx265. Exported so a
 * unit test can pin the override independently of the heavyweight
 * Docker fixture: a refactor that moves the override (e.g. into
 * `getEncoderPreset` itself) shouldn't be able to silently regress
 * the contract without a fast-test signal.
 */
export declare function resolvePresetForLockedEncoder<P extends {
    codec: "h264" | "h265" | "vp9" | "prores";
}>(basePreset: P, lockedEncoder: LockedRenderConfig["encoder"]): P;
export declare function resolveLockedVp9CpuUsed(lockedEncoder: Pick<LockedRenderConfig, "encoder" | "vp9CpuUsed">): number | undefined;
/**
 * Activity B: render a single chunk of the planDir. The `outputChunkPath`
 * argument is a file for mp4/mov outputs and a directory for png-sequence
 * outputs — the caller picks the right shape based on `meta/encoder.json`.
 * `renderChunk` enforces the same choice via `outputKind` on the result.
 */
export declare function renderChunk(planDir: string, chunkIndex: number, outputChunkPath: string): Promise<EffectiveChunkResult>;
//# sourceMappingURL=renderChunk.d.ts.map