| <!DOCTYPE html> |
| <html> |
| <title>Test the VideoDecoder API.</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script> |
| 'use strict'; |
| |
| // TODO(sandersd): Move metadata into a helper library. |
| // TODO(sandersd): Add H.264 idecode test once there is an API to query for |
| // supported codecs. |
| let h264 = { |
| buffer: fetch('h264.mp4').then(r => r.arrayBuffer()), |
| codec: "avc1.64000c", |
| description: {offset: 7229, size: 46}, |
| frames: [{offset: 48, size: 4007}, |
| {offset: 4055, size: 926}, |
| {offset: 4981, size: 241}, |
| {offset: 5222, size: 97}, |
| {offset: 5319, size: 98}, |
| {offset: 5417, size: 624}, |
| {offset: 6041, size: 185}, |
| {offset: 6226, size: 94}, |
| {offset: 6320, size: 109}, |
| {offset: 6429, size: 281}] |
| }; |
| |
| let vp9 = { |
| buffer: fetch('vp9.mp4').then(r => r.arrayBuffer()), |
| // TODO(sandersd): Verify that the file is actually level 1. |
| codec: "vp09.00.10.08", |
| frames: [{offset: 44, size: 3315}, |
| {offset: 3359, size: 203}, |
| {offset: 3562, size: 245}, |
| {offset: 3807, size: 172}, |
| {offset: 3979, size: 312}, |
| {offset: 4291, size: 170}, |
| {offset: 4461, size: 195}, |
| {offset: 4656, size: 181}, |
| {offset: 4837, size: 356}, |
| {offset: 5193, size: 159}] |
| }; |
| |
| function view(buffer, {offset, size}) { |
| return new Uint8Array(buffer, offset, size); |
| } |
| |
| // Calls done after giving async output/error callbacks a final chance to run. |
| async function asyncDone(test) { |
| test.step_timeout(test.step_func_done(), 0); |
| } |
| |
| async_test(async (t) => { |
| // VideoDecoderInit lacks required fields. |
| assert_throws_js(TypeError, () => { new VideoDecoder({}); }); |
| |
| // VideoDecoderInit has required fields. |
| let decoder = new VideoDecoder({ |
| output(chunk) { t.unreached_func("Unexpected output").call(); }, |
| error(error) { t.unreached_func("Unexpected error:" + error).call(); }, |
| }); |
| |
| asyncDone(t); |
| }, 'Test VideoDecoder construction'); |
| |
| async_test(async (t) => { |
| let decoder = new VideoDecoder({ |
| output(chunk) { t.unreached_func("Unexpected output").call(); }, |
| error(error) { t.unreached_func("Unexpected error:" + error).call(); }, |
| }); |
| |
| |
| let config = {}; |
| |
| // Bogus codec rejected. |
| config.codec = 'bogus'; |
| assert_throws_js(TypeError, () => { decoder.configure(config); }); |
| |
| // Audio codec rejected. |
| config.codec = 'vorbis'; |
| assert_throws_js(TypeError, () => { decoder.configure(config); }); |
| |
| // Ambiguous codec rejected. |
| config.codec = 'vp9'; |
| assert_throws_js(TypeError, () => { decoder.configure(config); }); |
| |
| // Codec with mime type rejected. |
| config.codec = 'video/webm; codecs="vp9"'; |
| assert_throws_js(TypeError, () => { decoder.configure(config); }); |
| |
| // Valid unambiguous codec accepted. |
| config.codec = 'vp09.00.10.08'; |
| decoder.configure(config); |
| |
| asyncDone(t); |
| }, 'Test VideoDecoder.configure() codec validity'); |
| |
| promise_test(t => vp9.buffer.then(buffer => { |
| let numOutputs = 0; |
| let decoder = new VideoDecoder({ |
| output(frame) { |
| t.step(() => { |
| assert_equals(++numOutputs, 1, "outputs"); |
| assert_equals(frame.cropWidth, 320, "cropWidth"); |
| assert_equals(frame.cropHeight, 240, "cropHeight"); |
| assert_equals(frame.timestamp, 0, "timestamp"); |
| frame.destroy(); |
| }); |
| }, |
| error(e) { |
| t.step(() => { |
| // TODO(sandersd): Change to 'throw e' once e is defined. |
| throw "decode error"; |
| }); |
| } |
| }); |
| |
| decoder.configure({codec: vp9.codec}); |
| |
| decoder.decode(new EncodedVideoChunk('key', 0, view(buffer, vp9.frames[0]))); |
| |
| return decoder.flush().then(() => { |
| assert_equals(numOutputs, 1, "outputs"); |
| }); |
| }), 'Decode VP9'); |
| |
| promise_test(t => { |
| let decoder = new VideoDecoder({ |
| output(frame) { |
| t.step(() => { |
| throw "unexpected output"; |
| }); |
| }, |
| error(e) { |
| t.step(() => { |
| throw "unexpected error"; |
| }); |
| } |
| }); |
| |
| decoder.close(); |
| |
| let fakeChunk = new EncodedVideoChunk('key', 0, Uint8Array.of(0)); |
| assert_throws_dom("InvalidStateError", |
| () => decoder.configure({codec: vp9.codec}), |
| "configure"); |
| assert_throws_dom("InvalidStateError", |
| () => decoder.decode(fakeChunk), |
| "reset"); |
| assert_throws_dom("InvalidStateError", |
| () => decoder.reset(), |
| "reset"); |
| assert_throws_dom("InvalidStateError", |
| () => decoder.close(), |
| "close"); |
| return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush'); |
| }, 'Closed decoder'); |
| |
| promise_test(t => { |
| let numErrors = 0; |
| let decoder = new VideoDecoder({ |
| output(frame) { |
| t.step(() => { |
| throw "unexpected output"; |
| }); |
| }, |
| error(e) { |
| numErrors++; |
| } |
| }); |
| |
| let fakeChunk = new EncodedVideoChunk('key', 0, Uint8Array.of(0)); |
| decoder.decode(fakeChunk); |
| |
| return decoder.flush().then( |
| () => { throw "flush succeeded unexpectedly"; }, |
| () => { assert_equals(numErrors, 1, "errors"); }); |
| }, 'Decode without configure'); |
| |
| promise_test(t => { |
| let numErrors = 0; |
| let decoder = new VideoDecoder({ |
| output(frame) { |
| t.step(() => { |
| throw "unexpected output"; |
| }); |
| }, |
| error(e) { |
| numErrors++; |
| } |
| }); |
| |
| decoder.configure({codec: vp9.codec}); |
| |
| let fakeChunk = new EncodedVideoChunk('key', 0, Uint8Array.of(0)); |
| decoder.decode(fakeChunk); |
| |
| return decoder.flush().then( |
| () => { throw "flush succeeded unexpectedly"; }, |
| () => { assert_equals(numErrors, 1, "errors"); }); |
| }, 'Decode corrupt VP9 frame'); |
| |
| promise_test(t => { |
| let numErrors = 0; |
| let decoder = new VideoDecoder({ |
| output(frame) { |
| t.step(() => { |
| throw "unexpected output"; |
| }); |
| }, |
| error(e) { |
| numErrors++; |
| } |
| }); |
| |
| decoder.configure({codec: vp9.codec}); |
| |
| let fakeChunk = new EncodedVideoChunk('key', 0, Uint8Array.of()); |
| decoder.decode(fakeChunk); |
| |
| return decoder.flush().then( |
| () => { throw "flush succeeded unexpectedly"; }, |
| () => { assert_equals(numErrors, 1, "errors"); }); |
| }, 'Decode empty VP9 frame'); |
| </script> |
| </html> |