UNPKG

mediabunny

Version:

Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.

546 lines 25.2 kB
/*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Rotation, SetRequired, Rational, Rectangle, MaybePromise } from './misc.js'; /** * Abstract base class for custom video sample resources. Implement this class to provide custom backing * for VideoSample instances. * @group Samples * @public */ export declare abstract class VideoSampleResource { /** * Returns the internal pixel format in which the frame is stored. * [See pixel formats](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame/format) */ abstract getFormat(): VideoSamplePixelFormat | null; /** Returns the width of the frame in pixels. */ abstract getCodedWidth(): number; /** Returns the height of the frame in pixels. */ abstract getCodedHeight(): number; /** Returns the width of the frame in square pixels, respecting pixel aspect ratio. */ abstract getSquarePixelWidth(): number; /** Returns the height of the frame in square pixels, respecting pixel aspect ratio. */ abstract getSquarePixelHeight(): number; /** Returns the color space of the frame. */ abstract getColorSpace(): VideoSampleColorSpace; /** * Closes this resource, releasing held resources. Called automatically when the last {@link VideoSample} using this * resource is closed. */ abstract close(): void; /** * Returns the data planes that hold the video data for this sample. The returned planes and data must be in the * format returned by `getFormat()`. */ abstract getDataPlanes(): MaybePromise<VideoDataPlane[]>; /** * Returns a new RGB {@link VideoSample} that contains the same content as this sample. The provided `init` object * must be used to set the metadata of this new video sample. When converting from a non-RGB format to RGB, the * conversion must respect `colorSpace`. */ abstract toRgbSample(init: SetRequired<VideoSampleInit, 'timestamp'>, colorSpace: PredefinedColorSpace): MaybePromise<VideoSample>; } /** * Describes a single data plane of a video frame. * @group Samples * @public */ export type VideoDataPlane = { /** The data of the plane. */ data: Uint8Array; /** The stride of the plane, in bytes. This is the distance in bytes between the start of each row of pixels. */ stride: number; }; /** * The list of {@link VideoSample} pixel formats. * @group Samples * @public */ export declare const VIDEO_SAMPLE_PIXEL_FORMATS: readonly ["I420", "I420P10", "I420P12", "I420A", "I420AP10", "I420AP12", "I422", "I422P10", "I422P12", "I422A", "I422AP10", "I422AP12", "I444", "I444P10", "I444P12", "I444A", "I444AP10", "I444AP12", "NV12", "RGBA", "RGBX", "BGRA", "BGRX"]; /** * The internal pixel format with which a {@link VideoSample} is stored. * [See pixel formats](https://www.w3.org/TR/webcodecs/#pixel-format) for more. * @group Samples * @public */ export type VideoSamplePixelFormat = typeof VIDEO_SAMPLE_PIXEL_FORMATS[number]; /** * Metadata used for VideoSample initialization. * @group Samples * @public */ export type VideoSampleInit = { /** * The internal pixel format in which the frame is stored. * [See pixel formats](https://www.w3.org/TR/webcodecs/#pixel-format) */ format?: VideoSamplePixelFormat; /** The width of the frame in pixels. */ codedWidth?: number; /** The height of the frame in pixels. */ codedHeight?: number; /** The rotation of the frame in degrees, clockwise. */ rotation?: Rotation; /** The presentation timestamp of the frame in seconds. */ timestamp?: number; /** The duration of the frame in seconds. */ duration?: number; /** The color space of the frame. */ colorSpace?: VideoColorSpaceInit; /** The byte layout of the planes of the frame. */ layout?: PlaneLayout[]; /** Visible region in the coded frame. When omitted, the rect defaults to `(0, 0, codedWidth, codedHeight)`. */ visibleRect?: Rectangle | undefined; /** Width of the frame in pixels after applying aspect ratio adjustments and rotation. */ displayWidth?: number | undefined; /** Height of the frame in pixels after applying aspect ratio adjustments and rotation. */ displayHeight?: number | undefined; }; /** * Represents a raw, unencoded video sample (frame). Mainly used as an expressive wrapper around WebCodecs API's * [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame), but can also be used standalone. * @group Samples * @public */ export declare class VideoSample implements Disposable { /** * The internal pixel format in which the frame is stored. Will be `null` if it's using an arbitrary internal * format not representable by `VideoSamplePixelFormat`. * [See pixel formats](https://www.w3.org/TR/webcodecs/#pixel-format) */ readonly format: VideoSamplePixelFormat | null; /** The visible region of the frame in the coded pixel grid. */ readonly visibleRect: Rectangle; /** The width of the frame in square pixels (respecting pixel aspect ratio), before rotation is applied. */ readonly squarePixelWidth: number; /** The height of the frame in square pixels (respecting pixel aspect ratio), before rotation is applied. */ readonly squarePixelHeight: number; /** The rotation of the frame in degrees, clockwise. */ readonly rotation: Rotation; /** * The pixel aspect ratio of the frame, as a rational number in its reduced form. Most videos use * square pixels (1:1). */ readonly pixelAspectRatio: Rational; /** * The presentation timestamp of the frame in seconds. May be negative. Frames with negative end timestamps should * not be presented. */ readonly timestamp: number; /** The duration of the frame in seconds. */ readonly duration: number; /** The color space of the frame. */ readonly colorSpace: VideoSampleColorSpace; /** The width of the frame in pixels. */ get codedWidth(): number; /** The height of the frame in pixels. */ get codedHeight(): number; /** The display width of the frame in pixels, after aspect ratio adjustment and rotation. */ get displayWidth(): number; /** The display height of the frame in pixels, after aspect ratio adjustment and rotation. */ get displayHeight(): number; /** The presentation timestamp of the frame in microseconds. */ get microsecondTimestamp(): number; /** The duration of the frame in microseconds. */ get microsecondDuration(): number; /** * Whether this sample uses a pixel format that can hold transparency data. Note that this doesn't necessarily mean * that the sample is transparent. */ get hasAlpha(): boolean | null; /** * Creates a new {@link VideoSample} from a * [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame). This is essentially a near zero-cost * wrapper around `VideoFrame`. The sample's metadata is optionally refined using the data specified in `init`. */ constructor(data: VideoFrame, init?: VideoSampleInit); /** * Creates a new {@link VideoSample} from a * [`CanvasImageSource`](https://udn.realityripple.com/docs/Web/API/CanvasImageSource), similar to the * [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame) constructor. When `VideoFrame` is * available, this is simply a wrapper around its constructor. If not, it will copy the source's image data to an * internal canvas for later use. */ constructor(data: CanvasImageSource, init: SetRequired<VideoSampleInit, 'timestamp'>); /** * Creates a new {@link VideoSample} from raw pixel data specified in `data`. Additional metadata must be provided * in `init`. */ constructor(data: AllowSharedBufferSource, init: SetRequired<VideoSampleInit, 'format' | 'codedWidth' | 'codedHeight' | 'timestamp'>); /** * Creates a new {@link VideoSample} backed by a custom {@link VideoSampleResource}. */ constructor(resource: VideoSampleResource, init: SetRequired<VideoSampleInit, 'timestamp'>); /** Clones this video sample. */ clone(): VideoSample; /** * Closes this video sample, releasing held resources. Video samples should be closed as soon as they are not * needed anymore. */ close(): void; /** * Returns the number of bytes required to hold this video sample's pixel data. */ allocationSize(options?: VideoFrameCopyToOptions): number; /** * Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. * @returns The byte layout of the planes of the copied data. */ copyTo(destination: AllowSharedBufferSource, options?: VideoFrameCopyToOptions): Promise<PlaneLayout[]>; /** * Converts this video sample to a VideoFrame for use with the WebCodecs API. The VideoFrame returned by this * method *must* be closed separately from this video sample. */ toVideoFrame(): VideoFrame; /** * Draws the video sample to a 2D canvas context. Rotation metadata will be taken into account. * * @param dx - The x-coordinate in the destination canvas at which to place the top-left corner of the source image. * @param dy - The y-coordinate in the destination canvas at which to place the top-left corner of the source image. * @param dWidth - The width in pixels with which to draw the image in the destination canvas. * @param dHeight - The height in pixels with which to draw the image in the destination canvas. */ draw(context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, dx: number, dy: number, dWidth?: number, dHeight?: number): void; /** * Draws the video sample to a 2D canvas context. Rotation metadata will be taken into account. * * @param sx - The x-coordinate of the top left corner of the sub-rectangle of the source image to draw into the * destination context. * @param sy - The y-coordinate of the top left corner of the sub-rectangle of the source image to draw into the * destination context. * @param sWidth - The width of the sub-rectangle of the source image to draw into the destination context. * @param sHeight - The height of the sub-rectangle of the source image to draw into the destination context. * @param dx - The x-coordinate in the destination canvas at which to place the top-left corner of the source image. * @param dy - The y-coordinate in the destination canvas at which to place the top-left corner of the source image. * @param dWidth - The width in pixels with which to draw the image in the destination canvas. * @param dHeight - The height in pixels with which to draw the image in the destination canvas. */ draw(context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, sx: number, sy: number, sWidth: number, sHeight: number, dx: number, dy: number, dWidth?: number, dHeight?: number): void; /** * Draws the sample in the middle of the canvas corresponding to the context with the specified fit behavior. */ drawWithFit(context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, options: { /** * Controls the fitting algorithm. * * - `'fill'` will stretch the image to fill the entire box, potentially altering aspect ratio. * - `'contain'` will contain the entire image within the box while preserving aspect ratio. This may lead to * letterboxing. * - `'cover'` will scale the image until the entire box is filled, while preserving aspect ratio. */ fit: 'fill' | 'contain' | 'cover'; /** A way to override rotation. Defaults to the rotation of the sample. */ rotation?: Rotation; /** * Specifies the rectangular region of the video sample to crop to. The crop region will automatically be * clamped to the dimensions of the video sample. Cropping is performed after rotation but before resizing. * The crop region is in the _display pixel space_ of the underlying video data. */ crop?: CropRectangle; }): void; /** * Converts this video sample to a * [`CanvasImageSource`](https://udn.realityripple.com/docs/Web/API/CanvasImageSource) for drawing to a canvas. * * You must use the value returned by this method immediately, as any VideoFrame created internally may * automatically be closed in the next microtask. */ toCanvasImageSource(): OffscreenCanvas | VideoFrame; /** * Transform this video sample to a new video sample given the options. Can be used to resize, rotate, and crop * the sample. * * In non-browser environments, this method will not work by default. To make it work, register a custom * transformer function via {@link registerVideoSampleTransformer}. */ transform(options: VideoSampleTransformOptions): Promise<VideoSample>; /** Sets the rotation metadata of this video sample. */ setRotation(newRotation: Rotation): void; /** Sets the presentation timestamp of this video sample, in seconds. */ setTimestamp(newTimestamp: number): void; /** Sets the duration of this video sample, in seconds. */ setDuration(newDuration: number): void; /** Calls `.close()`. */ [Symbol.dispose](): void; } /** * Options for transforming a {@link VideoSample}. The order of operations are: * * 1. Pixel aspect ratio normalization (always applied) * 2. Rotation * 3. Crop * 4. Resize using fit * @group Samples * @public */ export type VideoSampleTransformOptions = { /** * The width in pixels to resize the frames to. If height is not set, it will be deduced * automatically based on aspect ratio. */ width?: number; /** * The height in pixels to resize the frames to. If width is not set, it will be deduced * automatically based on aspect ratio. */ height?: number; /** * A positive integer. When provided, both the width and height will be rounded to the nearest multiple of * this number. */ roundDimensionsTo?: number; /** * The fitting algorithm in case both width and height are set. * * - `'fill'` will stretch the image to fill the entire box, potentially altering aspect ratio. * - `'contain'` will contain the entire image within the box while preserving aspect ratio. This may lead to * letterboxing. * - `'cover'` will scale the image until the entire box is filled, while preserving aspect ratio. */ fit?: 'fill' | 'contain' | 'cover'; /** * The clockwise rotation by which to rotate the frames. Rotation is applied before resizing. */ rotate?: Rotation; /** * Specifies the rectangular region of the frames to crop to. The crop region will automatically be * clamped to the dimensions of the frame. Cropping is performed after rotation but before resizing. */ crop?: CropRectangle; /** * Whether to discard or keep the transparency information of the video sample. The default is `'keep'`. */ alpha?: 'keep' | 'discard'; }; /** * A fully-resolved description of a video sample transformation, with all defaults and constraints baked in. * * The order of operations must be: * 1. Pixel aspect ratio normalization (always applied) * 2. Rotation * 3. Crop * 4. Resize using fit * @group Samples * @public */ export type VideoSampleTransformationDescription = { /** The width in pixels to resize the frames to. */ width: number; /** The height in pixels to resize the frames to. */ height: number; /** * The fitting algorithm. * * - `'fill'` will stretch the image to fill the entire box, potentially altering aspect ratio. * - `'contain'` will contain the entire image within the box while preserving aspect ratio. This may lead to * letterboxing. * - `'cover'` will scale the image until the entire box is filled, while preserving aspect ratio. */ fit: 'fill' | 'contain' | 'cover'; /** The clockwise rotation by which to rotate the frames. Rotation is applied before resizing. */ rotation: Rotation; /** * The rectangular region of the frames to crop to, clamped to the dimensions of the frame. Cropping is * performed after rotation but before resizing. */ crop: CropRectangle; /** Whether to discard or keep the transparency information of the video sample. */ alpha: 'keep' | 'discard'; }; /** * Registers a callback to handle the transformation of {@link VideoSample} instances. The callback can either return * the transformed sample, or `null` to indicate that it doesn't want to handle the given transformation task. * @group Samples * @public */ export declare const registerVideoSampleTransformer: (transformer: (sample: VideoSample, description: VideoSampleTransformationDescription) => MaybePromise<VideoSample | null>) => void; /** * Describes the color space of a {@link VideoSample}. Corresponds to the WebCodecs API's VideoColorSpace. * @group Samples * @public */ export declare class VideoSampleColorSpace { /** The color primaries standard used. */ readonly primaries: VideoColorPrimaries | null; /** The transfer characteristics used. */ readonly transfer: VideoTransferCharacteristics | null; /** The color matrix coefficients used. */ readonly matrix: VideoMatrixCoefficients | null; /** Whether the color values use the full range or limited range. */ readonly fullRange: boolean | null; /** Creates a new VideoSampleColorSpace. */ constructor(init?: VideoColorSpaceInit); /** Serializes the color space to a JSON object. */ toJSON(): VideoColorSpaceInit; } /** * Specifies the rectangular cropping region. * @group Miscellaneous * @public */ export type CropRectangle = { /** The distance in pixels from the left edge of the source frame to the left edge of the crop rectangle. */ left: number; /** The distance in pixels from the top edge of the source frame to the top edge of the crop rectangle. */ top: number; /** The width in pixels of the crop rectangle. */ width: number; /** The height in pixels of the crop rectangle. */ height: number; }; export declare const clampCropRectangle: (crop: CropRectangle, outerWidth: number, outerHeight: number) => CropRectangle; export declare const validateCropRectangle: (crop: CropRectangle, prefix: string) => void; type PlaneConfig = { sampleBytes: number; widthDivisor: number; heightDivisor: number; }; /** Helper to retrieve plane configurations based on WebCodecs § 9.8 Pixel Format definitions. */ export declare const getPlaneConfigs: (format: VideoSamplePixelFormat) => PlaneConfig[]; /** * Abstract base class for custom audio sample resources. Implement this class to provide custom backing * for AudioSample instances. * @group Samples * @public */ export declare abstract class AudioSampleResource { /** * Returns the audio sample format. * [See sample formats](https://developer.mozilla.org/en-US/docs/Web/API/AudioData/format) */ abstract getFormat(): AudioSampleFormat; /** Returns the audio sample rate in hertz. */ abstract getSampleRate(): number; /** Returns the number of audio frames in the sample, per channel. */ abstract getNumberOfFrames(): number; /** Returns the number of audio channels. */ abstract getNumberOfChannels(): number; /** Returns the presentation timestamp of the sample in seconds. */ abstract getTimestamp(): number; /** * Closes this resource, releasing held resources. Called automatically when the last {@link AudioSample} using this * resource is closed. */ abstract close(): void; /** * Returns the audio sample data for the plane given by `planeIndex`. The audio data must be in the format returned * by `getFormat()`. For interleaved formats, there is only one plane. */ abstract getDataPlane(planeIndex: number): Uint8Array; } /** * Metadata used for AudioSample initialization. * @group Samples * @public */ export type AudioSampleInit = { /** The audio data for this sample. */ data: AllowSharedBufferSource; /** * The audio sample format. [See sample formats](https://developer.mozilla.org/en-US/docs/Web/API/AudioData/format) */ format: AudioSampleFormat; /** The number of audio channels. */ numberOfChannels: number; /** The audio sample rate in hertz. */ sampleRate: number; /** The presentation timestamp of the sample in seconds. */ timestamp: number; }; /** * Options used for copying audio sample data. * @group Samples * @public */ export type AudioSampleCopyToOptions = { /** * The index identifying the plane to copy from. This must be 0 if using a non-planar (interleaved) output format. */ planeIndex: number; /** * The output format for the destination data. Defaults to the AudioSample's format. * [See sample formats](https://developer.mozilla.org/en-US/docs/Web/API/AudioData/format) */ format?: AudioSampleFormat; /** An offset into the source plane data indicating which frame to begin copying from. Defaults to 0. */ frameOffset?: number; /** * The number of frames to copy. If not provided, the copy will include all frames in the plane beginning * with frameOffset. */ frameCount?: number; }; /** * Represents a raw, unencoded audio sample. Mainly used as an expressive wrapper around WebCodecs API's * [`AudioData`](https://developer.mozilla.org/en-US/docs/Web/API/AudioData), but can also be used standalone. * @group Samples * @public */ export declare class AudioSample implements Disposable { /** * The audio sample format. * [See sample formats](https://developer.mozilla.org/en-US/docs/Web/API/AudioData/format) */ readonly format: AudioSampleFormat; /** The audio sample rate in hertz. */ readonly sampleRate: number; /** * The number of audio frames in the sample, per channel. In other words, the length of this audio sample in frames. */ readonly numberOfFrames: number; /** The number of audio channels. */ readonly numberOfChannels: number; /** The duration of the sample in seconds. */ readonly duration: number; /** * The presentation timestamp of the sample in seconds. May be negative. Samples with negative end timestamps should * not be presented. */ readonly timestamp: number; /** The presentation timestamp of the sample in microseconds. */ get microsecondTimestamp(): number; /** The duration of the sample in microseconds. */ get microsecondDuration(): number; /** * Creates a new {@link AudioSample}, either from an existing * [`AudioData`](https://developer.mozilla.org/en-US/docs/Web/API/AudioData) or from raw bytes specified in * {@link AudioSampleInit}. */ constructor(init: AudioData | AudioSampleInit | AudioSampleResource); /** Returns the number of bytes required to hold the audio sample's data as specified by the given options. */ allocationSize(options: AudioSampleCopyToOptions): number; /** Copies the audio sample's data to an ArrayBuffer or ArrayBufferView as specified by the given options. */ copyTo(destination: AllowSharedBufferSource, options: AudioSampleCopyToOptions): void; /** Clones this audio sample. */ clone(): AudioSample; /** * Closes this audio sample, releasing held resources. Audio samples should be closed as soon as they are not * needed anymore. */ close(): void; /** * Converts this audio sample to an AudioData for use with the WebCodecs API. The AudioData returned by this * method *must* be closed separately from this audio sample. */ toAudioData(): AudioData; /** Convert this audio sample to an AudioBuffer for use with the Web Audio API. */ toAudioBuffer(): AudioBuffer; /** Sets the presentation timestamp of this audio sample, in seconds. */ setTimestamp(newTimestamp: number): void; /** Calls `.close()`. */ [Symbol.dispose](): void; /** * Creates AudioSamples from an AudioBuffer, starting at the given timestamp in seconds. Typically creates exactly * one sample, but may create multiple if the AudioBuffer is exceedingly large. */ static fromAudioBuffer(audioBuffer: AudioBuffer, timestamp: number): AudioSample[]; } export declare const toInterleavedAudioFormat: (format: AudioSampleFormat) => "u8" | "s16" | "s32" | "f32"; export declare const audioSampleToInterleavedFormat: (sample: AudioSample, format: "u8" | "s16" | "s32" | "f32") => AudioSample; export {}; //# sourceMappingURL=sample.d.ts.map