/**
 * audioPadTrim — pad-or-trim the mixed-audio file so its exact duration
 * matches the assembled video's frame count divided by fps.
 *
 * Distributed render assemble step needs this because:
 *   - The plan's audio is mixed once against the composition's *declared*
 *     duration.
 *   - The actual video produced by concatenating per-chunk encodes is the
 *     sum of per-chunk frame counts. With closed-GOP concat-copy this is
 *     deterministic and equals the planned frame count, BUT downstream
 *     muxers (ffmpeg `-shortest` plus Apple's mov demuxer in particular)
 *     are sensitive to ±1ms audio/video duration drift and produce silent
 *     "audio cuts off early" or "video shows a frozen final frame" bugs.
 *
 * The fix: post-pad/trim audio to *exactly* `frameCount / fps` seconds at
 * assemble time. Both branches decode/filter and re-encode AAC. Concatenating
 * ADTS packets with `-c:a copy` is unsafe because concat timestamp estimation
 * can stretch the terminal packet in the final MP4.
 */
export interface ProbeVideoFrameInfo {
    /** Number of video frames in the stream. */
    frameCount: number;
    /** Numerator of the frame rate fraction (e.g. 30 or 30000). */
    fpsNum: number;
    /** Denominator of the frame rate fraction (e.g. 1 or 1001). */
    fpsDen: number;
}
export interface AudioProbeInfo {
    /** Decoded duration in seconds. */
    durationSeconds: number;
    /** Audio sample rate in Hz. Used when generating pad silence. */
    sampleRate?: number;
    /** Audio channel count. Used when generating pad silence. */
    channels?: number;
    /** Codec name reported by ffprobe. */
    audioCodec?: string;
}
export interface PadTrimAudioInput {
    /** Path to the assembled video. Used to derive `frameCount / fps`. */
    videoPath: string;
    /** Path to the pre-mixed audio (typically `<planDir>/audio.m4a`). */
    audioPath: string;
    /** Path the helper writes the duration-corrected audio to. */
    outputPath: string;
    signal?: AbortSignal;
    /**
     * Optional injectables for unit tests. Production callers omit them and
     * get the real `ffprobe`/`ffmpeg`-backed implementations.
     */
    probeVideoFrameInfo?: (videoPath: string) => Promise<ProbeVideoFrameInfo>;
    probeAudioInfo?: (audioPath: string, signal?: AbortSignal) => Promise<AudioProbeInfo>;
    runFfmpeg?: (args: string[]) => Promise<{
        success: boolean;
        error?: string;
    }>;
}
export type PadTrimOperation = "pad" | "trim" | "copy";
export interface PadTrimAudioResult {
    success: boolean;
    outputPath: string;
    /** `frameCount / fps` to ~nanosecond precision. */
    targetDurationSeconds: number;
    /** Probed duration of the input audio. */
    sourceDurationSeconds: number;
    /** How the duration was corrected. */
    operation: PadTrimOperation;
    /** Populated only when `success === false`. */
    error?: string;
}
export type PadTrimAudioStepKind = "copy" | "trim" | "normalize";
export interface PadTrimAudioStep {
    kind: PadTrimAudioStepKind;
    args: string[];
}
export interface PadTrimAudioPlan {
    operation: PadTrimOperation;
    steps: PadTrimAudioStep[];
    cleanupPaths: string[];
}
/**
 * Pure helper: decide the pad/trim operation and build the ffmpeg argv
 * sequence that materializes it. Exported separately so unit tests can pin
 * every branch without spawning ffmpeg.
 *
 *   - `sourceDuration < targetDuration` → pad with `apad` to the exact target
 *     and re-encode AAC. This decodes and re-encodes the already mixed audio;
 *     an earlier concat-copy shape avoided that but could not produce a
 *     portable result on the bundled Windows FFmpeg builds.
 *   - `sourceDuration > targetDuration` → filter to the exact target and
 *     re-encode AAC so packet padding cannot outlast the video.
 *   - `|Δ| < AUDIO_DURATION_TOLERANCE_SECONDS` → no-op `copy`, but we still
 *     run ffmpeg with `-c:a copy` to materialize the output path.
 */
export declare function buildPadTrimAudioPlan(audioPath: string, outputPath: string, sourceDurationSeconds: number, targetDurationSeconds: number): PadTrimAudioPlan;
export declare function buildPadTrimAudioArgs(audioPath: string, outputPath: string, sourceDurationSeconds: number, targetDurationSeconds: number): {
    args: string[];
    operation: PadTrimOperation;
};
/**
 * Pad or trim the mixed audio so its exact duration matches `frameCount / fps`
 * for the assembled video.
 */
export declare function padOrTrimAudioToVideoFrameCount(input: PadTrimAudioInput): Promise<PadTrimAudioResult>;
//# sourceMappingURL=audioPadTrim.d.ts.map