mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 19:03:46 +02:00
Loosen constraint on format returned by toRgbSample, fix X->A conversio, fix tests
This commit is contained in:
@@ -5,8 +5,10 @@ import {
|
||||
VideoSample,
|
||||
AudioSampleResource,
|
||||
AudioSample,
|
||||
VideoDataPlane,
|
||||
VideoSampleInit,
|
||||
} from '../../src/sample.js';
|
||||
import { toUint8Array } from '../../src/misc.js';
|
||||
import { MaybePromise, SetRequired, toUint8Array } from '../../src/misc.js';
|
||||
|
||||
class ImageVideoSampleResource extends VideoSampleResource {
|
||||
constructor(public image: HTMLImageElement | null) {
|
||||
@@ -42,25 +44,30 @@ class ImageVideoSampleResource extends VideoSampleResource {
|
||||
});
|
||||
}
|
||||
|
||||
allocationSize() {
|
||||
return this.image!.width * this.image!.height * 4;
|
||||
}
|
||||
|
||||
copyTo(destination: AllowSharedBufferSource): PlaneLayout[] {
|
||||
getDataPlanes(): MaybePromise<VideoDataPlane[]> {
|
||||
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,
|
||||
data: toUint8Array(imageData.data),
|
||||
stride: this.image!.width * 4,
|
||||
}];
|
||||
}
|
||||
|
||||
toRgbSample(
|
||||
init: SetRequired<VideoSampleInit, 'timestamp'>,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
format: 'RGBA' | 'RGBX' | 'BGRA' | 'BGRX',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
colorSpace: PredefinedColorSpace,
|
||||
): MaybePromise<VideoSample> {
|
||||
return new VideoSample(this, init);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.image = null;
|
||||
}
|
||||
@@ -92,6 +99,15 @@ test('Custom VideoSample resource usage', async () => {
|
||||
|
||||
videoFrame.close();
|
||||
|
||||
const size = sample.allocationSize();
|
||||
expect(size).toBe(2048 * 2048 * 4);
|
||||
|
||||
const buf = new ArrayBuffer(size);
|
||||
await sample.copyTo(buf);
|
||||
await sample.copyTo(buf, { format: 'RGBX' });
|
||||
await sample.copyTo(buf, { format: 'BGRA' });
|
||||
await sample.copyTo(buf, { format: 'BGRX' });
|
||||
|
||||
const clone = sample.clone();
|
||||
sample.close();
|
||||
expect(resource.image).not.toBe(null);
|
||||
@@ -140,32 +156,9 @@ class AudioBufferAudioSampleResource extends AudioSampleResource {
|
||||
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]!;
|
||||
}
|
||||
getDataPlane(planeIndex: number): Uint8Array {
|
||||
const data = this.audioBuffer!.getChannelData(planeIndex);
|
||||
return toUint8Array(data);
|
||||
}
|
||||
|
||||
close() {
|
||||
@@ -195,6 +188,11 @@ test('Custom AudioSample resource usage', async () => {
|
||||
expect(sample.timestamp).toBe(0);
|
||||
expect(sample.duration).toBeCloseTo(1);
|
||||
|
||||
const size1 = sample.allocationSize({ planeIndex: 0 });
|
||||
const size2 = sample.allocationSize({ planeIndex: 1 });
|
||||
expect(size1).toBe(48000 * 4);
|
||||
expect(size2).toBe(48000 * 4);
|
||||
|
||||
const buffer = new ArrayBuffer(48000 * 4);
|
||||
sample.copyTo(buffer, { planeIndex: 0 });
|
||||
|
||||
|
||||
@@ -287,6 +287,52 @@ test('RGB conversion for ArrayBuffer-backed data', async () => {
|
||||
|
||||
expect(Array.from(dest)).toEqual(Array.from(src));
|
||||
}
|
||||
|
||||
{
|
||||
// RGBX -> RGBA: X (whatever it is) becomes 255 to mean A
|
||||
const xSrc = new Uint8Array([
|
||||
10, 20, 30, 0, 40, 50, 60, 77,
|
||||
70, 80, 90, 128, 100, 110, 120, 200,
|
||||
]);
|
||||
|
||||
using sample = new VideoSample(xSrc.slice(), {
|
||||
timestamp: 0,
|
||||
codedWidth: 2,
|
||||
codedHeight: 2,
|
||||
format: 'RGBX',
|
||||
});
|
||||
|
||||
const dest = new Uint8Array(2 * 2 * 4);
|
||||
await sample.copyTo(dest, { format: 'RGBA' });
|
||||
|
||||
expect(Array.from(dest)).toEqual([
|
||||
10, 20, 30, 255, 40, 50, 60, 255,
|
||||
70, 80, 90, 255, 100, 110, 120, 255,
|
||||
]);
|
||||
}
|
||||
|
||||
{
|
||||
// RGBX -> BGRA: R and B swap, X becomes 255 to mean A
|
||||
const xSrc = new Uint8Array([
|
||||
10, 20, 30, 0, 40, 50, 60, 77,
|
||||
70, 80, 90, 128, 100, 110, 120, 200,
|
||||
]);
|
||||
|
||||
using sample = new VideoSample(xSrc.slice(), {
|
||||
timestamp: 0,
|
||||
codedWidth: 2,
|
||||
codedHeight: 2,
|
||||
format: 'RGBX',
|
||||
});
|
||||
|
||||
const dest = new Uint8Array(2 * 2 * 4);
|
||||
await sample.copyTo(dest, { format: 'BGRA' });
|
||||
|
||||
expect(Array.from(dest)).toEqual([
|
||||
30, 20, 10, 255, 60, 50, 40, 255,
|
||||
90, 80, 70, 255, 120, 110, 100, 255,
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
test('crop (rect option) for ArrayBuffer-backed data', async () => {
|
||||
|
||||
Reference in New Issue
Block a user