blob: 71a73c4ee3455001105f9a5d28d3e022246af018 [file] [log] [blame]
// META: global=window,dedicatedworker
// META: script=/webcodecs/utils.js
test(t => {
let image = makeImageBitmap(32, 16);
let frame = new VideoFrame(image, {timestamp: 10});
assert_equals(frame.timestamp, 10, "timestamp");
assert_equals(frame.duration, null, "duration");
assert_equals(frame.cropWidth, 32, "cropWidth");
assert_equals(frame.cropHeight, 16, "cropHeight");
assert_equals(frame.cropWidth, 32, "displayWidth");
assert_equals(frame.cropHeight, 16, "displayHeight");
frame.close();
}, 'Test we can construct a VideoFrame.');
test(t => {
let image = makeImageBitmap(1, 1);
let frame = new VideoFrame(image, {timestamp: 10});
assert_equals(frame.cropWidth, 1, "cropWidth");
assert_equals(frame.cropHeight, 1, "cropHeight");
assert_equals(frame.cropWidth, 1, "displayWidth");
assert_equals(frame.cropHeight, 1, "displayHeight");
frame.close();
}, 'Test we can construct an odd-sized VideoFrame.');
test(t => {
let image = makeImageBitmap(32, 16);
let frame = new VideoFrame(image, {timestamp: 0});
// TODO(sandersd): This would be more clear as RGBA, but conversion has
// not be specified (or implemented) yet.
if (frame.format !== "I420") {
return;
}
assert_equals(frame.planes.length, 3, "number of planes");
// Validate Y plane metadata.
let yPlane = frame.planes[0];
let yStride = yPlane.stride;
let yRows = yPlane.rows;
let yLength = yPlane.length;
// Required minimums to contain the visible data.
assert_greater_than_equal(yRows, 16, "Y plane rows");
assert_greater_than_equal(yStride, 32, "Y plane stride");
assert_greater_than_equal(yLength, 32 * 16, "Y plane length");
// Not required by spec, but sets limit at 50% padding per dimension.
assert_less_than_equal(yRows, 32, "Y plane rows");
assert_less_than_equal(yStride, 64, "Y plane stride");
assert_less_than_equal(yLength, 32 * 64, "Y plane length");
// Validate Y plane data.
let buffer = new ArrayBuffer(yLength);
let view = new Uint8Array(buffer);
frame.planes[0].readInto(view);
// TODO(sandersd): This probably needs to be fuzzy unless we can make
// guarantees about the color space.
assert_equals(view[0], 94, "Y value at (0, 0)");
frame.close();
}, 'Test we can read planar data from a VideoFrame.');
test(t => {
let image = makeImageBitmap(32, 16);
let frame = new VideoFrame(image, {timestamp: 0});
// TODO(sandersd): This would be more clear as RGBA, but conversion has
// not be specified (or implemented) yet.
if (frame.format !== "I420") {
return;
}
assert_equals(frame.planes.length, 3, "number of planes");
// Attempt to read Y plane data, but close the frame first.
let yPlane = frame.planes[0];
let yLength = yPlane.length;
frame.close();
let buffer = new ArrayBuffer(yLength);
let view = new Uint8Array(buffer);
assert_throws_dom("InvalidStateError", () => yPlane.readInto(view));
}, 'Test we cannot read planar data from a closed VideoFrame.');
test(t => {
let image = makeImageBitmap(32, 16);
image.close();
assert_throws_dom("InvalidStateError", () => {
let frame = new VideoFrame(image, {timestamp: 10});
})
}, 'Test constructing VideoFrames from closed ImageBitmap throws.');
test(t => {
let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
assert_throws_js(TypeError, () => {
let frame = new VideoFrame('ABCD', [], vfInit);
}, 'invalid pixel format');
assert_throws_dom('ConstraintError', () => {
let frame = new VideoFrame('ARGB', [], {timestamp: 1234});
}, 'missing coded size');
function constructFrame(init) {
let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); // 4x2
let uPlaneData = new Uint8Array([1, 2]); // 2x1
let yPlane = {src: yPlaneData, stride: 4, rows: 2};
let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
let frame = new VideoFrame('I420', [yPlane, uPlane, vPlane], init);
}
assert_throws_dom(
'ConstraintError', () => {constructFrame({
timestamp: 1234,
codedWidth: 1 << 32 - 1,
codedHeight: 1 << 32 - 1
})},
'invalid coded size');
assert_throws_dom(
'ConstraintError',
() => {constructFrame({timestamp: 1234, codedWidth: 4, codedHeight: 0})},
'invalid coded height');
assert_throws_dom(
'ConstraintError',
() => {constructFrame({timestamp: 1234, codedWidth: 0, codedHeight: 4})},
'invalid coded width');
assert_throws_dom(
'ConstraintError', () => {constructFrame({
timestamp: 1234,
codedWidth: 4,
codedHeight: 2,
cropLeft: 100,
cropRight: 100
})},
'invalid crop left/right');
assert_throws_dom(
'ConstraintError',
() => {constructFrame(
{timestamp: 1234, codedWidth: 4, codedHeight: 2, cropWidth: 0})},
'invalid crop width');
assert_throws_dom(
'ConstraintError',
() => {constructFrame(
{timestamp: 1234, codedWidth: 4, codedHeight: 2, cropHeight: 0})},
'invalid crop height');
assert_throws_dom(
'ConstraintError', () => {constructFrame({
timestamp: 1234,
codedWidth: 4,
codedHeight: 2,
cropHeight: -1,
cropWidth: -100
})},
'invalid negative crop');
assert_throws_dom(
'ConstraintError', () => {constructFrame({
timestamp: 1234,
codedWidth: 4,
codedHeight: 2,
displayWidth: 1 << 32 - 1
})},
'invalid display width');
assert_throws_dom(
'ConstraintError', () => {constructFrame({
timestamp: 1234,
codedWidth: 4,
codedHeight: 2,
displayWidth: 1 << 32 - 1,
displayHeight: 1 << 32
})},
'invalid display height');
}, 'Test invalid planar constructed VideoFrames');
test(t => {
let fmt = 'I420';
let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); // 4x2
let uPlaneData = new Uint8Array([1, 2]); // 2x1
let yPlane = {src: yPlaneData, stride: 4, rows: 2};
let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
let frame = new VideoFrame(fmt, [yPlane, uPlane, vPlane], vfInit);
assert_equals(frame.planes.length, 3, 'plane count');
assert_equals(frame.format, fmt, 'plane format');
verifyPlane(yPlane, frame.planes[0]);
verifyPlane(uPlane, frame.planes[1]);
verifyPlane(vPlane, frame.planes[2]);
frame.close();
assert_throws_dom('ConstraintError', () => {
let frame = new VideoFrame(fmt, [yPlane, uPlane], vfInit);
}, 'too few planes');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.stride = 1;
let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
}, 'y stride too small');
assert_throws_dom('ConstraintError', () => {
let badUPlane = {...uPlane};
badUPlane.stride = 1;
let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
}, 'u stride too small');
assert_throws_dom('ConstraintError', () => {
let badVPlane = {...vPlane};
badVPlane.stride = 1;
let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
}, 'v stride too small');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.rows = 1;
let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
}, 'y height too small');
assert_throws_dom('ConstraintError', () => {
let badUPlane = {...uPlane};
badUPlane.rows = 0;
let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
}, 'u height too small');
assert_throws_dom('ConstraintError', () => {
let badVPlane = {...vPlane};
badVPlane.rows = 0;
let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
}, 'v height too small');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.rows = 100;
let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
}, 'y height too large');
assert_throws_dom('ConstraintError', () => {
let badUPlane = {...uPlane};
badUPlane.rows = 100;
let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
}, 'u height too large');
assert_throws_dom('ConstraintError', () => {
let badVPlane = {...vPlane};
badVPlane.rows = 100;
let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
}, 'v height too large');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.src = yPlaneData.slice(1, 4);
let frame = new VideoFrame(fmt, [badYPlane, uPlane, vPlane], vfInit);
}, 'y plane size too small');
assert_throws_dom('ConstraintError', () => {
let badUPlane = {...uPlane};
badUPlane.src = uPlaneData.slice(1, 1);
let frame = new VideoFrame(fmt, [yPlane, badUPlane, vPlane], vfInit);
}, 'u plane size too small');
assert_throws_dom('ConstraintError', () => {
let badVPlane = {...vPlane};
badVPlane.src = uPlaneData.slice(1, 1);
let frame = new VideoFrame(fmt, [yPlane, uPlane, badVPlane], vfInit);
}, 'v plane size too small');
}, 'Test planar constructed I420 VideoFrame');
test(t => {
let fmt = 'I420';
let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); // 4x2
let uPlaneData = new Uint8Array([1, 2]); // 2x1
let yPlane = {src: yPlaneData, stride: 4, rows: 2};
let uPlane = vPlane = {src: uPlaneData, stride: 2, rows: 1};
let aPlaneData = yPlaneData.reverse();
let aPlane = {src: aPlaneData, stride: 4, rows: 2};
let frame = new VideoFrame(fmt, [yPlane, uPlane, vPlane, aPlane], vfInit);
assert_equals(frame.planes.length, 4, 'plane count');
assert_equals(frame.format, fmt, 'plane format');
verifyPlane(yPlane, frame.planes[0]);
verifyPlane(uPlane, frame.planes[1]);
verifyPlane(vPlane, frame.planes[2]);
verifyPlane(aPlane, frame.planes[3]);
frame.close();
// Most constraints are tested as part of I420 above.
assert_throws_dom('ConstraintError', () => {
let badAPlane = {...aPlane};
badAPlane.stride = 1;
let frame =
new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
}, 'a stride too small');
assert_throws_dom('ConstraintError', () => {
let badAPlane = {...aPlane};
badAPlane.rows = 1;
let frame =
new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
}, 'a height too small');
assert_throws_dom('ConstraintError', () => {
let badAPlane = {...aPlane};
badAPlane.rows = 100;
let frame =
new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
}, 'a height too large');
assert_throws_dom('ConstraintError', () => {
let badAPlane = {...yPlane};
badAPlane.src = aPlaneData.slice(1, 4);
let frame =
new VideoFrame(fmt, [yPlane, uPlane, vPlane, badAPlane], vfInit);
}, 'a plane size too small');
}, 'Test planar constructed I420+Alpha VideoFrame');
test(t => {
let fmt = 'NV12';
let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
let yPlaneData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); // 4x2
let yPlane = {src: yPlaneData, stride: 4, rows: 2};
let uvPlaneData = new Uint8Array([1, 2, 3, 4]);
let uvPlane = {src: uvPlaneData, stride: 4, rows: 1};
let frame = new VideoFrame(fmt, [yPlane, uvPlane], vfInit);
assert_equals(frame.planes.length, 2, 'plane count');
assert_equals(frame.format, fmt, 'plane format');
verifyPlane(yPlane, frame.planes[0]);
verifyPlane(uvPlane, frame.planes[1]);
frame.close();
assert_throws_dom('ConstraintError', () => {
let frame = new VideoFrame(fmt, [yPlane], vfInit);
}, 'too few planes');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.stride = 1;
let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
}, 'y stride too small');
assert_throws_dom('ConstraintError', () => {
let badUVPlane = {...uvPlane};
badUVPlane.stride = 2;
let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
}, 'uv stride too small');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.rows = 1;
let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
}, 'y height too small');
assert_throws_dom('ConstraintError', () => {
let badUVPlane = {...uvPlane};
badUVPlane.rows = 0;
let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
}, 'uv height too small');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.rows = 100;
let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
}, 'y height too large');
assert_throws_dom('ConstraintError', () => {
let badUVPlane = {...uvPlane};
badUVPlane.rows = 100;
let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
}, 'u height too large');
assert_throws_dom('ConstraintError', () => {
let badYPlane = {...yPlane};
badYPlane.src = yPlaneData.slice(1, 4);
let frame = new VideoFrame(fmt, [badYPlane, uvPlane], vfInit);
}, 'y plane size too small');
assert_throws_dom('ConstraintError', () => {
let badUVPlane = {...uvPlane};
badUVPlane.src = uvPlaneData.slice(1, 1);
let frame = new VideoFrame(fmt, [yPlane, badUVPlane], vfInit);
}, 'u plane size too small');
}, 'Test planar constructed NV12 VideoFrame');
test(t => {
let vfInit = {timestamp: 1234, codedWidth: 4, codedHeight: 2};
let argbPlaneData =
new Uint8Array(new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer);
let argbPlane = {src: argbPlaneData, stride: 4 * 4, rows: 2};
let frame = new VideoFrame('ABGR', [argbPlane], vfInit);
assert_equals(frame.planes.length, 1, 'plane count');
assert_equals(frame.format, 'ABGR', 'plane format');
verifyPlane(argbPlane, frame.planes[0]);
frame.close();
frame = new VideoFrame('ARGB', [argbPlane], vfInit);
assert_equals(frame.planes.length, 1, 'plane count');
assert_equals(frame.format, 'ARGB', 'plane format');
verifyPlane(argbPlane, frame.planes[0]);
frame.close();
frame = new VideoFrame('XBGR', [argbPlane], vfInit);
assert_equals(frame.planes.length, 1, 'plane count');
assert_equals(frame.format, 'XBGR', 'plane format');
verifyPlane(argbPlane, frame.planes[0]);
frame.close();
frame = new VideoFrame('XRGB', [argbPlane], vfInit);
assert_equals(frame.planes.length, 1, 'plane count');
assert_equals(frame.format, 'XRGB', 'plane format');
verifyPlane(argbPlane, frame.planes[0]);
frame.close();
['ABGR', 'ARGB', 'XBGR', 'XRGB'].forEach(fmt => {
assert_throws_dom('ConstraintError', () => {
let frame = new VideoFrame(fmt, [], vfInit);
}, fmt + ': too few planes');
assert_throws_dom('ConstraintError', () => {
let badARGBPlane = {...argbPlane};
badARGBPlane.stride = 1;
let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
}, fmt + ': stride too small');
assert_throws_dom('ConstraintError', () => {
let badARGBPlane = {...argbPlane};
badARGBPlane.rows = 1;
let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
}, fmt + ': height too small');
assert_throws_dom('ConstraintError', () => {
let badARGBPlane = {...argbPlane};
badARGBPlane.rows = 100;
let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
}, fmt + ': height too large');
assert_throws_dom('ConstraintError', () => {
let badARGBPlane = {...argbPlane};
badARGBPlane.src = argbPlaneData.slice(1, 4);
let frame = new VideoFrame(fmt, [badARGBPlane], vfInit);
}, fmt + ': plane size too small');
});
}, 'Test planar constructed RGB VideoFrames');
test(t => {
let image = makeImageBitmap(32, 16);
let frame = new VideoFrame(image, {timestamp: 0});
assert_true(!!frame);
frame_copy = new VideoFrame(frame);
assert_equals(frame.format, frame_copy.format);
assert_equals(frame.timestamp, frame_copy.timestamp);
assert_equals(frame.codedWidth, frame_copy.codedWidth);
assert_equals(frame.codedHeight, frame_copy.codedHeight);
assert_equals(frame.displayWidth, frame_copy.displayWidth);
assert_equals(frame.displayHeight, frame_copy.displayHeight);
assert_equals(frame.duration, frame_copy.duration);
frame_copy.close();
frame_copy = new VideoFrame(frame, {duration: 1234});
assert_equals(frame.timestamp, frame_copy.timestamp);
assert_equals(frame_copy.duration, 1234);
frame_copy.close();
frame_copy = new VideoFrame(frame, {timestamp: 1234, duration: 456});
assert_equals(frame_copy.timestamp, 1234);
assert_equals(frame_copy.duration, 456);
frame_copy.close();
frame.close();
}, 'Test VideoFrame constructed VideoFrame');