import type { AFEncodedSource, AFEncodedStream } from './AFEncodedSource.js'; import { MEDIA_DECODER_COORDINATOR_LEASE, MEDIA_DECODER_COORDINATOR_REQUEST } from './MediaDecoderCoordinator.js'; import type { AFFrame, AFManifest } from './AFContainer.js'; import type { MediaDecoderLease, MediaDecoderLeaseRequest } from './MediaDecoderCoordinator.js'; /** A fully loaded container or the progressively assembled manifest consumed by the decoder. */ export type AFDecoderSource = string | ArrayBuffer | { manifest: AFManifest; } | AFEncodedSource; /** Owns a decoded frame when it is selected by the current request. */ export type AFDecoderProcess = (frame: VideoFrame, id: number) => void | Promise; /** Fatal initialization or WebCodecs failure callback. */ export type AFDecoderErrorCallback = (error: unknown) => void; /** Construction options for {@link AFDecoder}. */ export interface AFDecoderOptions { process?: AFDecoderProcess; onError?: AFDecoderErrorCallback; hardwareAcceleration?: HardwareAcceleration; /** @internal Whether the candidate ladder may relax to the browser's default backend. */ allowNoPreferenceFallback?: boolean; colorSpace?: VideoColorSpaceInit | null; /** Wall-clock milliseconds allowed for the first requested keyframe output. */ firstDecodeTimeout?: number; /** * Throughput profile for continuous forward playback. Prefers codec configs WITHOUT * `optimizeForLatency` (software decoders then frame-thread across cores — measured 6× on * 5-megapixel lossless frames) and enables {@link AFDecoder#streamFrame} / * {@link AFDecoder#flushTail}. The pipelined codec withholds outputs until ~a thread-pool * of further input arrives, so callers must either keep feeding ahead (streamFrame) or * flush the tail. Leave off for scrub-first consumers: latency mode emits every decode * immediately. */ streaming?: boolean; /** @internal Indexed stream selected when `source` implements AFEncodedSource. */ encodedStream?: AFEncodedStream; /** @internal Product-level identity and optional lifecycle callback for standalone admission. */ [MEDIA_DECODER_COORDINATOR_REQUEST]?: MediaDecoderLeaseRequest; /** @internal Logical member supplied by an atomic media-decoder group transaction. */ [MEDIA_DECODER_COORDINATOR_LEASE]?: MediaDecoderLease; } /** Per-request behavior for a bounded random-access decode. */ export interface AFDecoderRequestOptions { /** Drain a streaming decoder after the requested chunks are submitted. */ flushTail?: boolean; } /** Stage reached by one WebCodecs configuration candidate. */ export type AFDecoderConfigAttemptStep = 'advisory' | 'configure' | 'first-decode' | 'watchdog' | 'accepted'; /** * Immutable diagnostic for one WebCodecs configuration candidate. A `configure` attempt without * an error is provisional; it changes to `accepted` only after the requested keyframe outputs. */ export interface AFDecoderConfigAttempt { readonly config: Readonly; readonly advisorySupported: boolean; readonly step: AFDecoderConfigAttemptStep; readonly error?: unknown; } /** Stable low-level failure codes mapped onto the public Baked Motion error contract by owners. */ export type AFDecoderErrorCode = 'webcodecs-unavailable' | 'decoder-unsupported' | 'decoder-stalled'; /** Monotonic codec-activity counters exposed for playback diagnostics. */ export interface AFDecoderCounters { /** Codec resets issued after admission (each one abandons queued work). */ readonly resets: number; /** Forward-run extensions: requests appended to the live codec without a reset. */ readonly forwardExtensions: number; /** Decoded frames that emerged from the codec, including GOP-walk frames. */ readonly decodedFrames: number; } /** Typed decoder admission failure with a frozen point-in-time attempt history. */ export declare class AFDecoderError extends Error { readonly code: AFDecoderErrorCode; readonly step: AFDecoderConfigAttemptStep; readonly configAttempts: readonly AFDecoderConfigAttempt[]; readonly cause?: unknown; constructor(code: AFDecoderErrorCode, message: string, step: AFDecoderConfigAttemptStep, configAttempts: readonly AFDecoderConfigAttempt[], options?: { cause?: unknown; }); } /** Promise with the resolver pair retained for the decoder's asynchronous initialization. */ export interface AFDecoderDeferred extends Promise { resolve(value: TValue | PromiseLike): void; reject(reason?: unknown): void; } type AFDecoderFirstDecodeState = 'idle' | 'pending' | 'accepted' | 'failed' | 'disposed'; /** @internal Shared by decoder admission and Baked Motion's no-media support preflight. */ export declare function afDecoderConfigCandidates(manifest: Pick, options?: Pick): VideoDecoderConfig[]; /** * Frame-accurate WebCodecs decoder for a single `.af` clip. * * Ownership: a delivered ("wanted") frame is handed to `process` and is **not** closed by * the decoder — the callback takes ownership and must `close()` it when done (the default * `process` closes immediately; {@link AFVideo} instead holds it until the slot advances so * the GPU can blit it on the next render). Frames decoded only to walk a GOP forward to a * wanted frame are always closed by the decoder. * * @class AFDecoder * @short Frame-accurate random-access `.af` decoder (WebCodecs). * @category Video */ export declare class AFDecoder { source: AFDecoderSource; process: AFDecoderProcess | null; onError: AFDecoderErrorCallback; colorSpace: VideoColorSpaceInit | null; hardwareAcceleration: HardwareAcceleration; allowNoPreferenceFallback: boolean; firstDecodeTimeout: number; streaming: boolean; encodedStream: AFEncodedStream; manifest: AFManifest | null; config: VideoDecoderConfig | null; decoder: VideoDecoder | null; enabled: boolean; frame: number | null; frameProcessed: number | null; wanted: Set; _framesByTimestamp: Map; loading: AFDecoderDeferred; configAttempts: readonly AFDecoderConfigAttempt[]; _firstDecodeState: AFDecoderFirstDecodeState; _firstDecodeTimestamp: number | null; _firstDecodeTimer: ReturnType | null; _decoderConfigCandidates: readonly VideoDecoderConfig[]; _nextDecoderConfigCandidate: number; _lastCandidateFailureStep: AFDecoderConfigAttemptStep | null; _lastCandidateFailure: unknown; _streamCursor: number | null; _keyRequired: boolean; _forwardRunTarget: number | null; _forwardHighWater: number; _counters: { resets: number; forwardExtensions: number; decodedFrames: number; }; _mediaDecoderLease: MediaDecoderLease | null; _mediaDecoderRequest: MediaDecoderLeaseRequest; _encodedSource: AFEncodedSource | null; _sourceGeneration: number; _sourceWaitController: AbortController | null; _sourcePrefetchController: AbortController | null; _sourcePrefetchStart: number; _sourcePrefetchEnd: number; /** * @param {string|ArrayBuffer|{manifest: object}} source - `.af` URL to fetch, an * already-loaded ArrayBuffer, or a pre-parsed `{ manifest }` whose frame entries already * carry `data` views (the streaming path — see {@link VAVTrackStream}). * @param {object} [options={}] * @param {(frame: VideoFrame, id: number) => (void|Promise)} [options.process] - Frame sink; owns delivered frames. * @param {(error: unknown) => void} [options.onError] - Fatal-error callback. * @param {'prefer-hardware'|'prefer-software'|'no-preference'} [options.hardwareAcceleration='prefer-hardware'] * @param {VideoColorSpaceInit} [options.colorSpace] - Decoder color-space override. The default fits * camera video; DATA tracks (e.g. USV splat planes: full-range mono, sRGB-compatible transfer) * must declare theirs or conversion-based consumers (GPU texture import, RGBA copyTo) apply a * limited→full range rescale that corrupts the byte planes. * @param {number} [options.firstDecodeTimeout=5000] - Wall-clock deadline for the first requested keyframe output. */ constructor(source: AFDecoderSource, options?: AFDecoderOptions); _init(): Promise; _initDecoder(): Promise; _configureNextDecoderCandidate(): Promise; _updateConfiguredAttempt(step: AFDecoderConfigAttemptStep, error?: unknown): void; _startFirstDecodeWatchdog(decoder: VideoDecoder, timestamp: number): void; _acceptFirstDecode(decoder: VideoDecoder, timestamp: number): void; _rejectDecoderCandidate(decoder: VideoDecoder, step: 'configure' | 'first-decode' | 'watchdog', error?: unknown): void; _retryDecoderCandidate(): Promise; _failFirstDecode(error: unknown): void; _decoderError(decoder: VideoDecoder, cause: unknown): void; _releaseMediaDecoderLease(): void; _decode(decoder: VideoDecoder, chunk: EncodedVideoChunk): boolean; _output(decoder: VideoDecoder, frame: VideoFrame): Promise; _outputFailure(decoder: VideoDecoder, frame: VideoFrame, cause: unknown): void; /** * Whether encoded chunks are still queued on the underlying codec. While true, a new * `request()` resets the codec and cancels the in-flight walk, so callers re-issuing an * unchanged frame set should wait instead of interrupting it. * * @type {boolean} */ get busy(): boolean; /** * Whether requested frames are still on their way to `process`. The codec dequeues chunks * almost immediately (so `busy` drops), but outputs land a few milliseconds later; callers * that re-issue an unchanged frame set inside that window would decode the same pictures * again for nothing. * * @type {boolean} */ get pending(): boolean; /** * Monotonic codec-activity counters: post-admission resets, forward-run extensions, and * decoded outputs (including GOP-walk frames). Consumed by playback diagnostics. * * @type {AFDecoderCounters} */ get counters(): AFDecoderCounters; /** * Whether {@link AFDecoder#request} for `frames` would extend the current forward run by * appending chunks to the live codec — no reset, so queued work and already-delivered * frames survive. True only for `streaming` decoders whose fed cursor can reach every * requested frame sequentially (the earliest target's GOP keyframe is at or behind the * next append position). Owners use this to bypass their busy-codec request parking during * forward playback. * * @param {Iterable} frames - Prospective frame indices. * @return {boolean} */ canAppendForward(frames: Iterable): boolean; /** * Decode a single frame. * * @param {number} index - Frame index. */ setFrame(index: number): void; /** * Decode a set of frames — typically the two frames of an interpolation pair. Only the * listed frames are kept; anything decoded to reach them is dropped. Whichever frame is * already decoded and left off the list stays untouched on screen. * * @param {number[]} frames - Frame indices to produce. */ request(frames: Iterable, { flushTail }?: AFDecoderRequestOptions): void; _withForwardRunRange(list: readonly number[]): readonly number[]; _ensureAndProduce(source: AFEncodedSource, frames: readonly number[], ensured: readonly number[], generation: number, controller: AbortController, flushTail: boolean): Promise; _prefetchForward(source: AFEncodedSource, current: number): void; _sourceFailure(error: unknown): void; /** * Additively feed ONE frame in playback order and keep it (`streaming` decoders only). * Contiguous deltas and any keyframe append to the codec's input stream without a reset — * a keyframe legally re-enters anywhere, which is how a loop wrap feeds frame 0 mid-stream. * A discontinuity falls back to a GOP walk that also re-primes the stream cursor, so the * frames after it append contiguously again. * * Unlike {@link AFDecoder#request}, the wanted set GROWS: every streamed frame is delivered * to `process` when it emerges. The caller paces feeding (a frame-threaded codec can hold * ~a thread-pool of inputs before its first output) and bounds its own delivered residency. * * @param {number} index - Frame index, the next one in playback order. */ streamFrame(index: number): void; /** * Force the codec to emit everything it holds. Required when * no further input is coming — a paused seek, or the final frames of a non-looping clip — * because a frame-threaded codec otherwise withholds its pipeline tail. Afterwards the * codec only accepts a keyframe, so the stream cursor is invalidated; the next * `streamFrame` re-walks its GOP transparently. */ flushTail(): void; _clamp(index: number): number; _chunk(meta: AFFrame): EncodedVideoChunk; _keyIndexFor(index: number): number; _walkGOP(target: number): void; _produce(list: readonly number[]): void; _tryExtendForwardRun(list: readonly number[]): boolean; _drainForwardRun(): void; /** * Release the decoder and stop delivering frames. Any frame currently owned by `process` * (e.g. held in a texture slot) is the caller's to close. A pending admission watchdog is * cancelled, and output racing teardown is closed instead of delivered. */ dispose(): void; } export {};