/** * ThumbnailRenderer - WebGL2-based thumbnail rendering with HDR support * Uses browser-specific rendering paths for optimal quality: * - Chromium: Native HDR via drawingBufferColorSpace * - Non-Chromium: Shader-based PQ tone mapping */ import type { VRView } from "../render/CanvasRenderer"; export interface ThumbnailRenderOptions { width: number; height: number; rotation?: number; colorPrimaries?: string; colorTransfer?: string; hdrEnabled?: boolean; } export declare class ThumbnailRenderer { private canvas; private gl; private program; private texture; private vao; private rotation; private vrProgram; private vrVao; private vrLocs; private vrView; private flatWidth; private flatHeight; private static readonly VR_PREVIEW_HEIGHT; private static readonly VR_FISHEYE_FOV; private static readonly VR_PLANET_SCALE; private decoder; private pendingDecodeResolve; /** * The frame a RUN is being decoded for, and the best answer so far. * * A precise preview walks a whole GOP to reach one frame, and the output * callback used to paint every frame it passed on the way — ninety 4K * texture uploads to show the ninetieth. Painting is 80% of what a preview * costs on such a source, and all but one of those paints is thrown away in * the same millisecond. While this is set the callback picks instead of * paints, and the one frame that was asked for is drawn at the end. */ private seqTargetUs; private seqBest; private seqBestTs; private lastDecoderConfig; private decoderRevivedUnproven; private hasNativeHDRSupport; private isHDRSource; private isAnnexBSource; private hdrEnabled; private lastColorPrimaries?; private lastColorTransfer?; constructor(); /** * Detect if browser supports native HDR (Chromium-based) */ private detectChromium; /** * Detect HDR color space from metadata */ private detectHDRColorSpace; /** * Initialize WebGL2 context with appropriate shader for browser */ initialize(options: ThumbnailRenderOptions): void; /** * Simple WebGL initialization for Chromium (native HDR support) */ private initWebGLSimple; /** * WebGL initialization with HDR tone mapping for non-Chromium browsers */ private initWebGLWithHDR; /** * Create and compile shader program */ private createProgram; /** * Setup geometry (fullscreen quad) */ private setupGeometry; /** * Configure WebCodecs VideoDecoder */ configureDecoder(codec: string, extradata: Uint8Array | null, width: number, height: number, profile?: number, level?: number): Promise; /** * Map FFmpeg codec names to WebCodecs codec strings * (Copied from MoviVideoDecoder for consistency) */ private mapCodecToWebCodecs; /** * Decode and render a packet using WebCodecs */ /** * Decode a RUN of packets — a keyframe and the frames that follow it — and * leave the last one on the canvas. * * Sent together and flushed once, because a flush is the one thing that * breaks a run: Chrome answers the first delta after it with "A key frame is * required after configure() or flush()". * * Only ONE of them is painted — the one belonging to `targetSec`, or the * last to arrive when no target is given. Painting each frame as it came out * was most of what a precise preview cost: a 4K walk of ninety frames did * ninety texture uploads to show the last, and measured 157ms of a 195ms * preview. See seqTargetUs. */ /** Hand over the frame a run settled on and end the run. A method, so the * answer isn't narrowed away by the `= null` this function does on the way * in — only the decoder's own callback ever puts a frame here. */ private takeSeqBest; decodeSequenceAndRender(items: Array<{ data: Uint8Array; pts: number; key: boolean; }>, targetSec?: number): Promise; decodeAndRender(packetData: Uint8Array, pts: number, duration?: number, /** * A frame that is NOT the keyframe the seek landed on — one of the frames * between it and where the pointer actually is (see the precise preview * mode). Sent as a delta chunk, because that is what it is: telling the * decoder a P-frame is a keyframe makes it reject the whole run. */ opts?: { key?: boolean; }): Promise; /** * Recreate the WebCodecs decoder from the last-used config. Called when the * decoder has died (closed / errored) so a single decode failure doesn't * disable thumbnails for the whole session. Returns false if there is no * saved config or reconfiguration fails. */ private recreateDecoder; /** * Render RGBA data to canvas */ render(rgbaData: Uint8Array, width: number, height: number): void; /** * Render VideoFrame to canvas */ renderVideoFrame(frame: VideoFrame): void; /** * Set (or clear) the 360° reprojection view. When non-null, the next draw() * reprojects the equirectangular texture to this camera angle; null restores * the flat passthrough. Must be set BEFORE render()/decodeAndRender(), since * draw() runs synchronously inside those. */ setProjection(view: VRView | null): void; /** * Lazily compile the equirect/fisheye/stereographic raycast program (ported * verbatim from CanvasRenderer.initVRProgram) plus its own unrotated * fullscreen quad feeding v_ndc. Returns true when ready. */ private initVRProgram; /** Reproject the bound equirect texture to the current 360 view. */ private drawVR; private draw; /** * Setup texture */ private setupTexture; /** * Set HDR enabled state */ setHDREnabled(enabled: boolean): void; /** * Get the rendered canvas */ getCanvas(): HTMLCanvasElement; /** * Destroy WebGL resources */ destroy(): void; } //# sourceMappingURL=ThumbnailRenderer.d.ts.map