Bug 1203836 - Test that MediaElementAudioSourceNode pipes correct data to an AudioContext. r=karlt
authorPaul Adenot <paul@paul.cx>
Mon, 21 Sep 2015 11:00:12 +0200
changeset 263600 abdbbdfa88e8e48933fa9fa0730c1c2a6e1ff6a1
parent 263599 68d05b03e9f1403e7398161162f665271eebe78c
child 263601 e61f327085dacc6a236e86d12cc871728e368696
push id29415
push usercbook@mozilla.com
push dateTue, 22 Sep 2015 10:41:24 +0000
treeherdermozilla-central@a1ccea59e254 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs1203836
milestone43.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 1203836 - Test that MediaElementAudioSourceNode pipes correct data to an AudioContext. r=karlt
dom/media/webaudio/test/mochitest.ini
dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -116,16 +116,18 @@ skip-if = toolkit == 'android' # bug 105
 [test_dynamicsCompressorNodePassThrough.html]
 [test_gainNode.html]
 [test_gainNodeInLoop.html]
 [test_gainNodePassThrough.html]
 [test_maxChannelCount.html]
 [test_mediaDecoding.html]
 [test_mediaElementAudioSourceNode.html]
 tags=capturestream
+[test_mediaElementAudioSourceNodeFidelity.html]
+tags=capturestream
 [test_mediaElementAudioSourceNodePassThrough.html]
 tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaElementAudioSourceNodeCrossOrigin.html]
 tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaStreamAudioDestinationNode.html]
 [test_mediaStreamAudioSourceNode.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaStreamAudioSourceNode doesn't get data from cross-origin media resources</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+// Get an Opus file containing a sine wave at maximum amplitude, of duration
+// `lengthSeconds`, and of frequency `frequency`.
+function getSineWaveFile(frequency, lengthSeconds, callback) {
+  var chunks = [];
+  var off = new OfflineAudioContext(1, lengthSeconds * 48000, 48000);
+  var osc = off.createOscillator();
+  var rec = new MediaRecorder(osc);
+  rec.ondataavailable = function(e) {
+    chunks.push(e.data);
+  };
+  rec.onstop = function(e) {
+    var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
+    callback(blob);
+  }
+  osc.frequency.value = 1.0;
+  osc.start();
+  rec.start();
+  off.startRendering().then(function(buffer) {
+    rec.stop();
+  });
+}
+
+function binIndexForFrequency(frequency, analyser) {
+  return 1 + Math.round(frequency *
+                        analyser.fftSize /
+                        analyser.context.sampleRate);
+}
+
+function debugCanvas(analyser) {
+  var cvs = document.createElement("canvas");
+  document.body.appendChild(cvs);
+
+  // Easy: 1px per bin
+  cvs.width = analyser.frequencyBinCount;
+  cvs.height = 256;
+  cvs.style.border = "1px solid red";
+
+  var c = cvs.getContext('2d');
+  var buf = new Uint8Array(analyser.frequencyBinCount);
+
+  function render() {
+    c.clearRect(0, 0, cvs.width, cvs.height);
+    analyser.getByteFrequencyData(buf);
+    for (var i = 0; i < buf.length; i++) {
+      c.fillRect(i, (256 - (buf[i])), 1, 256);
+    }
+    requestAnimationFrame(render);
+  }
+  requestAnimationFrame(render);
+}
+
+
+function checkFrequency(an) {
+  an.getFloatFrequencyData(frequencyArray);
+  // We should have no energy when checking the data largely outside the index
+  // for 440Hz (the frequency of the sine wave), start checking an octave above,
+  // the Opus compression can add some harmonics to the pure since wave.
+  var index = binIndexForFrequency(880, an);
+  var underTreshold = true;
+  for (var i = index; i < frequencyArray.length; i++) {
+    // Let some slack, there might be some noise here because of int -> float
+    // conversion or the Opus encoding.
+    if (frequencyArray[i] > an.minDecibels + 40) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function test() {
+  getSineWaveFile(440, 1, (blob) => {
+      var audioElement = new Audio();
+      audioElement.src = URL.createObjectURL(blob);
+      audioElement.loop = true;
+      var ac = new AudioContext();
+      var mediaElementSource = ac.createMediaElementSource(audioElement);
+      var an = ac.createAnalyser();
+      frequencyArray = new Float32Array(an.frequencyBinCount);
+
+      // Uncomment this to check what the analyser is doing.
+      // debugCanvas(an);
+
+      mediaElementSource.connect(an);
+
+      audioElement.play();
+      // We want to check the we have the expected audio for at least one loop of
+      // the HTMLMediaElement. The file is one second, and we use the default FFT
+      // size.
+      var lastCurrentTime = 0;
+      var looped = false;
+      audioElement.onplaying = function() {
+        audioElement.ontimeupdate = function() {
+          if (checkFrequency(an)) {
+            ok(true, "Found correct audio signal during analysis");
+            dump(lastCurrentTime + " " + audioElement.currentTime + "\n");
+            if (lastCurrentTime > audioElement.currentTime) {
+              if (looped) {
+                audioElement.ontimeupdate = null;
+                audioElement.onplaying = null;
+                SimpleTest.finish()
+              }
+              lastCurrentTime = audioElement.currentTime;
+              looped = true;
+            } else {
+              lastCurrentTime = audioElement.currentTime;
+            }
+          } else {
+            ok(false, "Found unexpected noise during analysis.");
+            audioElement.ontimeupdate = null;
+            audioElement.onplaying = null;
+            ac.close();
+            audioElement.src = '';
+            SimpleTest.finish()
+          }
+        }
+      }
+  });
+}
+
+SpecialPowers.pushPrefEnv(
+    {'set': [['media.recorder.audio_node.enabled', true]]}, test);
+
+</script>