import { type VolumeKeyframe } from "./mediaVolumeEnvelope.js";
export declare function readElementPlaybackRate(el: Element): number;
export declare function readElementPlaybackStart(el: Element): number;
/**
 * Resolve a media element's timeline window without conflating a video's
 * authored display slot with the amount of source left to decode.
 *
 * An explicit video slot may outlive its source and holds the final frame.
 * Audio remains source-bounded because it has no visual hold state.
 */
export declare function resolveRuntimeMediaClipDuration(params: {
    isVideo: boolean;
    sourceDuration: number | null;
    hostRemaining: number | null;
    explicitDuration: number | null;
}): number | null;
export type RuntimeMediaClip = {
    el: HTMLVideoElement | HTMLAudioElement;
    start: number;
    mediaStart: number;
    duration: number;
    end: number;
    volume: number | null;
    playbackRate: number;
    loop: boolean;
    /** Source media duration in seconds (from el.duration). Used for loop wrapping. */
    sourceDuration: number | null;
    /**
     * Probed volume keyframes from the GSAP timeline (same probe the renderer
     * uses). When present, `syncRuntimeMedia` drives volume from the envelope
     * rather than from `data-volume` + GSAP-change tracking, eliminating the
     * race between the 60 Hz transport tick and GSAP's own seek.
     */
    volumeKeyframes?: VolumeKeyframe[];
};
export declare function refreshRuntimeMediaCache(params?: {
    resolveStartSeconds?: (element: Element) => number;
    resolveDurationSeconds?: (element: HTMLVideoElement | HTMLAudioElement) => number | null;
    shouldIncludeElement?: (element: HTMLVideoElement | HTMLAudioElement) => boolean;
}): {
    timedMediaEls: Array<HTMLVideoElement | HTMLAudioElement>;
    mediaClips: RuntimeMediaClip[];
    videoClips: RuntimeMediaClip[];
    maxMediaEnd: number;
};
/**
 * Drop every per-source sync baseline tracked for `el` — offset drift
 * samples, the seek-past-buffered-range retry latch, and the last
 * runtime-applied volume — so the next `syncRuntimeMedia` tick treats it as
 * a first tick (hard resync, fresh drift baseline) instead of comparing
 * against state computed for a different file. Used both when a clip leaves
 * its active window (below) and by the runtime's proxy-swap helper
 * (mediaProxy.ts) right after an in-place `src` swap, which points the same
 * element at a different file without ever leaving its active window.
 */
export declare function evictMediaSyncState(el: HTMLMediaElement): void;
/** Test-only seam: whether any per-source sync state is still tracked for `el`. */
export declare function hasMediaSyncStateForTest(el: HTMLMediaElement): boolean;
export declare function syncRuntimeMedia(params: {
    clips: RuntimeMediaClip[];
    timeSeconds: number;
    playing: boolean;
    playbackRate: number;
    /** Force-mute every element (parent-frame proxy owns all audio). Asserted per
     *  tick so sub-composition media added mid-playback inherits the silence. */
    outputMuted?: boolean;
    /**
     * User's explicit mute preference (set via `onSetMuted`). Symmetric to
     * `outputMuted` — also asserted per tick — so a sub-composition that
     * activates after the user mutes doesn't briefly play at author volume
     * before the next bridge message lands.
     */
    userMuted?: boolean;
    /**
     * User's volume preference (0–1, set via `onSetVolume`). Multiplied with the
     * per-clip author volume so `data-volume="0.5"` at user volume 0.8 yields 0.4.
     */
    userVolume?: number;
    /**
     * Invoked at most once when a media element's `play()` promise rejects with
     * `NotAllowedError`. The caller is expected to latch and post a single
     * outbound message; further invocations are suppressed by the caller.
     */
    onAutoplayBlocked?: () => void;
    onElementVolume?: (el: HTMLMediaElement, volume: number) => void;
    /** Is THIS element owned by the Web Audio transport? Owned → mute it (transport
     *  plays it); not owned → leave audible (HTMLMedia fallback). Per-element, not a
     *  global flag, so a not-yet-claimed track isn't muted by other tracks. */
    isWebAudioOwned?: (el: HTMLMediaElement) => boolean;
    forceSync?: boolean;
}): void;
//# sourceMappingURL=media.d.ts.map