/**
 * Video Frame Injector
 *
 * Creates a BeforeCaptureHook that replaces native <video> elements with
 * pre-extracted frame images during rendering. This is the Hyperframes-specific
 * video handling strategy — OSS users with different video pipelines can
 * provide their own hook or skip video injection entirely.
 */
import { type Page } from "puppeteer-core";
import { type FrameLookupTable } from "./videoFrameExtractor.js";
import { type BeforeCaptureHook } from "./frameCapture.js";
import { type EngineConfig } from "../config.js";
export interface VideoFrameInjectorOptions extends Partial<Pick<EngineConfig, "frameDataUriCacheLimit" | "frameDataUriCacheBytesLimitMb">> {
    frameSrcResolver?: (framePath: string) => string | null;
}
interface FrameSourceCacheStats {
    entries: number;
    bytes: number;
    /** Total entries evicted since cache creation. A high count vs a small
     * composition signals the byte budget is too tight (cache thrash). */
    evictions: number;
    /** Total inserts rejected because the entry alone exceeds bytesLimit.
     * Non-zero means a single frame is bigger than the configured budget —
     * raise `frameDataUriCacheBytesLimitMb` if it recurs in production. */
    oversizedRejections: number;
}
interface FrameSourceCache {
    get: (framePath: string) => Promise<string>;
    /** Exposed for tests + telemetry; reflects current cache occupancy. */
    stats: () => FrameSourceCacheStats;
}
/**
 * Two-bound LRU keyed by frame path. Either bound triggers eviction of the
 * oldest entry — entry count protects against pathological many-tiny-frames
 * cases, and the byte budget keeps memory bounded when the per-frame data
 * URI grows (4K PNG frames are ~33 MB once base64-encoded).
 *
 * If a single entry's data URI exceeds `bytesLimit`, we skip caching it
 * (returning the URI directly to the caller). Without this guard, the
 * post-insert eviction loop would drop the entry we just inserted and the
 * cache would degrade into a CPU hot path — every subsequent `get()` would
 * re-read from disk and re-base64 the same frame.
 *
 * **Invariant**: cached values MUST be strings whose `.length` equals the
 * byte count we account for at insertion. We derive size on demand via
 * `cache.get(key)?.length` rather than maintaining a parallel `Map<string, number>`.
 * If you ever wrap the value (e.g. cache a Buffer or an object), the byte
 * accounting silently breaks — switch to a parallel size map first.
 */
declare function createFrameSourceCache(entryLimit: number, bytesLimit: number, frameSrcResolver?: (framePath: string) => string | null): FrameSourceCache;
export declare const __testing: {
    createFrameSourceCache: typeof createFrameSourceCache;
};
/**
 * Creates a BeforeCaptureHook that injects pre-extracted video frames
 * into the page, replacing native <video> elements with frame images.
 */
export declare function createVideoFrameInjector(frameLookup: FrameLookupTable | null, config?: VideoFrameInjectorOptions): BeforeCaptureHook | null;
/**
 * Bounds and transform of a video element, queried from Chrome each frame.
 * Used by the two-pass HDR compositing pipeline to position native HDR frames.
 */
export interface VideoElementBounds {
    videoId: string;
    x: number;
    y: number;
    width: number;
    height: number;
    opacity: number;
    /** CSS transform matrix as a DOMMatrix-compatible string, e.g. "matrix(1,0,0,1,0,0)" */
    transform: string;
    zIndex: number;
    visible: boolean;
}
/**
 * Hide specific video elements by ID. Used in Pass 1 of the HDR pipeline so
 * Chrome screenshots only contain DOM content (text, overlays) with transparent
 * holes where the HDR videos go.
 */
export declare function hideVideoElements(page: Page, videoIds: string[]): Promise<void>;
/**
 * Restore visibility of video elements after a DOM screenshot.
 */
export declare function showVideoElements(page: Page, videoIds: string[]): Promise<void>;
/**
 * Query the current bounds, transform, and visibility of video elements.
 * Called after seeking (so GSAP has moved things) but before the screenshot.
 */
export declare function queryVideoElementBounds(page: Page, videoIds: string[]): Promise<VideoElementBounds[]>;
/**
 * Stacking info for a single timed element, used by the z-ordered layer compositor.
 */
export interface ElementStackingInfo {
    id: string;
    zIndex: number;
    x: number;
    y: number;
    width: number;
    height: number;
    /** Layout dimensions before CSS transforms (offsetWidth/offsetHeight). */
    layoutWidth: number;
    layoutHeight: number;
    opacity: number;
    visible: boolean;
    /**
     * True when the SDR video replacement image injected beside this element is
     * currently paintable. Native videos are hidden during capture, so this lets
     * the layered HDR compositor keep their replacement frames in the right DOM
     * layer without reviving unrelated hidden elements.
     */
    renderFrameVisible: boolean;
    isHdr: boolean;
    transform: string;
    borderRadius: [number, number, number, number];
    /**
     * CSS `object-fit` value for replaced elements (`<img>`, `<video>`).
     * One of: `fill` (default), `cover`, `contain`, `none`, `scale-down`.
     * The HDR compositor uses this to resample image/video buffers into the
     * element's layout box the same way the browser would.
     */
    objectFit: string;
    /**
     * CSS `object-position` value (e.g. `"50% 50%"`, `"center top"`).
     * Falls back to the CSS default `"50% 50%"` (center) when unset.
     */
    objectPosition: string;
    /**
     * Clip rect from the nearest ancestor with `overflow: hidden` (or
     * `clip`/`clip-path`). When set, the HDR compositor must scissor the
     * element's blit to this viewport-relative rectangle. `null` means no
     * clipping ancestor was found — render at full element bounds.
     */
    clipRect: {
        x: number;
        y: number;
        width: number;
        height: number;
    } | null;
}
/**
 * Query Chrome for ALL timed elements' stacking context.
 * Returns z-index, bounds, opacity, and whether each element is a native HDR source.
 *
 * Queries every element with `data-start` (not just videos) so the layer compositor
 * can determine z-ordering between DOM content and HDR video/image elements.
 *
 * @param nativeHdrIds Combined set of HDR-tagged element IDs (videos AND images).
 */
export declare function queryElementStacking(page: Page, nativeHdrIds: Set<string>): Promise<ElementStackingInfo[]>;
export {};
//# sourceMappingURL=videoFrameInjector.d.ts.map