Bug 1517022 - Improve logic for ensuring mixed-blobs. r=jib
authorAndreas Pehrson <apehrson@mozilla.com>
Mon, 12 Aug 2019 07:55:20 +0000
changeset 487437 92a83858c472514ae8a92691597395ba3143cb7d
parent 487436 e04969269d7d538c808853b864cdcd7f5e53d885
child 487438 8aec34b78d07a465bfdb0dbeb9894040b2ac6e6d
push id92251
push userpehrsons@gmail.com
push dateMon, 12 Aug 2019 07:58:13 +0000
treeherderautoland@8aec34b78d07 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs1517022, 1014393
milestone70.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 1517022 - Improve logic for ensuring mixed-blobs. r=jib This changes from trying to encode at the memory-blob-limit to requesting small blobs for ensuring a memory blob and increasingly large blobs for ensuring a file blob. A theory why this would end up failing on Android hw is because fake video on Android is 320x240 as opposed to 640x480 on other platforms. Add timing changes from bug 1014393 and the permafail on Android doesn't seem surprising. Differential Revision: https://phabricator.services.mozilla.com/D40656
dom/media/test/test_mediarecorder_record_gum_video_timeslice_mixed.html
--- a/dom/media/test/test_mediarecorder_record_gum_video_timeslice_mixed.html
+++ b/dom/media/test/test_mediarecorder_record_gum_video_timeslice_mixed.html
@@ -4,87 +4,95 @@
   <title>Test MediaRecorder Record gUM video with Timeslice, and playback of mixed memory and file blobs</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="gUM_support.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="text/javascript">
-function unexpected({name}) {
-  ok(false, `${name} unexpectedly fired`);
+function unexpected({type}) {
+  ok(false, `${type} unexpectedly fired`);
 }
 
 (async _ => {
   SimpleTest.waitForExplicitFinish();
   let blobUrl = null;
+  let stream = null;
   try {
     // This is the memory limit per blob. If a blob is larger than this,
-    // MediaRecorder will put it in a file. We tell MediaRecorder to issue 10
-    // blobs per second (i.e., timeslice of 100ms) with a video bps of ten times
-    // the blob limit. That should make the blob size hover around this limit,
-    // causing us to go both under and over.
+    // MediaRecorder will put it in a file. For this test we need to get at
+    // least one blob under, and one blob over the limit.
     const memoryLimit = 3000;
     await SpecialPowers.pushPrefEnv({set: [
       ["media.recorder.max_memory", memoryLimit],
     ]});
-    let hasMemoryBlob = false;
-    let hasFileBlob = false;
     // We always use fake devices since the loopback ones don't make enough
     // pixels change per frame to make the encoded frames large enough.
     await pushGetUserMediaTestPrefs({fakeAudio: true, fakeVideo: true});
-    const stream = await navigator.mediaDevices.getUserMedia(
+    stream = await navigator.mediaDevices.getUserMedia(
       {audio: true, video: true});
     const blobs = [];
 
-    mediaRecorder = new MediaRecorder(
-      stream, {videoBitsPerSecond: memoryLimit * 8 * 10});
+    mediaRecorder = new MediaRecorder(stream);
     is(mediaRecorder.stream, stream,
        "Media recorder stream = element stream at the start of recording");
-    mediaRecorder.start(100);
+    mediaRecorder.start();
     mediaRecorder.addEventListener("warning", unexpected);
     mediaRecorder.addEventListener("error", unexpected);
     mediaRecorder.addEventListener("stop", unexpected);
-    mediaRecorder.addEventListener("dataavailable", ({data, type}) => {
-      const isMemoryBlob = data.size < memoryLimit;
-      const isFileBlob = data.size >= memoryLimit;
-      if (!hasMemoryBlob) {
-        hasMemoryBlob = isMemoryBlob;
-      }
-      if (!hasFileBlob) {
-        hasFileBlob = isFileBlob;
-      }
-      info(`dataavailable fired, size=${data.size}, ` +
-           `memory=${isMemoryBlob}, file=${isFileBlob}`);
+    await new Promise(r => mediaRecorder.onstart = r);
+
+    for (let hasMemory = false; !hasMemory;) {
+      mediaRecorder.requestData();
+      const {data} = await new Promise(r => mediaRecorder.ondataavailable = r);
       blobs.push(data);
+      ok(data.size < memoryLimit, "Blob should be small enough at start");
+      hasMemory = data.size > 0 && data.size < memoryLimit;
+      info(`Blob is ${data.size} bytes.${hasMemory ? " In memory." : ""}`);
+    }
+    info("Got a memory blob");
 
-      // We'll stop recording when we have both a memory and a file blob
-      if (hasMemoryBlob && hasFileBlob && mediaRecorder.state == "recording") {
-        info("Stopping recording");
-        mediaRecorder.removeEventListener("stop", unexpected);
-        mediaRecorder.stop();
-      }
-    });
+    SimpleTest.requestFlakyTimeout("Wait for file blob");
+    for (let hasFile = false, waitTimeMs = 500; !hasFile; waitTimeMs *= 4) {
+      info(`Waiting ${waitTimeMs/1000} seconds for file blob`);
+      await new Promise(r => setTimeout(r, waitTimeMs));
+      mediaRecorder.requestData();
+      const {data} = await new Promise(r => mediaRecorder.ondataavailable = r);
+      blobs.push(data);
+      hasFile = data.size > memoryLimit;
+      info(`Blob is ${data.size} bytes. In ${hasFile ? "file" : "memory"}.`);
+    }
+    info("Got a file blob");
 
+    mediaRecorder.stop();
+    const {data} = await new Promise(r => mediaRecorder.ondataavailable = r);
+    blobs.push(data);
+    mediaRecorder.removeEventListener("stop", unexpected);
     await new Promise(r => mediaRecorder.onstop = r);
 
     const video = document.createElement("video");
     const blob = new Blob(blobs);
     blobUrl = URL.createObjectURL(blob);
     video.src = blobUrl;
     info(`Starting playback. Blob-size=${blob.size}`);
     video.play();
 
     await Promise.race([
       new Promise(res => video.onended = res),
       new Promise((_, rej) => video.onerror = _ => rej(video.error.message)),
     ]);
   } catch (e) {
     ok(false, e);
   } finally {
+    if (stream) {
+      for (const t of stream.getTracks()) {
+        t.stop();
+      }
+    }
     if (blobUrl) {
       URL.revokeObjectURL(blobUrl);
     }
     SimpleTest.finish();
   }
 })();
 </script>
 </pre>