Skip to content
Permalink
Browse files
Add cropping support to WebCodecsVideoFrame copyTo
https://bugs.webkit.org/show_bug.cgi?id=246683
rdar://problem/101282697

Reviewed by Eric Carlson.

Implement cropping in copyTo as per https://w3c.github.io/webcodecs/#dom-videoframe-copyto step 8.4.
Beef up testing for RGBA, BGRA and NV12 as well as I420.

* LayoutTests/imported/w3c/web-platform-tests/webcodecs/videoFrame-copyTo.any-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/webcodecs/videoFrame-copyTo.any.js:
(makeRGBA_4x2):
(makeRGBA_2x4):
(makeBGRA_4x2):
(makeBGRA_2x4):
(makeNV12_2x4):
(makeI420_2x4):
(promise_test.async t):
* LayoutTests/imported/w3c/web-platform-tests/webcodecs/videoFrame-copyTo.any.worker-expected.txt:
* Source/WebCore/platform/graphics/cv/VideoFrameCV.mm:
(WebCore::copyPlane):
(WebCore::copyRGBData):
(WebCore::copyNV12):
(WebCore::copyI420):
(WebCore::VideoFrame::copyTo):

Canonical link: https://commits.webkit.org/255716@main
  • Loading branch information
youennf committed Oct 19, 2022
1 parent 284e48c commit 903dab21ab4f09d231bd7064153c712243aaa900
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 50 deletions.
@@ -13,6 +13,13 @@ PASS Test address overflow.
PASS Test codedRect.
PASS Test empty rect.
PASS Test unaligned rect.
FAIL Test left crop. assert_equals: buffer contents at index 0 expected 3 but got 1
PASS Test left crop I420.
PASS Test left crop NV12.
PASS Test left crop RGBA.
PASS Test left crop BGRA.
PASS Test top crop I420.
PASS Test top crop NV12.
PASS Test top crop RGBA.
PASS Test top crop BGRA.
PASS Test invalid rect.

@@ -15,6 +15,70 @@ function makeRGBA_2x2() {
return new VideoFrame(data, init);
}

function makeRGBA_4x2() {
const data = new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8,
9,10,11,12, 13,14,15,16,
21,22,23,24, 25,26,27,28,
29,30,31,32, 33,34,35,36,
]);
const init = {
format: 'RGBA',
timestamp: 0,
codedWidth: 4,
codedHeight: 2,
};
return new VideoFrame(data, init);
}

function makeRGBA_2x4() {
const data = new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8,
9,10,11,12, 13,14,15,16,
21,22,23,24, 25,26,27,28,
29,30,31,32, 33,34,35,36,
]);
const init = {
format: 'RGBA',
timestamp: 0,
codedWidth: 2,
codedHeight: 4,
};
return new VideoFrame(data, init);
}

function makeBGRA_4x2() {
const data = new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8,
9,10,11,12, 13,14,15,16,
21,22,23,24, 25,26,27,28,
29,30,31,32, 33,34,35,36,
]);
const init = {
format: 'BGRA',
timestamp: 0,
codedWidth: 4,
codedHeight: 2,
};
return new VideoFrame(data, init);
}

function makeBGRA_2x4() {
const data = new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8,
9,10,11,12, 13,14,15,16,
21,22,23,24, 25,26,27,28,
29,30,31,32, 33,34,35,36,
]);
const init = {
format: 'BGRA',
timestamp: 0,
codedWidth: 2,
codedHeight: 4,
};
return new VideoFrame(data, init);
}

const NV12_DATA = new Uint8Array([
1, 2, 3, 4, // y
5, 6, 7, 8,
@@ -31,6 +95,26 @@ function makeNV12_4x2() {
return new VideoFrame(NV12_DATA, init);
}

function makeNV12_2x4() {
const init = {
format: 'NV12',
timestamp: 0,
codedWidth: 2,
codedHeight: 4,
};
return new VideoFrame(NV12_DATA, init);
}

function makeI420_2x4() {
const init = {
format: 'I420',
timestamp: 0,
codedWidth: 2,
codedHeight: 4,
};
return new VideoFrame(NV12_DATA, init);
}

promise_test(async t => {
const frame = makeI420_4x2();
frame.close();
@@ -237,9 +321,154 @@ promise_test(async t => {
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);

assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test left crop I420.');

promise_test(async t => {
const frame = makeNV12_4x2();
const options = {
rect: {x: 2, y: 0, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 2},
{offset: 4, stride: 2},
];
const expectedData = new Uint8Array([
3, 4, // y
7, 8,
11, // u
12 // v
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test left crop NV12.');

promise_test(async t => {
const frame = makeRGBA_4x2();
const options = {
rect: {x: 2, y: 0, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 8},
];
const expectedData = new Uint8Array([
9, 10, 11, 12, 13, 14, 15 ,16,
29, 30, 31, 32, 33, 34, 35, 36
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test left crop RGBA.');

promise_test(async t => {
const frame = makeBGRA_4x2();
const options = {
rect: {x: 2, y: 0, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 8},
];
const expectedData = new Uint8Array([
9, 10, 11, 12, 13, 14, 15 ,16,
29, 30, 31, 32, 33, 34, 35, 36
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test left crop BGRA.');

promise_test(async t => {
const frame = makeI420_2x4();
const options = {
rect: {x: 0, y: 2, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 2},
{offset: 4, stride: 1},
{offset: 5, stride: 1},
];
const expectedData = new Uint8Array([
5, 6, // y
7, 8,
10, // u
12 // v
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);

assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test top crop I420.');

promise_test(async t => {
const frame = makeNV12_2x4();
const options = {
rect: {x: 0, y: 2, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 2},
{offset: 4, stride: 2},
];
const expectedData = new Uint8Array([
5, 6, // y
7, 8,
11, // u
12 // v
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test top crop NV12.');

promise_test(async t => {
const frame = makeRGBA_2x4();
const options = {
rect: {x: 0, y: 1, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 8},
];
const expectedData = new Uint8Array([
9, 10, 11, 12, 13, 14, 15 ,16,
21, 22, 23, 24, 25, 26, 27, 28
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test top crop RGBA.');

promise_test(async t => {
const frame = makeBGRA_2x4();
const options = {
rect: {x: 0, y: 1, width: 2, height: 2},
};
const expectedLayout = [
{offset: 0, stride: 8},
];
const expectedData = new Uint8Array([
9, 10, 11, 12, 13, 14, 15 ,16,
21, 22, 23, 24, 25, 26, 27, 28
]);
assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
const data = new Uint8Array(expectedData.length);
const layout = await frame.copyTo(data, options);
assert_layout_equals(layout, expectedLayout);
assert_buffer_equals(data, expectedData);
}, 'Test left crop.');
}, 'Test top crop BGRA.');

promise_test(async t => {
const frame = makeI420_4x2();
@@ -13,6 +13,13 @@ PASS Test address overflow.
PASS Test codedRect.
PASS Test empty rect.
PASS Test unaligned rect.
FAIL Test left crop. assert_equals: buffer contents at index 0 expected 3 but got 1
PASS Test left crop I420.
PASS Test left crop NV12.
PASS Test left crop RGBA.
PASS Test left crop BGRA.
PASS Test top crop I420.
PASS Test top crop NV12.
PASS Test top crop RGBA.
PASS Test top crop BGRA.
PASS Test invalid rect.

0 comments on commit 903dab2

Please sign in to comment.