From 81d3d1e6a4db43954f19017031984bde2445cb35 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:14:12 +0100 Subject: [PATCH] Add VideoSampleResource and AudioSampleResource --- src/sample.ts | 293 ++++++++++++++++---- test/browser/custom-sample-resource.test.ts | 203 ++++++++++++++ test/public/red-image.png | Bin 0 -> 68865 bytes 3 files changed, 443 insertions(+), 53 deletions(-) create mode 100644 test/browser/custom-sample-resource.test.ts create mode 100644 test/public/red-image.png diff --git a/src/sample.ts b/src/sample.ts index 02e454c..f0f9dda 100644 --- a/src/sample.ts +++ b/src/sample.ts @@ -23,10 +23,10 @@ polyfillSymbolDispose(); type FinalizationRegistryValue = { type: 'video'; - data: VideoFrame | OffscreenCanvas | Uint8Array; + data: VideoFrame | OffscreenCanvas | Uint8Array | VideoSampleResource; } | { type: 'audio'; - data: AudioData | Uint8Array; + data: AudioData | Uint8Array | AudioSampleResource; }; // Let's manually handle logging the garbage collection errors that are typically logged by the browser. This way, they @@ -69,6 +69,46 @@ if (typeof FinalizationRegistry !== 'undefined') { }); } +/** + * Abstract base class for custom video sample resources. Implement this class to provide custom backing + * for VideoSample instances. + * @group Samples + * @public + */ +export abstract class VideoSampleResource { + /** @internal */ + _referenceCount: number = 0; + /** @internal */ + _lastAllocationBuffer: ArrayBuffer | null = null; + + /** + * 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(): VideoPixelFormat | 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 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 number of bytes required to hold this video sample's pixel data. */ + abstract allocationSize(options: VideoFrameCopyToOptions): number; + + /** Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. */ + abstract copyTo(destination: AllowSharedBufferSource, options: VideoFrameCopyToOptions): PlaneLayout[]; +} + /** * Metadata used for VideoSample initialization. * @group Samples @@ -102,7 +142,7 @@ export type VideoSampleInit = { */ export class VideoSample implements Disposable { /** @internal */ - _data!: VideoFrame | OffscreenCanvas | Uint8Array | null; + _data!: VideoFrame | OffscreenCanvas | Uint8Array | VideoSampleResource | null; /** @internal */ _closed: boolean = false; @@ -177,8 +217,12 @@ export class VideoSample implements Disposable { data: AllowSharedBufferSource, init: SetRequired ); + /** + * Creates a new {@link VideoSample} backed by a custom {@link VideoSampleResource}. + */ + constructor(resource: VideoSampleResource, init: SetRequired); constructor( - data: VideoFrame | CanvasImageSource | AllowSharedBufferSource, + data: VideoFrame | CanvasImageSource | AllowSharedBufferSource | VideoSampleResource, init?: VideoSampleInit, ) { if ( @@ -314,8 +358,34 @@ export class VideoSample implements Disposable { transfer: 'iec61966-2-1', fullRange: true, }); + } else if (data instanceof VideoSampleResource) { + if (!init || typeof init !== 'object') { + throw new TypeError('init must be an object.'); + } + if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) { + throw new TypeError('init.rotation, when provided, must be 0, 90, 180, or 270.'); + } + if (!Number.isFinite(init.timestamp)) { + throw new TypeError('init.timestamp must be a number.'); + } + if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) { + throw new TypeError('init.duration, when provided, must be a non-negative number.'); + } + + this._data = data; + data._referenceCount++; + + this.format = data.getFormat(); + this.codedWidth = data.getCodedWidth(); + this.codedHeight = data.getCodedHeight(); + this.rotation = init.rotation ?? 0; + this.timestamp = init.timestamp!; + this.duration = init.duration ?? 0; + this.colorSpace = data.getColorSpace(); } else { - throw new TypeError('Invalid data type: Must be a BufferSource or CanvasImageSource.'); + throw new TypeError( + 'Invalid data type: Must be a BufferSource, CanvasImageSource, or VideoSampleResource.', + ); } finalizationRegistry?.register(this, { type: 'video', data: this._data }, this); @@ -329,7 +399,13 @@ export class VideoSample implements Disposable { assert(this._data !== null); - if (isVideoFrame(this._data)) { + if (this._data instanceof VideoSampleResource) { + return new VideoSample(this._data, { + timestamp: this.timestamp, + duration: this.duration, + rotation: this.rotation, + }); + } else if (isVideoFrame(this._data)) { return new VideoSample(this._data.clone(), { timestamp: this.timestamp, duration: this.duration, @@ -369,7 +445,12 @@ export class VideoSample implements Disposable { finalizationRegistry?.unregister(this); - if (isVideoFrame(this._data)) { + if (this._data instanceof VideoSampleResource) { + this._data._referenceCount--; + if (this._data._referenceCount === 0) { + this._data.close(); + } + } else if (isVideoFrame(this._data)) { this._data.close(); } else { this._data = null; // GC that shit @@ -379,15 +460,17 @@ export class VideoSample implements Disposable { } /** Returns the number of bytes required to hold this video sample's pixel data. */ - allocationSize() { + allocationSize(options: VideoFrameCopyToOptions = {}) { if (this._closed) { throw new Error('VideoSample is closed.'); } assert(this._data !== null); - if (isVideoFrame(this._data)) { - return this._data.allocationSize(); + if (this._data instanceof VideoSampleResource) { + return this._data.allocationSize(options); + } else if (isVideoFrame(this._data)) { + return this._data.allocationSize(options); } else if (this._data instanceof Uint8Array) { return this._data.byteLength; } else { @@ -396,7 +479,7 @@ export class VideoSample implements Disposable { } /** Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. */ - async copyTo(destination: AllowSharedBufferSource) { + async copyTo(destination: AllowSharedBufferSource, options: VideoFrameCopyToOptions = {}) { if (!isAllowSharedBufferSource(destination)) { throw new TypeError('destination must be an ArrayBuffer or an ArrayBuffer view.'); } @@ -407,8 +490,10 @@ export class VideoSample implements Disposable { assert(this._data !== null); - if (isVideoFrame(this._data)) { - await this._data.copyTo(destination); + if (this._data instanceof VideoSampleResource) { + this._data.copyTo(destination, options); + } else if (isVideoFrame(this._data)) { + await this._data.copyTo(destination, options); } else if (this._data instanceof Uint8Array) { const dest = toUint8Array(destination); dest.set(this._data); @@ -427,14 +512,35 @@ export class VideoSample implements Disposable { * 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() { + toVideoFrame(): VideoFrame { if (this._closed) { throw new Error('VideoSample is closed.'); } assert(this._data !== null); - if (isVideoFrame(this._data)) { + if (this._data instanceof VideoSampleResource) { + const format = this._data.getFormat() ?? 'RGBA'; + const allocationSize = this._data.allocationSize({ format }); + + let data: ArrayBuffer; + if (this._data._lastAllocationBuffer?.byteLength === allocationSize) { + data = this._data._lastAllocationBuffer; + } else { + data = this._data._lastAllocationBuffer = new ArrayBuffer(allocationSize); + } + const layout = this._data.copyTo(data, { format }); + + return new VideoFrame(data, { + format, + codedWidth: this._data.getCodedWidth(), + codedHeight: this._data.getCodedHeight(), + colorSpace: this._data.getColorSpace(), + layout, + timestamp: this.microsecondTimestamp, + duration: this.microsecondDuration, + }); + } else if (isVideoFrame(this._data)) { return new VideoFrame(this._data, { timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || undefined, // Drag 0 duration to undefined, glitches some codecs @@ -748,7 +854,7 @@ export class VideoSample implements Disposable { * 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 will + * You must use the value returned by this method immediately, as any VideoFrame created internally may * automatically be closed in the next microtask. */ toCanvasImageSource() { @@ -758,7 +864,7 @@ export class VideoSample implements Disposable { assert(this._data !== null); - if (this._data instanceof Uint8Array) { + if (this._data instanceof VideoSampleResource || this._data instanceof Uint8Array) { // Requires VideoFrame to be defined const videoFrame = this.toVideoFrame(); queueMicrotask(() => videoFrame.close()); // Let's automatically close the frame in the next microtask @@ -891,6 +997,51 @@ const AUDIO_SAMPLE_FORMATS = new Set( ['f32', 'f32-planar', 's16', 's16-planar', 's32', 's32-planar', 'u8', 'u8-planar'], ); +/** + * Abstract base class for custom audio sample resources. Implement this class to provide custom backing + * for AudioSample instances. + * @group Samples + * @public + */ +export abstract class AudioSampleResource { + /** @internal */ + _referenceCount: number = 0; + + /** + * 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 number of bytes required to hold the underlying audio data as specified by the given options. + */ + abstract allocationSize(options: AudioSampleCopyToOptions): number; + + /** + * Copies the underlying audio data to an ArrayBuffer or ArrayBufferView as specified by the given options. + */ + abstract copyTo(destination: AllowSharedBufferSource, options: AudioSampleCopyToOptions): void; +} + /** * Metadata used for AudioSample initialization. * @group Samples @@ -943,7 +1094,7 @@ export type AudioSampleCopyToOptions = { */ export class AudioSample implements Disposable { /** @internal */ - _data: AudioData | Uint8Array; + _data: AudioData | Uint8Array | AudioSampleResource; /** @internal */ _closed: boolean = false; @@ -983,7 +1134,8 @@ export class AudioSample implements Disposable { * [`AudioData`](https://developer.mozilla.org/en-US/docs/Web/API/AudioData) or from raw bytes specified in * {@link AudioSampleInit}. */ - constructor(init: AudioData | AudioSampleInit) { + constructor(init: AudioData | AudioSampleInit | AudioSampleResource); + constructor(init: AudioData | AudioSampleInit | AudioSampleResource) { if (isAudioData(init)) { if (init.format === null) { throw new TypeError('AudioData with null format is not supported.'); @@ -997,6 +1149,16 @@ export class AudioSample implements Disposable { this.numberOfChannels = init.numberOfChannels; this.timestamp = init.timestamp / 1e6; this.duration = init.numberOfFrames / init.sampleRate; + } else if (init instanceof AudioSampleResource) { + this._data = init; + init._referenceCount++; + + this.format = init.getFormat(); + this.sampleRate = init.getSampleRate(); + this.numberOfFrames = init.getNumberOfFrames(); + this.numberOfChannels = init.getNumberOfChannels(); + this.timestamp = init.getTimestamp(); + this.duration = this.numberOfFrames / this.sampleRate; } else { if (!init || typeof init !== 'object') { throw new TypeError('Invalid AudioDataInit: must be an object.'); @@ -1072,6 +1234,10 @@ export class AudioSample implements Disposable { throw new Error('AudioSample is closed.'); } + if (this._data instanceof AudioSampleResource) { + return this._data.allocationSize(options); + } + const destFormat = options.format ?? this.format; const frameOffset = options.frameOffset ?? 0; @@ -1124,6 +1290,10 @@ export class AudioSample implements Disposable { throw new Error('AudioSample is closed.'); } + if (this._data instanceof AudioSampleResource) { + return this._data.copyTo(destination, options); + } + const { planeIndex, format, frameCount: optFrameCount, frameOffset: optFrameOffset } = options; const destFormat = format ?? this.format; @@ -1253,9 +1423,14 @@ export class AudioSample implements Disposable { throw new Error('AudioSample is closed.'); } - if (isAudioData(this._data)) { + if (this._data instanceof AudioSampleResource) { + const sample = new AudioSample(this._data); + sample.setTimestamp(this.timestamp); // Make sure the timestamp is correct + + return sample; + } else if (isAudioData(this._data)) { const sample = new AudioSample(this._data.clone()); - sample.setTimestamp(this.timestamp); // Make sure the timestamp is precise (beyond microsecond accuracy) + sample.setTimestamp(this.timestamp); return sample; } else { @@ -1281,7 +1456,12 @@ export class AudioSample implements Disposable { finalizationRegistry?.unregister(this); - if (isAudioData(this._data)) { + if (this._data instanceof AudioSampleResource) { + this._data._referenceCount--; + if (this._data._referenceCount === 0) { + this._data.close(); + } + } else if (isAudioData(this._data)) { this._data.close(); } else { this._data = new Uint8Array(0); @@ -1299,42 +1479,15 @@ export class AudioSample implements Disposable { throw new Error('AudioSample is closed.'); } - if (isAudioData(this._data)) { + if (this._data instanceof AudioSampleResource) { + return this._createAudioDataFromData(); + } else if (isAudioData(this._data)) { if (this._data.timestamp === this.microsecondTimestamp) { // Timestamp matches, let's just return the data (but cloned) return this._data.clone(); } else { // It's impossible to simply change an AudioData's timestamp, so we'll need to create a new one - if (formatIsPlanar(this.format)) { - const size = this.allocationSize({ planeIndex: 0, format: this.format }); - const data = new ArrayBuffer(size * this.numberOfChannels); - - // We gotta read out each plane individually - for (let i = 0; i < this.numberOfChannels; i++) { - this.copyTo(new Uint8Array(data, i * size, size), { planeIndex: i, format: this.format }); - } - - return new AudioData({ - format: this.format, - sampleRate: this.sampleRate, - numberOfFrames: this.numberOfFrames, - numberOfChannels: this.numberOfChannels, - timestamp: this.microsecondTimestamp, - data, - }); - } else { - const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format })); - this.copyTo(data, { planeIndex: 0, format: this.format }); - - return new AudioData({ - format: this.format, - sampleRate: this.sampleRate, - numberOfFrames: this.numberOfFrames, - numberOfChannels: this.numberOfChannels, - timestamp: this.microsecondTimestamp, - data, - }); - } + return this._createAudioDataFromData(); } } else { return new AudioData({ @@ -1350,6 +1503,40 @@ export class AudioSample implements Disposable { } } + /** @internal */ + _createAudioDataFromData() { + if (formatIsPlanar(this.format)) { + const size = this.allocationSize({ planeIndex: 0, format: this.format }); + const data = new ArrayBuffer(size * this.numberOfChannels); + + // We gotta read out each plane individually + for (let i = 0; i < this.numberOfChannels; i++) { + this.copyTo(new Uint8Array(data, i * size, size), { planeIndex: i, format: this.format }); + } + + return new AudioData({ + format: this.format, + sampleRate: this.sampleRate, + numberOfFrames: this.numberOfFrames, + numberOfChannels: this.numberOfChannels, + timestamp: this.microsecondTimestamp, + data, + }); + } else { + const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format })); + this.copyTo(data, { planeIndex: 0, format: this.format }); + + return new AudioData({ + format: this.format, + sampleRate: this.sampleRate, + numberOfFrames: this.numberOfFrames, + numberOfChannels: this.numberOfChannels, + timestamp: this.microsecondTimestamp, + data, + }); + } + } + /** Convert this audio sample to an AudioBuffer for use with the Web Audio API. */ toAudioBuffer() { if (this._closed) { diff --git a/test/browser/custom-sample-resource.test.ts b/test/browser/custom-sample-resource.test.ts new file mode 100644 index 0000000..46f6f39 --- /dev/null +++ b/test/browser/custom-sample-resource.test.ts @@ -0,0 +1,203 @@ +import { expect, test } from 'vitest'; +import { + VideoSampleResource, + VideoSampleColorSpace, + VideoSample, + AudioSampleResource, + AudioSample, +} from '../../src/sample.js'; +import { toUint8Array } from '../../src/misc.js'; + +class ImageVideoSampleResource extends VideoSampleResource { + constructor(public image: HTMLImageElement | null) { + super(); + } + + getFormat(): VideoPixelFormat { + return 'RGBA'; + } + + getCodedWidth() { + return this.image!.width; + } + + getCodedHeight() { + return this.image!.height; + } + + getColorSpace() { + return new VideoSampleColorSpace({ + matrix: 'rgb', + primaries: 'bt709', + transfer: 'iec61966-2-1', + fullRange: true, + }); + } + + allocationSize() { + return this.image!.width * this.image!.height * 4; + } + + copyTo(destination: AllowSharedBufferSource): PlaneLayout[] { + const canvas = new OffscreenCanvas(this.image!.width, this.image!.height); + const ctx = canvas.getContext('2d')!; + + ctx.drawImage(this.image!, 0, 0); + + const imageData = ctx.getImageData(0, 0, this.image!.width, this.image!.height); + toUint8Array(destination).set(imageData.data); + + return [{ + offset: 0, + stride: this.image!.width * 4, + }]; + } + + close() { + this.image = null; + } +} + +test('Custom VideoSample resource usage', async () => { + const image = new Image(); + image.src = '/red-image.png'; + await image.decode(); + + const resource = new ImageVideoSampleResource(image); + const sample = new VideoSample(resource, { timestamp: 0 }); + expect(sample.format).toBe('RGBA'); + expect(sample.codedWidth).toBe(2048); + expect(sample.codedHeight).toBe(2048); + expect(sample.colorSpace).toEqual({ + matrix: 'rgb', + primaries: 'bt709', + transfer: 'iec61966-2-1', + fullRange: true, + }); + + const videoFrame = sample.toVideoFrame(); + const canvas = new OffscreenCanvas(1024, 1024); + const context = canvas.getContext('2d')!; + context.drawImage(videoFrame, 0, 0); + const imageData = context.getImageData(0, 0, 1024, 1024); + expect([...imageData.data.slice(0, 4)]).toEqual([255, 0, 0, 255]); + + videoFrame.close(); + + const clone = sample.clone(); + sample.close(); + expect(resource.image).not.toBe(null); + + clone.close(); + expect(resource.image).toBe(null); +}); + +test('Custom VideoSample resource, invalid construction', async () => { + const image = new Image(); + image.src = '/red-image.png'; + await image.decode(); + + const resource = new ImageVideoSampleResource(image); + // @ts-expect-error Wrong + expect(() => new VideoSample(resource)).toThrow(TypeError); + // @ts-expect-error Wrong + expect(() => new VideoSample(resource, { duration: 0 })).toThrow(TypeError); +}); + +class AudioBufferAudioSampleResource extends AudioSampleResource { + constructor( + public audioBuffer: AudioBuffer | null, + public timestamp: number, + ) { + super(); + } + + getFormat(): AudioSampleFormat { + return 'f32-planar'; + } + + getSampleRate() { + return this.audioBuffer!.sampleRate; + } + + getNumberOfFrames() { + return this.audioBuffer!.length; + } + + getNumberOfChannels() { + return this.audioBuffer!.numberOfChannels; + } + + getTimestamp() { + return this.timestamp; + } + + allocationSize() { + const frameCount = this.audioBuffer!.length; + return frameCount * Float32Array.BYTES_PER_ELEMENT; + } + + copyTo(destination: AllowSharedBufferSource, options: AudioDataCopyToOptions): void { + const frameCount = this.audioBuffer!.length; + const planeIndex = options.planeIndex; + + const channel = this.audioBuffer!.getChannelData(planeIndex); + + let destView: Float32Array; + if (destination instanceof ArrayBuffer) { + destView = new Float32Array(destination); + } else if (destination instanceof SharedArrayBuffer) { + destView = new Float32Array(destination); + } else { + destView = new Float32Array(destination.buffer, destination.byteOffset, destination.byteLength / 4); + } + + const frameOffset = options.frameOffset ?? 0; + const copyFrameCount = options.frameCount !== undefined ? options.frameCount : (frameCount - frameOffset); + + for (let i = 0; i < copyFrameCount; i++) { + destView[i] = channel[i + frameOffset]!; + } + } + + close() { + this.audioBuffer = null; + } +} + +test('Custom AudioSample resource usage', async () => { + const audioContext = new AudioContext(); + const audioBuffer = audioContext.createBuffer(2, 48000, 48000); + + const leftChannel = audioBuffer.getChannelData(0); + const rightChannel = audioBuffer.getChannelData(1); + for (let i = 0; i < 48000; i++) { + const t = i / 48000; + leftChannel[i] = Math.sin(2 * Math.PI * 440 * t); + rightChannel[i] = Math.cos(2 * Math.PI * 440 * t); + } + + const resource = new AudioBufferAudioSampleResource(audioBuffer, 0); + const sample = new AudioSample(resource); + + expect(sample.format).toBe('f32-planar'); + expect(sample.sampleRate).toBe(48000); + expect(sample.numberOfFrames).toBe(48000); + expect(sample.numberOfChannels).toBe(2); + expect(sample.timestamp).toBe(0); + expect(sample.duration).toBeCloseTo(1); + + const buffer = new ArrayBuffer(48000 * 4); + sample.copyTo(buffer, { planeIndex: 0 }); + + const view = new Float32Array(buffer); + expect(view[0]).toBeCloseTo(0, 4); + expect(view[13]).toBeCloseTo(Math.sin(2 * Math.PI * 440 * (13 / 48000))); + + const clone = sample.clone(); + sample.close(); + expect(resource.audioBuffer).not.toBe(null); + + clone.close(); + expect(resource.audioBuffer).toBe(null); +}); diff --git a/test/public/red-image.png b/test/public/red-image.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1b03c832b417499f83543014ad6579e6ef89cf GIT binary patch literal 68865 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 V4{hw@AK@Ez|J0(q-EL3K{{z5iMn3=m literal 0 HcmV?d00001