Bug 1203836 - Test that MediaElementAudioSourceNode pipes correct data to an AudioContext. r=karlt, a=lizzard
authorPaul Adenot <paul@paul.cx>
Mon, 21 Sep 2015 11:00:12 +0200
changeset 284270 33718e845d0d19c5d9a0a8e4feee686079e022f8
parent 284269 d64d54d04a6b0f83a98a6101a59074054bf851c8
child 284271 53f7553a1c8bdf1a285a6b3b2e348e65873a51bf
push id8486
push usercbook@mozilla.com
push dateThu, 01 Oct 2015 07:50:37 +0000
treeherdermozilla-aurora@33718e845d0d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, lizzard
bugs1203836
milestone43.0a2
Bug 1203836 - Test that MediaElementAudioSourceNode pipes correct data to an AudioContext. r=karlt, a=lizzard
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>