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-canvas.window.html - WPT Dashboard Interop Dashboard
// META: script=/webcodecs/videoFrame-utils.js
promise_test(async t => {
const W = 5;
const H = 4;
const canvas = document.createElement('canvas');
canvas.width = W;
canvas.height = H;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#00ff00';
ctx.fillRect(0, 0, W, H);
const frame = new VideoFrame(canvas, {timestamp: 0});
t.add_cleanup(() => frame.close());
const format = frame.format;
assert_true(
format === 'RGBA' || format === 'RGBX' ||
format === 'BGRA' || format === 'BGRX',
`Expected an RGB(A/X) format, got ${format}`);
const size = frame.allocationSize();
const buf = new Uint8Array(size);
await frame.copyTo(buf);
const bpp = 4;
for (let row = 0; row < H; row++) {
for (let x = 0; x < W; x++) {
const i = (row * W + x) * bpp;
const pixel = [buf[i], buf[i + 1], buf[i + 2], buf[i + 3]];
// Green channel must be 255; red and blue must be 0.
// Alpha may be 255 for RGBA/BGRA or implementation-defined for X formats.
const rIdx = (format === 'BGRA' || format === 'BGRX') ? 2 : 0;
const gIdx = 1;
const bIdx = (format === 'BGRA' || format === 'BGRX') ? 0 : 2;
const aIdx = 3;
assert_equals(pixel[rIdx], 0,
`row ${row} col ${x}: red channel should be 0, got ${pixel[rIdx]}`);
assert_equals(pixel[gIdx], 255,
`row ${row} col ${x}: green channel should be 255, got ${pixel[gIdx]}`);
assert_equals(pixel[bIdx], 0,
`row ${row} col ${x}: blue channel should be 0, got ${pixel[bIdx]}`);
if (format === 'RGBA' || format === 'BGRA') {
assert_equals(pixel[aIdx], 255,
`row ${row} col ${x}: alpha should be 255, got ${pixel[aIdx]}`);
}
}
}
}, 'copyTo from canvas-backed VideoFrame returns correct pixels for all rows');
promise_test(async t => {
// Use a different non-aligned width to ensure this isn't width-specific.
const W = 3;
const H = 8;
const canvas = document.createElement('canvas');
canvas.width = W;
canvas.height = H;
const ctx = canvas.getContext('2d');
// Draw a distinct color per row so stride errors are easily detected.
for (let row = 0; row < H; row++) {
ctx.fillStyle = `rgb(${row * 30}, ${100 + row * 10}, ${200 - row * 20})`;
ctx.fillRect(0, row, W, 1);
}
const frame = new VideoFrame(canvas, {timestamp: 0});
t.add_cleanup(() => frame.close());
const format = frame.format;
const size = frame.allocationSize();
const buf = new Uint8Array(size);
await frame.copyTo(buf);
const bpp = 4;
// Read back canvas pixels for ground truth.
const imageData = ctx.getImageData(0, 0, W, H);
const canvasPixels = imageData.data;
for (let row = 0; row < H; row++) {
for (let x = 0; x < W; x++) {
const frameIdx = (row * W + x) * bpp;
const canvasIdx = (row * W + x) * 4;
const cR = canvasPixels[canvasIdx];
const cG = canvasPixels[canvasIdx + 1];
const cB = canvasPixels[canvasIdx + 2];
let fR, fG, fB;
if (format === 'BGRA' || format === 'BGRX') {
fB = buf[frameIdx];
fG = buf[frameIdx + 1];
fR = buf[frameIdx + 2];
} else {
fR = buf[frameIdx];
fG = buf[frameIdx + 1];
fB = buf[frameIdx + 2];
}
assert_approx_equals(fR, cR, 1,
`row ${row} col ${x}: R mismatch`);
assert_approx_equals(fG, cG, 1,
`row ${row} col ${x}: G mismatch`);
assert_approx_equals(fB, cB, 1,
`row ${row} col ${x}: B mismatch`);
}
}
}, 'copyTo from canvas-backed VideoFrame matches canvas pixel data per row');