/**
 * @module _shared/scene-audio-mux
 *
 * Lay every `<audio>` element discovered by `scene-audio-scan.ts` onto a
 * single audio track and mux it into the producer's silent video output.
 *
 * Why this exists: `@hyperframes/producer` renders frames + assembles the
 * MP4 but does not embed audio from sub-composition `<audio>` elements
 * (verified against the upstream Hyperframes reference example). We do the
 * mux ourselves with one ffmpeg pass, copying the producer's video stream
 * untouched so we don't pay a re-encode tax.
 *
 * The filter graph for N audio inputs is:
 *
 *   [1:a]adelay=Sms:all=1,volume=V[a1];
 *   [2:a]adelay=Sms:all=1,volume=V[a2];
 *   ...
 *   [a1][a2]...amix=inputs=N:dropout_transition=0:normalize=0[mixed]
 *
 * Single-input case skips the amix (cheaper and avoids amix's auto-gain).
 *
 * Pure construction lives in `buildAudioMuxFilter`. The async `muxAudioIntoVideo`
 * shells out to ffmpeg via the project's existing `execSafe` wrapper.
 */
import type { SceneAudioElement } from "./scene-audio-scan.js";
export interface AudioMuxFilter {
    /** ffmpeg `-filter_complex` string. */
    filterComplex: string;
    /** Output stream label inside the graph (e.g. `[mixed]` or `[a0]`). */
    outLabel: string;
    /** Number of audio inputs the caller must `-i` before this filter. */
    inputCount: number;
}
/**
 * Build a filter_complex for the given audio elements. Pure — no I/O.
 *
 * Each element becomes one ffmpeg input (input index 1, 2, ... — input 0 is
 * the silent video). `adelay` shifts the audio to its absolute timeline
 * start, `volume` applies per-element gain, and `amix` blends them. When
 * there's only one element we skip `amix` entirely.
 */
export declare function buildAudioMuxFilter(audios: SceneAudioElement[]): AudioMuxFilter | null;
export interface AudioMuxOptions {
    /** Producer's silent video output (will be replaced with the mux'd file). */
    videoPath: string;
    /** Audio elements with absolute timing, from `scanSceneAudio`. */
    audios: SceneAudioElement[];
    /** Final output container — drives the audio codec choice. */
    format: "mp4" | "webm" | "mov";
    /** Cap output to this many seconds via ffmpeg `-t`. Falls back to `videoDuration`. */
    totalDuration?: number;
    /** Producer's video duration (probed by ffprobe). Used as default cap. */
    videoDuration?: number;
    /** Callback for ffmpeg stderr lines (progress / errors). */
    onProgress?: (line: string) => void;
}
export interface AudioMuxResult {
    /** True when ffmpeg succeeded. */
    success: boolean;
    /** Absolute path of the produced file (overwrites the input on success). */
    outputPath: string;
    /** ffmpeg stderr tail when success === false. */
    error?: string;
    /** Number of audio inputs muxed. */
    audioCount: number;
}
/**
 * Run ffmpeg to overlay every `<audio>` element onto the producer's silent
 * video. Replaces the input file on success. Idempotent — re-running on the
 * same project repeats the work but produces an equivalent result.
 *
 * No-op (`success: true, audioCount: 0`) when `audios` is empty.
 */
export declare function muxAudioIntoVideo(opts: AudioMuxOptions): Promise<AudioMuxResult>;
