export type CaptureCodec = 'avc' | 'vp9'; export declare const CHUNK_HEADER_BYTES = 5; export declare const CAPTURE_FPS = 60; /** Where ffmpeg drops the extracted PNGs before they are renamed onto the recorder's frame indices. */ export declare const EXTRACT_DIR = ".extract"; export declare function isCaptureCodec(value: unknown): value is CaptureCodec; /** The elementary stream written during the recording. */ export declare function elementaryName(codec: CaptureCodec): string; /** The playable file kept next to the frames. */ export declare function containerName(codec: CaptureCodec): string; export interface ChunkMessage { frameIndex: number; key: boolean; bytes: Buffer; } /** Split a binary message into its header and chunk bytes; null when it cannot be one. */ export declare function parseChunkMessage(data: Buffer): ChunkMessage | null; /** * Which frame indices arrived. Chrome's encoders emit one chunk per frame in input order, so a * gap here is a frame the recorder itself reported as failed; `finalizeFrames` fills it from a * neighbour after extraction, the way `FrameSequencer` does for raw frames. */ export declare class ChunkLedger { private next; readonly present: number[]; readonly missing: number[]; /** Whether this chunk is new (a late or duplicate index is ignored). */ accept(frameIndex: number): boolean; /** Mark the tail up to the recorder's declared count as missing. */ finish(frameCount: number): void; get received(): number; /** One past the highest index seen (or declared). */ get expected(): number; } /** The 32-byte IVF file header (VP9, timebase 1/60). */ export declare function ivfHeader(width: number, height: number, frameCount: number): Buffer; /** Appends chunks to the elementary stream as they arrive; closes with the IVF count patched in. */ export declare class CaptureWriter { readonly codec: CaptureCodec; private readonly width; private readonly height; readonly filePath: string; private readonly fd; private frames; private closed; constructor(codec: CaptureCodec, dir: string, width: number, height: number); append(frameIndex: number, bytes: Buffer): void; close(): void; } /** * `-c copy` into a container. The raw H.264 demuxer would otherwise take its timing from the * encoder's VUI (Chrome's software encoder writes a 120 Hz tick), so the frame rate is forced on * the input and the timestamps regenerated. */ export declare function remuxArgs(codec: CaptureCodec, dir: string): string[]; /** * One PNG per decoded frame, in decode order, into `.extract/`. `-vsync 0` (the spelling every * ffmpeg since 4.x accepts) stops ffmpeg from duplicating or dropping frames to hit a rate. */ export declare function extractArgs(codec: CaptureCodec, dir: string): string[]; /** * Rename the extracted PNGs onto the frame indices the ledger says they belong to, then fill * every hole up to the declared count by copying the previous frame (or the first one, for a * leading gap) so `_%06d.png` stays aligned with the recorder's frame clock. Returns the * same `{written, missing}` the raw path reports. */ export declare function finalizeFrames(dir: string, recordingName: string, ledger: ChunkLedger, frameCount: number): { written: number; missing: number[]; };