Bug 1659837 [wpt PR 25082] - Parse codecs on VideoEncoder::Configure(), a=testonly
authorThomas Guilbert <tguilbert@chromium.org>
Wed, 26 Aug 2020 08:52:05 +0000
changeset 546466 374951ccaed536829b979163ce663c2e9646370c
parent 546465 3283fbf1d7eef39b62be4593ce3d3186560b9248
child 546467 6bc108aeafc613711250b885d243ac595e622981
push id37735
push userabutkovits@mozilla.com
push dateThu, 27 Aug 2020 21:29:40 +0000
treeherdermozilla-central@109f3a4de567 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1659837, 25082, 1116783, 1094166, 2363711, 800277
milestone82.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1659837 [wpt PR 25082] - Parse codecs on VideoEncoder::Configure(), a=testonly Automatic update from web-platform-tests Parse codecs on VideoEncoder::Configure() This CL front-loads the configuration parsing and codec support verification into the Configure() method. This means that and invalid or unsupported config should immediately throw an exception, rather than being reported in the error callback. This CL also allows encoders to be re-configured. Bug: 1116783, 1094166 Change-Id: I88df452cfa2a1b0fcfeb4da6c279665661d7380f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2363711 Commit-Queue: Thomas Guilbert <tguilbert@chromium.org> Reviewed-by: Chrome Cunningham <chcunningham@chromium.org> Cr-Commit-Position: refs/heads/master@{#800277} -- wpt-commits: be6aa0ccd2409613b0adc835580cec14def7d505 wpt-pr: 25082
testing/web-platform/tests/webcodecs/video-encoder.html
--- a/testing/web-platform/tests/webcodecs/video-encoder.html
+++ b/testing/web-platform/tests/webcodecs/video-encoder.html
@@ -26,56 +26,77 @@ async function asyncDone(test) {
 }
 
 async_test(async (t) => {
   // VideoEncoderInit lacks required fields.
   assert_throws_js(TypeError, () => { new VideoEncoder({}); });
 
   // VideoEncoderInit has required fields.
   let encoder = new VideoEncoder({
-    output(chunk) { assert_unreached("Unexpected output");  },
-    error(error) { assert_unreached("Unexpected error:" + error); },
+    output(chunk) { t.unreached_func("Unexpected output").call(); },
+    error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
   encoder.close();
 
   asyncDone(t);
 }, 'Test VideoEncoder construction');
 
 async_test(async (t) => {
   let encoder = new VideoEncoder({
-    output(chunk) { assert_unreached("Unexpected output");  },
-    error(error) { assert_unreached("Unexpected error:" + error); },
+    output(chunk) { t.unreached_func("Unexpected output").call(); },
+    error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
   const requiredConfigPairs = {
     codec: 'vp8',
     framerate: 25,
     width: 640,
     height: 480
   };
   let incrementalConfig = {};
 
   for (let key in requiredConfigPairs) {
-    // Configure  should fail while required keys are missing.
+    // Configure should fail while required keys are missing.
     assert_throws_js(TypeError, () => { encoder.configure(incrementalConfig); });
     incrementalConfig[key] = requiredConfigPairs[key];
   }
 
   // Configure should pass once incrementalConfig meets all requirements.
   encoder.configure(incrementalConfig);
 
+  // We should be able to reconfigure the encoder.
+  encoder.configure(incrementalConfig);
+
+  let config = incrementalConfig;
+
+  // Bogus codec rejected.
+  config.codec = 'bogus';
+  assert_throws_js(TypeError, () => { encoder.configure(config); });
+
+  // Audio codec rejected.
+  config.codec = 'vorbis';
+  assert_throws_js(TypeError, () => { encoder.configure(config); });
+
+  // Ambiguous codec rejected.
+  config.codec = 'vp9';
+  assert_throws_js(TypeError, () => { encoder.configure(config); });
+
+  // Codec with mime type rejected.
+  config.codec = 'video/webm; codecs="vp9"';
+  assert_throws_js(TypeError, () => { encoder.configure(config); });
+
   encoder.close();
 
   asyncDone(t);
 }, 'Test VideoEncoder.configure()');
 
 async_test(async (t) => {
   let encoder = new VideoEncoder({
-    output(chunk) { assert_unreached("Unexpected output"); },
-    error(error) { assert_unreached("Unexpected error:" + error); },
+    output(chunk) { t.unreached_func("Unexpected output").call(); },
+    error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
   let videoFrame = await createVideoFrame(640, 480, 0);
 
   assert_throws_dom('InvalidStateError',
                     () => { encoder.encode(videoFrame); },
                     'first encode');
 
@@ -88,17 +109,17 @@ async_test(async (t) => {
 
   asyncDone(t);
 }, 'Test encode() before configure() throws InvalidStateError.');
 
 async_test(async (t) => {
   let output_chunks = [];
   let encoder = new VideoEncoder({
     output(chunk) { output_chunks.push(chunk); },
-    error(error) { assert_unreached("Unexpected error:" + error); },
+    error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
   // No encodes yet.
   assert_equals(encoder.encodeQueueSize, 0);
 
   const config = {
     codec: 'vp8',
     framerate: 25,
@@ -128,10 +149,80 @@ async_test(async (t) => {
   assert_equals(output_chunks.length, 2);
   assert_equals(output_chunks[0].timestamp, frame1.timestamp);
   assert_equals(output_chunks[1].timestamp, frame2.timestamp);
 
   encoder.close();
 
   asyncDone(t);
 }, 'Test successful configure(), encode(), and flush()');
+
+async_test(async (t) => {
+  let output_chunks = [];
+  let encoder = new VideoEncoder({
+    output(chunk) { output_chunks.push(chunk); },
+    error(error) { t.unreached_func("Unexpected error:" + error).call(); },
+  });
+
+  // No encodes yet.
+  assert_equals(encoder.encodeQueueSize, 0);
+
+  const config = {
+    codec: 'vp8',
+    framerate: 25,
+    width: 640,
+    height: 480
+  };
+  encoder.configure(config);
+
+  let frame1 = await createVideoFrame(640, 480, 0);
+  let frame2 = await createVideoFrame(640, 480, 33333);
+
+  encoder.encode(frame1);
+  encoder.configure(config);
+
+  encoder.encode(frame2);
+
+  await encoder.flush();
+
+  // We can guarantee that all encodes are processed after a flush.
+  assert_equals(encoder.encodeQueueSize, 0);
+
+  // The first frame may have been dropped when reconfiguring.
+  // This shouldn't happen, and should be fixed/called out in the spec, but
+  // this is preptively added to prevent flakiness.
+  // TODO: Remove these checks when implementations handle this correctly.
+  assert_true(output_chunks.length == 1 || output_chunks.length == 2);
+
+  if (output_chunks.length == 1) {
+    // If we only have one chunk frame, make sure we droped the frame that was
+    // in flight when we reconfigured.
+    assert_equals(output_chunks[0].timestamp, frame2.timestamp);
+  } else {
+    assert_equals(output_chunks[0].timestamp, frame1.timestamp);
+    assert_equals(output_chunks[1].timestamp, frame2.timestamp);
+  }
+
+  output_chunks = [];
+
+  let frame3 = await createVideoFrame(640, 480, 66666);
+  let frame4 = await createVideoFrame(640, 480, 100000);
+
+  encoder.encode(frame3);
+
+  // Verify that a failed call to configure does not change the encoder's state.
+  config.codec = 'bogus';
+  assert_throws_js(TypeError, () => encoder.configure(config));
+
+  encoder.encode(frame4);
+
+  await encoder.flush();
+
+  assert_equals(output_chunks[0].timestamp, frame3.timestamp);
+  assert_equals(output_chunks[1].timestamp, frame4.timestamp);
+
+  encoder.close();
+
+  asyncDone(t);
+}, 'Test successful encode() after re-configure().');
+
 </script>
 </html>