Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /webcodecs/videoFrame-copyTo-rgbx.any.html - WPT Dashboard Interop Dashboard
- /webcodecs/videoFrame-copyTo-rgbx.any.worker.html - WPT Dashboard Interop Dashboard
// META: global=window,dedicatedworker
// Each pixel has distinct RGB values and a distinct fourth byte, so that any
// byte that is dropped, replaced or moved shows up in the copy.
const kWidth = 4;
const kHeight = 2;
const kPixels = [
{r: 255, g: 0, b: 0, x: 0x00},
{r: 0, g: 255, b: 0, x: 0x40},
{r: 0, g: 0, b: 255, x: 0x7f},
{r: 255, g: 255, b: 255, x: 0xff},
{r: 0, g: 0, b: 0, x: 0x01},
{r: 10, g: 200, b: 90, x: 0x80},
{r: 200, g: 100, b: 30, x: 0xfe},
{r: 128, g: 64, b: 192, x: 0x33},
];
function makeData(format) {
const data = new Uint8Array(kWidth * kHeight * 4);
kPixels.forEach(({r, g, b, x}, i) => {
const bgr = format.startsWith('BGR');
data[4 * i + 0] = bgr ? b : r;
data[4 * i + 1] = g;
data[4 * i + 2] = bgr ? r : b;
data[4 * i + 3] = x;
});
return data;
}
function makeFrame(format, data) {
return new VideoFrame(data, {
format: format,
codedWidth: kWidth,
codedHeight: kHeight,
timestamp: 0,
});
}
function validateArrays(original, copy)
{
assert_equals(original.byteLength, copy.byteLength, "array length");
for (let i = 0;i < original.byteLength;i++) {
assert_equals(copy[i], (i % 4) !== 3 ? original[i] : 255);
}
}
for (const format of ['RGBX', 'BGRX']) {
promise_test(async t => {
const data = makeData(format);
const frame = makeFrame(format, data);
t.add_cleanup(() => frame.close());
assert_equals(frame.format, format);
// Copying without a format option keeps the format of the frame.
const size = frame.allocationSize();
assert_equals(size, data.byteLength);
const copy = new Uint8Array(size);
const layout = await frame.copyTo(copy);
assert_equals(layout.length, 1);
assert_equals(layout[0].stride, kWidth * 4);
validateArrays(data, copy);
}, `copyTo() of a ${format} VideoFrame`);
promise_test(async t => {
const data = makeData(format);
const frame = makeFrame(format, data);
t.add_cleanup(() => frame.close());
// Explicitly requesting the format of the frame gives the same bytes.
const options = {format: format};
const copy = new Uint8Array(frame.allocationSize(options));
await frame.copyTo(copy, options);
validateArrays(data, copy);
}, `copyTo() of a ${format} VideoFrame to ${format}`);
promise_test(async t => {
const data = makeData(format);
const frame = makeFrame(format, data);
t.add_cleanup(() => frame.close());
// A copy of the frame keeps the fourth byte as well.
const clone = new VideoFrame(frame);
t.add_cleanup(() => clone.close());
assert_equals(clone.format, format);
const copy = new Uint8Array(clone.allocationSize());
await clone.copyTo(copy);
validateArrays(data, copy);
}, `copyTo() of a clone of a ${format} VideoFrame`);
}
// An RGBX or BGRX frame is opaque: copying it to a format with alpha must give opaque pixels
for (const [from, to, tolerance] of [
['BGRX', 'BGRA', 0], ['RGBX', 'RGBA', 0], ['BGRX', 'RGBA', 1],
['RGBX', 'BGRA', 1]]) {
promise_test(async t => {
// The padding byte of the pixels is not 0xff for most of them.
const frame = makeFrame(from, makeData(from));
t.add_cleanup(() => frame.close());
const options = {format: to};
const copy = new Uint8Array(frame.allocationSize(options));
const layout = await frame.copyTo(copy, options);
assert_equals(layout.length, 1);
const expected = makeData(to);
assert_equals(copy.byteLength, expected.byteLength, 'array length');
for (let i = 0; i < copy.byteLength; i++) {
if (i % 4 === 3) {
assert_equals(copy[i], 255, `alpha of pixel ${(i - 3) / 4}`);
} else {
assert_approx_equals(
copy[i], expected[i], tolerance, `byte ${i} of the copy`);
}
}
}, `copyTo() of a ${from} VideoFrame to ${to} gives opaque pixels`);
}