/** * Movi Types - Core type definitions for the streaming video library */ export type TrackType = "video" | "audio" | "subtitle"; export interface Track { id: number; type: "video" | "audio" | "subtitle"; codec: string; codecString?: string; extradata?: Uint8Array; profile?: number; level?: number; language?: string; label?: string; width?: number; height?: number; frameRate?: number; channels?: number; sampleRate?: number; subtitleType?: "text" | "image"; } export interface VideoTrack extends Track { type: "video"; width: number; height: number; frameRate: number; pixelFormat?: string; colorSpace?: string; colorPrimaries?: string; colorTransfer?: string; bitRate?: number; rotation?: number; colorRange?: string; isHDR?: boolean; /** * 360° spherical projection from container metadata: 0 / undefined = not a * 360 video, else AVSphericalProjection+1 (1=equirectangular, 2=cubemap, * 3=equirectangular-tile, 4=half-equirectangular). Only equirectangular * (1 and 3) is renderable by the current 360 viewer. */ projection?: number; /** * True when this is an embedded cover-art pseudo-stream (ID3v2 APIC, * FLAC PICTURE, MP4 covr, Matroska attachment). These look like single- * frame PNG/JPEG video streams to the demuxer; consumers picking an * active video track should skip them and read the picture via the * player's cover-art accessor instead. */ isAttachedPic?: boolean; } export interface AudioTrack extends Track { type: "audio"; channels: number; sampleRate: number; bitRate?: number; } export interface SubtitleTrack extends Track { type: "subtitle"; subtitleType: "text" | "image"; } export interface SubtitleCue { start: number; end: number; text?: string; image?: ImageBitmap; position?: { x: number; y: number; }; } export interface SourceConfig { type: "url" | "file" | "encrypted"; url?: string; file?: File; headers?: Record; /** Encrypted source config */ encrypted?: { videoUrl: string; tokenUrl: string; videoId: string; fingerprint: string; sessionToken: string; tokenRefreshInterval?: number; onAuthFailed?: (reason: string) => void; }; } /** Audio source with language metadata for multi-language support */ export interface AudioSourceEntry { url: string; type?: string; lang: string; label: string; /** * Pre-built adapter for this track (e.g. an HLS audio rendition presented as * a concatenated segment stream). Opened directly instead of `url` when set; * `url` still serves as the display/cache key. */ adapter?: import("./source/SourceAdapter").SourceAdapter; } /** External subtitle source (VTT/SRT) with language metadata */ export interface SubtitleSourceEntry { url: string; lang: string; label: string; format?: "vtt" | "srt" | "ttml"; } export interface CacheConfig { type: "lru"; maxSizeMB: number; } export type RendererType = "canvas"; export type DecoderType = "auto" | "software"; export interface PlayerConfig { /** * Standard source descriptor (url / file / encrypted). Optional when a * pre-built `sourceAdapter` is supplied instead. */ source?: SourceConfig; /** * Pre-built SourceAdapter — overrides `source` when present. * Use this to feed media from a custom protocol (WebSocket, WebRTC data * channel, IndexedDB, encrypted blob, etc.) without writing a SourceConfig * branch. The adapter's `getSize()` and `read()` are called directly. */ sourceAdapter?: import("./source/SourceAdapter").SourceAdapter; /** * Skip the MSE stream engines (Shaka / hls.js / dash.js) for a DASH source * and play its single-file Representation through the FFmpeg-WASM demuxer * instead. Set after an MSE engine fails at RUNTIME on a codec the browser * can't decode (e.g. Safari + HE-AAC): the demuxer decodes every codec. */ forceStreamDemux?: boolean; /** * Skip Shaka and play a stream through a specific MSE engine (hls.js / * dash.js) directly. Set when Shaka failed at runtime but the other engine * is more lenient (e.g. a manifest/actual codec mismatch) — tried before the * heavier WASM demuxer so hardware MSE playback is preferred when possible. */ forceStreamEngine?: "dashjs" | "hlsjs"; /** * When force-demuxing a DASH source, use this specific video Representation * file instead of the best one — set when the user picks a quality in the * demuxer-mode quality menu, so the re-load lands on the chosen rendition. */ forceVideoRendition?: string; /** Separate audio source — single or multi-language */ audioSource?: SourceConfig; /** Multiple audio tracks with language metadata */ audioTracks?: AudioSourceEntry[]; /** External subtitle tracks (VTT/SRT) with language metadata */ subtitleTracks?: SubtitleSourceEntry[]; renderer?: RendererType; decoder?: DecoderType; cache?: CacheConfig; canvas?: HTMLCanvasElement | OffscreenCanvas; wasmBinary?: Uint8Array; enablePreviews?: boolean; frameRate?: number; headers?: Record; audioOnly?: boolean; drm?: boolean; licenseUrl?: string; licenseHeaders?: Record; lcevc?: boolean; lcevcUrl?: string; } export interface Chapter { title: string; start: number; end: number; /** * Optional artwork for the chapter, supplied by the host alongside the list. * Where it is set the timeline tile shows it instead of decoding a frame at * `start` — which is both the picture the host wanted and one less seek. * Container chapters never carry one; it is only ever set through the * `chapters` attribute/property. */ image?: string; } export interface MediaInfo { formatName: string; duration: number; bitRate: number; startTime: number; tracks: Track[]; chapters: Chapter[]; metadata?: { [key: string]: string; }; } export interface VideoDecoderConfig { codec: string; codedWidth: number; codedHeight: number; description?: Uint8Array; colorSpace?: { primaries?: VideoColorPrimaries | null; transfer?: VideoTransferCharacteristics | null; matrix?: VideoMatrixCoefficients | null; fullRange?: boolean | null; }; hardwareAcceleration?: "no-preference" | "prefer-hardware" | "prefer-software"; } export interface AudioDecoderConfig { codec: string; sampleRate: number; numberOfChannels: number; description?: Uint8Array; } export interface Packet { streamIndex: number; keyframe: boolean; timestamp: number; dts: number; duration: number; data: Uint8Array; isIdr: boolean; isRasl: boolean; disposable: boolean; } /** * Pluggable subtitle renderer. movi-player decodes video to its own WebGL canvas * (no HTMLVideoElement), so a native `` overlay can't be used — and full * ASS/SSA styling (positioning, karaoke, embedded fonts) is beyond the built-in * text/bitmap path. Register one of these via `player.setSubtitleRenderer()` (or * `.setSubtitleRenderer()`) to take over an embedded subtitle track * with your own renderer — e.g. jassub (libass-wasm) for pixel-accurate ASS — * without the core taking that dependency. * * When a renderer is set, the player stops feeding the selected subtitle stream * to its internal decoder and drives this instead: `configure` on track select, * `pushPacket` for each demuxed subtitle packet, `render` every frame with the * current media time and the video's native dimensions, `setDelay` on a subtitle * offset change, `clear` on seek/track change, and `destroy` on teardown/swap. * If `mount` is present the player calls it with an overlay element already sized * and positioned over the visible video (letterbox-aware) for the renderer to * draw into. */ export interface SubtitleRenderer { /** Called on track selection. `extradata` is the codec header (the ASS * `[Script Info]`/`[V4+ Styles]` block for ass/ssa). `fonts` are embedded * font attachments when available (may be undefined). */ configure(track: SubtitleTrack, extradata?: Uint8Array, fonts?: Uint8Array[]): Promise | void; /** One demuxed subtitle packet for the active track. */ pushPacket(packet: Packet): Promise | void; /** Draw the state for `mediaTime` (seconds). `videoWidth`/`videoHeight` are the * source frame dimensions the subtitle coordinates are authored against. */ render(mediaTime: number, videoWidth: number, videoHeight: number): Promise | void; /** Optional: receive the player's subtitle overlay element (absolutely * positioned over the visible video) to append a canvas/DOM into. */ mount?(container: HTMLElement): void; /** Subtitle timing offset in seconds (positive = later). */ setDelay(seconds: number): void; /** Drop all pending state — called on seek and track change. */ clear(): void; /** Release resources — called on teardown or when swapped out. */ destroy(): Promise | void; } export interface DecodedVideoFrame { timestamp: number; duration: number; width: number; height: number; format: "yuv420p" | "rgb24" | "rgba"; data: Uint8Array; planes?: { y?: Uint8Array; u?: Uint8Array; v?: Uint8Array; }; } export interface DecodedAudioFrame { timestamp: number; duration: number; sampleRate: number; channels: number; numFrames: number; format: "f32-planar"; channelData: Float32Array[]; } export type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "seeking" | "buffering" | "ended" | "error"; export interface PlayerEventMap { frame: DecodedVideoFrame; audio: DecodedAudioFrame; subtitle: SubtitleCue; stateChange: PlayerState; timeUpdate: number; durationChange: number; tracksChange: Track[]; error: Error; filerevoked: { offset: number; length: number; reason: string; }; loadStart: void; loadEnd: void; /** * An in-place rendition switch started (`active: true`) or finished. Playback * continues throughout — audio never stops — but the picture is held for a * second or two while the new rendition opens, and without a sign of life * that pause reads as the player glitching. `label` names the rung being * moved to, for a UI that wants to say so. */ renditionSwitch: { active: boolean; label?: string; }; seeking: number; seeked: number; bufferUpdate: { start: number; end: number; }[]; ended: void; preloadcomplete: void; /** * Embedded cover art extracted from the source (ID3v2 APIC, FLAC PICTURE, * MP4 covr, Matroska attachment). Fires once after track enumeration when * an attached_pic pseudo-stream is present. Recipients own the bitmap and * should close() it on disposal. Fires with `null` when an art track was * present but extraction failed, so listeners waiting on it can stop. */ coverart: ImageBitmap | null; /** * Fired once when playback falls back to linear (forward-only, non-seekable) * mode because the server has no HTTP Range support and the file is too large * to cache whole. The UI hides the timeline and disables seeking/thumbnails. */ linearmode: void; /** * Autoplay-with-sound was refused, so playback started muted instead. The UI * surfaces the "Tap to unmute" pill — the user's gesture is the only way back * to audio. Emitted by the native fallback, whose muting happens inside the * media element (the WASM path detects the same condition off its * AudioContext, see maybeFallbackToMutedAutoplay). */ autoplaymuted: void; } //# sourceMappingURL=types.d.ts.map