export interface VideoColorSpace {
    /** Color transfer characteristics, e.g. "bt709", "smpte2084", "arib-std-b67" */
    colorTransfer: string;
    /** Color primaries, e.g. "bt709", "bt2020" */
    colorPrimaries: string;
    /** Color matrix/space, e.g. "bt709", "bt2020nc" */
    colorSpace: string;
}
export interface VideoMetadata {
    durationSeconds: number;
    videoStreamDurationSeconds: number;
    /** Absolute presentation timestamp at which the selected video stream
     * starts. FFmpeg input seeks are relative to this point, while ffprobe frame
     * timestamps are absolute, so callers crossing those APIs must normalize by
     * this value. Absent only in legacy/manually-constructed metadata. */
    videoStreamStartSeconds?: number;
    width: number;
    height: number;
    fps: number;
    videoCodec: string;
    hasAudio: boolean;
    /** True when r_frame_rate and avg_frame_rate differ significantly (>10%), indicating variable frame rate. */
    isVFR: boolean;
    /** True when the stream carries an alpha channel. */
    hasAlpha: boolean;
    /** Color space info from the video stream. Null if ffprobe didn't report it. */
    colorSpace: VideoColorSpace | null;
}
export interface AudioMetadata {
    durationSeconds: number;
    /** Audio stream's own duration (from `stream.duration`), falling back to
     *  container duration when the stream field is absent. Prefer this over
     *  `durationSeconds` for stream-level parity checks. */
    streamDurationSeconds?: number;
    sampleRate: number;
    channels: number;
    audioCodec: string;
    bitrate?: number;
}
interface StillImageMetadata {
    width: number;
    height: number;
    colorSpace: VideoColorSpace | null;
}
export interface MediaProbeProfile {
    hasVideoStream: boolean;
    hasAudioStream: boolean;
    visualKind: "none" | "still" | "moving";
}
/**
 * Probe stream capabilities without assuming the caller's element type.
 * File extensions and HTTP MIME are deliberately ignored: extensionless
 * assets and valid media served through generic CDN content types must work.
 */
export declare function probeMediaProfile(filePath: string, options?: {
    signal?: AbortSignal;
}): Promise<MediaProbeProfile>;
export declare function extractPngMetadataFromBuffer(buf: Buffer): StillImageMetadata | null;
/**
 * Does this pix_fmt carry an alpha channel?
 *
 * Exported so the test asserts the shipped predicate rather than a copy of
 * the pattern. Anchored at the start, matching studio-server's
 * mediaMetadata.ts: the previous inline pattern bound its `(^|[^a-z])` anchor
 * to the first alternative only — `|` is looser than concatenation — so the
 * guard was decorative for every other name. It also missed abgr, ya8/ya16
 * and ayuv64, and its `gray[a-z0-9]*a` branch matched only gray8a/gray16a,
 * names FFmpeg renamed to ya8/ya16 in 2013. A `ya8` grayscale-plus-alpha PNG
 * reported hasAlpha:false, resolveFrameFormat picked jpg, and the overlay
 * flattened to an opaque rectangle.
 */
export declare function pixelFormatHasAlpha(pixelFormat: string): boolean;
/**
 * Read an ffprobe tag case-insensitively. ffmpeg/libavformat versions disagree
 * on tag casing — VP9 alpha is `alpha_mode` in older builds and `ALPHA_MODE`
 * in newer ones; HDR tags vary similarly. Use this for any sidecar tag where
 * you want to be resilient across muxer versions.
 */
export declare function readTagCI(tags: Record<string, string | undefined> | undefined, name: string): string;
/**
 * Parse an ffprobe rational frame rate ("30000/1001") or plain number.
 *
 * Returns 0 for anything not a usable positive rate. Exported so tests
 * exercise the shipped function directly instead of re-importing the module
 * behind a spawn mock.
 *
 * Every guard here is load-bearing, because a bad value is NOT caught
 * downstream: callers use `meta.fps || 30`, which only rescues 0 and NaN.
 * Infinity and negatives are truthy and flow into buildEncoderArgs as
 * `-r Infinity` / `-r -30`, which ffmpeg rejects mid-render, and into
 * frameCount arithmetic that then goes negative or non-finite.
 *
 *  - the QUOTIENT is checked, not just the operands: "1e308/1e-10" and
 *    "2/1e-320" have finite parts and an infinite result;
 *  - the sign is checked: "-30/1", "30/-1" and "-60" all parsed clean;
 *  - more than two parts is rejected: "30/1/2" used to fall through to the
 *    bare parseFloat below and return 30, as did "60fps", because parseFloat
 *    stops at trailing garbage;
 *  - sub-0.005 rates round to 0 at 2dp and would be replaced by the caller's
 *    30fps default, re-encoding a 300-second 1/300-fps timelapse as a
 *    ~1/30-second clip. Kept as 0 is wrong too, so they are floored to the
 *    smallest representable 2dp rate instead.
 */
export declare function parseFrameRate(frameRateStr: string | undefined): number;
/**
 * Probe a media file (video, image, or container) and return normalized metadata.
 *
 * Despite the legacy name `extractVideoMetadata` (still exported as a
 * deprecated alias below), this also handles still images such as PNG so it
 * can be used uniformly for any visual asset the HDR pipeline encounters.
 */
export declare function extractMediaMetadata(filePath: string): Promise<VideoMetadata>;
/**
 * Return the FFmpeg input-seek position of the final decoded video frame.
 *
 * A fixed seek window near EOF is not sufficient: sub-1fps and sparse VFR
 * sources can have no frame timestamp inside that window even though the last
 * decoded frame remains displayed through the stream duration. ffprobe seeks
 * to the preceding keyframe and walks forward; retaining only its stdout tail
 * keeps memory bounded even for a pathological long GOP. ffprobe reports
 * absolute presentation timestamps, but FFmpeg input `-ss` is relative to the
 * stream start; the result is normalized into that relative seek domain. Some
 * unindexed transports cannot decode after an interval seek, so an empty tail
 * probe falls back to a bounded-output full scan rather than rejecting valid
 * media. The scan may cost decode time, but retains only 64 KiB of timestamps.
 */
export declare function extractFinalVideoFrameTimestamp(filePath: string, metadata: Pick<VideoMetadata, "videoStreamDurationSeconds" | "videoStreamStartSeconds">, signal?: AbortSignal): Promise<number>;
/**
 * @deprecated Use `extractMediaMetadata` — this name is kept for backward
 * compatibility with consumers that imported the original video-only name
 * before still-image (PNG) support was added. New callers should prefer
 * `extractMediaMetadata`.
 */
export declare const extractVideoMetadata: typeof extractMediaMetadata;
export declare function extractAudioMetadata(filePath: string, options?: {
    signal?: AbortSignal;
}): Promise<AudioMetadata>;
export interface KeyframeAnalysis {
    avgIntervalSeconds: number;
    maxIntervalSeconds: number;
    keyframeCount: number;
    isProblematic: boolean;
}
/**
 * Check keyframe intervals in a video file. Intervals > 2s cause seeking
 * issues in the headless renderer and audio/video desync. Videos from
 * yt-dlp --download-sections or screen recordings often have sparse keyframes.
 */
export declare function analyzeKeyframeIntervals(filePath: string): Promise<KeyframeAnalysis>;
export {};
//# sourceMappingURL=ffprobe.d.ts.map