Bug 1406910 - Add mochitest for audio gUM and no output. r?jib draft
authorAndreas Pehrson <pehrsons@mozilla.com>
Fri, 06 Oct 2017 12:51:12 +0200
changeset 766753 9725938897541e366cece0af4be2e47574536817
parent 766752 929e65c8e0e599c3c4f6e627be595f3a50c0a42c
push id102394
push userbmo:apehrson@mozilla.com
push dateTue, 13 Mar 2018 09:57:43 +0000
reviewersjib
bugs1406910
milestone60.0a1
Bug 1406910 - Add mochitest for audio gUM and no output. r?jib MozReview-Commit-ID: 2POeIQBPEUo
dom/media/tests/mochitest/head.js
dom/media/tests/mochitest/mochitest.ini
dom/media/tests/mochitest/test_getUserMedia_audio_inputOnly.html
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -252,16 +252,44 @@ function createOscillatorStream(ac, freq
 
   var oscDest = ac.createMediaStreamDestination();
   osc.connect(oscDest);
   osc.start();
   return oscDest.stream;
 }
 
 /**
+ * Record a MediaStream until the tracks in `stream` end, or the promise `stop`
+ * resolves.
+ *
+ * @param {MediaStream} stream
+ *                 A MediaStream object which we shall record.
+ * @param {Promise} stop
+ *                 A Promise that on resolving shall stop the recording.
+ * @returns {blob} A blob containing the recorded file.
+ */
+async function record(stream, stop) {
+  let recorder = new MediaRecorder(stream);
+  recorder.start();
+  let dataAvailable = haveEvent(recorder, "dataavailable");
+
+  await Promise.race([
+    haveEvent(recorder, "stop"),
+    haveEvent(recorder, "error").then(e => Promise.reject(e)),
+    stop,
+  ]);
+
+  if (recorder.state == "recording") {
+    recorder.stop();
+  }
+
+  return (await dataAvailable).data;
+}
+
+/**
  * Create the necessary HTML elements for head and body as used by Mochitests
  *
  * @param {object} meta
  *        Meta information of the test
  * @param {string} meta.title
  *        Description of the test
  * @param {string} [meta.bug]
  *        Bug the test was created for
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -40,16 +40,17 @@ skip-if = android_version == '18' # andr
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_noOffer.html]
 [test_enumerateDevices.html]
 [test_enumerateDevices_iframe.html]
 skip-if = true # needed by test_enumerateDevices.html on builders
 [test_ondevicechange.html]
 skip-if = os == 'android'
 [test_getUserMedia_active_autoplay.html]
+[test_getUserMedia_audio_inputOnly.html]
 [test_getUserMedia_audioCapture.html]
 skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator), android(Bug 1264333)
 [test_getUserMedia_addTrackRemoveTrack.html]
 skip-if = android_version == '18' || os == 'linux' # android(Bug 1189784, timeouts on 4.3 emulator), linux bug 1377450
 [test_getUserMedia_addtrack_removetrack_events.html]
 skip-if = os == 'linux' && debug # Bug 1389983
 [test_getUserMedia_basicAudio_loopback.html]
 skip-if = os == 'mac' || os == 'win' || toolkit == 'android' # Bug 1404995, no loopback devices on some platforms
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_audio_inputOnly.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    title: "getUserMedia Audio Test with no audio output present",
+    bug: "1406027",
+    visible: true,
+  });
+
+  runTest(async () => {
+    if (!FAKE_ENABLED) {
+      // Set the frequency to something other than pulse's sine-source's default
+      TEST_AUDIO_FREQ = 2000;
+    }
+
+    // We want to test audio capture by a MediaStreamGraph that has no audio
+    // outputs. Playing a file uses a separate path for output so this is ok
+    // to feed the MSG input track through our loopback device.
+    let outputAudio = createMediaElement("audio", "output");
+    let sine = URL.createObjectURL(await getSineWaveFile(TEST_AUDIO_FREQ, 5));
+    outputAudio.loop = true;
+    outputAudio.src = sine;
+    await haveEvent(outputAudio, "playing");
+
+    let stream = await getUserMedia({audio: true});
+    let blob = await record(stream, wait(5000));
+    stream.getTracks().forEach(t => t.stop());
+    outputAudio.src = "";
+    URL.revokeObjectURL(sine);
+
+    let audio = document.createElement("audio");
+    audio.preload = "metadata";
+    let recording = URL.createObjectURL(blob);
+    audio.src = recording;
+    await haveEvent(audio, "loadedmetadata");
+    let ended = haveEvent(audio, "ended");
+
+    let ac = new AudioContext();
+    let analyser = new AudioStreamAnalyser(ac, audio.mozCaptureStream());
+    analyser.enableDebugCanvas();
+    audio.play();
+
+    let count = 0;
+    await analyser.waitForAnalysisSuccess(
+        array => {
+          if (array[analyser.binIndexForFrequency(TEST_AUDIO_FREQ * 0.5)] < 50 &&
+              array[analyser.binIndexForFrequency(TEST_AUDIO_FREQ)] > 200 &&
+              array[analyser.binIndexForFrequency(TEST_AUDIO_FREQ * 1.5)] < 50) {
+            if (count == 0) {
+              info("Audio analysis passed. Need 9 more passes in a row.");
+            }
+            // Audio input should be smooth enough to pass the check 10
+            // iterations in a row
+            return (++count) == 10;
+          }
+          if (count > 0) {
+            info(`Audio analysis failed after ${count} passes`);
+          }
+          count = 0;
+          return false;
+        },
+        ended.then(() => new Error("Recording ended. Analysis failed.")));
+
+    ok(true, "Analysis OK");
+
+    analyser.disableDebugCanvas();
+    URL.revokeObjectURL(recording);
+    audio.src = null;
+  });
+
+</script>
+</pre>
+</body>
+</html>