146 lines
4.8 KiB
JavaScript
146 lines
4.8 KiB
JavaScript
const I420_DATA = new Uint8Array([
|
|
1, 2, 3, 4, // y
|
|
5, 6, 7, 8,
|
|
9, 10, // u
|
|
11, 12, // v
|
|
]);
|
|
|
|
function makeI420_4x2() {
|
|
const init = {
|
|
format: 'I420',
|
|
timestamp: 0,
|
|
codedWidth: 4,
|
|
codedHeight: 2,
|
|
};
|
|
return new VideoFrame(I420_DATA, init);
|
|
}
|
|
|
|
function testBufferConstructedI420Frame(bufferType) {
|
|
let fmt = 'I420';
|
|
let vfInit = {format: fmt, timestamp: 1234, codedWidth: 4, codedHeight: 2};
|
|
|
|
let buffer;
|
|
if (bufferType == 'SharedArrayBuffer' ||
|
|
bufferType == 'Uint8Array(SharedArrayBuffer)') {
|
|
buffer = new SharedArrayBuffer(I420_DATA.length);
|
|
} else {
|
|
assert_true(bufferType == 'ArrayBuffer' ||
|
|
bufferType == 'Uint8Array(ArrayBuffer)');
|
|
buffer = new ArrayBuffer(I420_DATA.length);
|
|
}
|
|
let bufferView = new Uint8Array(buffer);
|
|
bufferView.set(I420_DATA);
|
|
let data = bufferType.startsWith('Uint8Array') ? bufferView : buffer;
|
|
|
|
let frame = new VideoFrame(data, vfInit);
|
|
assert_equals(frame.format, fmt, 'plane format');
|
|
assert_equals(frame.colorSpace.primaries, 'bt709', 'color primaries');
|
|
assert_equals(frame.colorSpace.transfer, 'bt709', 'color transfer');
|
|
assert_equals(frame.colorSpace.matrix, 'bt709', 'color matrix');
|
|
assert_false(frame.colorSpace.fullRange, 'color range');
|
|
frame.close();
|
|
|
|
let y = {offset: 0, stride: 4};
|
|
let u = {offset: 8, stride: 2};
|
|
let v = {offset: 10, stride: 2};
|
|
|
|
assert_throws_js(TypeError, () => {
|
|
let y = {offset: 0, stride: 1};
|
|
let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
|
|
}, 'y stride too small');
|
|
assert_throws_js(TypeError, () => {
|
|
let u = {offset: 8, stride: 1};
|
|
let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
|
|
}, 'u stride too small');
|
|
assert_throws_js(TypeError, () => {
|
|
let v = {offset: 10, stride: 1};
|
|
let frame = new VideoFrame(data, {...vfInit, layout: [y, u, v]});
|
|
}, 'v stride too small');
|
|
assert_throws_js(TypeError, () => {
|
|
let frame = new VideoFrame(data.slice(0, 8), vfInit);
|
|
}, 'data too small');
|
|
}
|
|
|
|
function assert_buffer_equals(actual, expected) {
|
|
assert_true(expected instanceof Uint8Array, 'actual instanceof Uint8Array');
|
|
|
|
if (actual instanceof ArrayBuffer ||
|
|
(typeof(SharedArrayBuffer) != 'undefined' &&
|
|
actual instanceof SharedArrayBuffer)) {
|
|
actual = new Uint8Array(actual);
|
|
} else {
|
|
assert_true(actual instanceof Uint8Array,
|
|
'expected instanceof Uint8Array, ArrayBuffer, or SharedArrayBuffer');
|
|
}
|
|
|
|
assert_equals(actual.length, expected.length, 'buffer length');
|
|
for (let i = 0; i < actual.length; i++) {
|
|
if (actual[i] != expected[i]) {
|
|
assert_equals(actual[i], expected[i], 'buffer contents at index ' + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
function assert_layout_equals(actual, expected) {
|
|
assert_equals(actual.length, expected.length, 'layout planes');
|
|
for (let i = 0; i < actual.length; i++) {
|
|
assert_object_equals(actual[i], expected[i], 'plane ' + i + ' layout');
|
|
}
|
|
}
|
|
|
|
async function testI420_4x2_copyTo(destination) {
|
|
const frame = makeI420_4x2();
|
|
const expectedLayout = [
|
|
{offset: 0, stride: 4},
|
|
{offset: 8, stride: 2},
|
|
{offset: 10, stride: 2},
|
|
];
|
|
const expectedData = new Uint8Array([
|
|
1, 2, 3, 4, // y
|
|
5, 6, 7, 8,
|
|
9, 10, // u
|
|
11, 12 // v
|
|
]);
|
|
|
|
assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
|
|
const layout = await frame.copyTo(destination);
|
|
assert_layout_equals(layout, expectedLayout);
|
|
assert_buffer_equals(destination, expectedData);
|
|
}
|
|
|
|
function verifyTimestampRequiredToConstructFrame(imageSource) {
|
|
assert_throws_js(
|
|
TypeError,
|
|
() => new VideoFrame(imageSource),
|
|
'timestamp required to construct VideoFrame from this source');
|
|
let validFrame = new VideoFrame(imageSource, {timestamp: 0});
|
|
validFrame.close();
|
|
}
|
|
|
|
// Fills a visible region within `data` with the given YUV values.
|
|
function fillYUV(data, codedWidth, codedHeight, visibleRect, v_y, v_u, v_v) {
|
|
for (let y = 0; y < codedHeight; y++) {
|
|
for (let x = 0; x < codedWidth; x++) {
|
|
data[y * codedWidth + x] =
|
|
(x >= visibleRect.x && y < visibleRect.y + visibleRect.height) ? v_y
|
|
: 0;
|
|
}
|
|
}
|
|
const uvWidth = codedWidth / 2;
|
|
const uvHeight = codedHeight / 2;
|
|
const uvPlaneSize = uvWidth * uvHeight;
|
|
const uvOffset = codedWidth * codedHeight;
|
|
const uvX = visibleRect.x / 2;
|
|
const uvMaxY = (visibleRect.y + visibleRect.height) / 2;
|
|
for (let y = 0; y < uvHeight; y++) {
|
|
for (let x = 0; x < uvWidth; x++) {
|
|
if (x >= uvX && y < uvMaxY) {
|
|
data[uvOffset + y * uvWidth + x] = v_u;
|
|
data[uvOffset + uvPlaneSize + y * uvWidth + x] = v_v;
|
|
} else {
|
|
data[uvOffset + y * uvWidth + x] =
|
|
data[uvOffset + uvPlaneSize + y * uvWidth + x] = 128;
|
|
}
|
|
}
|
|
}
|
|
}
|