DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
<!DOCTYPE HTML>
<html>
<head>
  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
  title: "getUserMedia in multiple iframes with different constraints",
  bug: "1404977"
});
/**
  * Verify that we can successfully call getUserMedia for the same device in
  * multiple iframes concurrently. This is checked by creating a number of
  * iframes and performing a separate getUserMedia call in each. We verify the
  * stream returned by that call has the same constraints as requested both
  * immediately after the call and after all gUM calls have been made. The test
  * then verifies the streams can be played.
  */
runTest(async function() {
  // Compare constraints and return a string with the differences in
  // echoCancellation, autoGainControl, and noiseSuppression. The string
  // will be empty if there are no differences.
  function getConstraintDifferenceString(constraints, otherConstraints) {
    let diffString = "";
    if (constraints.echoCancellation != otherConstraints.echoCancellation) {
      diffString += "echoCancellation different: " +
                    `${constraints.echoCancellation} != ${otherConstraints.echoCancellation}, `;
    }
    if (constraints.autoGainControl != otherConstraints.autoGainControl) {
      diffString += "autoGainControl different: " +
                    `${constraints.autoGainControl} != ${otherConstraints.autoGainControl}, `;
    }
    if (constraints.noiseSuppression != otherConstraints.noiseSuppression) {
      diffString += "noiseSuppression different: " +
                    `${constraints.noiseSuppression} != ${otherConstraints.noiseSuppression}, `;
    }
    // Replace trailing comma and space if any
    return diffString.replace(/, $/, "");
  }

  // We need a real device to get a MediaEngine supporting constraints
  let audioDevice = SpecialPowers.getCharPref("media.audio_loopback_dev", "");
  if (!audioDevice) {
    todo(false, "No device set by framework. Try --use-test-media-devices");
    return;
  }

  let egn = (e, g, n) => ({
    echoCancellation: e,
    autoGainControl: g,
    noiseSuppression: n
  });

  let allConstraintCombinations = [
    egn(false, false, false),
    egn(true,  false, false),
    egn(false, true,  false),
    egn(false, false, true),
    egn(true,  true,  false),
    egn(true,  false, true),
    egn(false, true,  true),
    egn(true,  true,  true),
  ];

  // TODO: We would like to be able to perform an arbitrary number of gUM calls
  // at once, but issues with pulse and audio IPC mean on some systems we're
  // limited to as few as 2 concurrent calls. To avoid issues we chunk test runs
  // to only two calls at a time. The while, splice and GC lines can be removed,
  // the extra scope removed and allConstraintCombinations can be renamed to
  // constraintCombinations once this issue is resolved. See bug 1480489.
  while (allConstraintCombinations.length) {
    {
      let constraintCombinations = allConstraintCombinations.splice(0, 2);
      // Array to store objects that associate information used in our test such as
      // constraints, iframes, gum streams, and various promises.
      let testCases = [];

      for (let constraints of constraintCombinations) {
        let testCase = {requestedConstraints: constraints};
        // Provide an id for logging, labeling related elements.
        testCase.id = `testCase.` +
                      `e=${constraints.echoCancellation}.` +
                      `g=${constraints.noiseSuppression}.` +
                      `n=${constraints.noiseSuppression}`;
        testCases.push(testCase);
        testCase.iframe = document.createElement("iframe");
        testCase.iframeLoadedPromise = new Promise((resolve, reject) => {
          testCase.iframe.onload = () => { resolve(); };
        });
        document.body.appendChild(testCase.iframe);
      }
      is(testCases.length,
        constraintCombinations.length,
        "Should have created a testcase for each constraint");

      // Wait for all iframes to be loaded
      await Promise.all(testCases.map(tc => tc.iframeLoadedPromise));

      // Start a tone at our top level page so the gUM calls will record something
      // should we wish to verify their recording in future.
      let tone = new LoopbackTone(new AudioContext, TEST_AUDIO_FREQ);
      tone.start();

      // One by one see if we can grab a gUM stream per iframe
      for (let testCase of testCases) {
        // Use normal gUM rather than our test helper as the test harness was
        // not made to be used inside iframes.
        testCase.gumStream =
          await testCase.iframe.contentWindow.navigator.mediaDevices.getUserMedia({audio: testCase.requestedConstraints})
          .catch(e => Promise.reject(`getUserMedia calls should not fail! Failed at ${testCase.id} with: ${e}!`));
        let differenceString = getConstraintDifferenceString(
          testCase.requestedConstraints,
          testCase.gumStream.getAudioTracks()[0].getSettings());
        ok(!differenceString,
          `gUM stream for ${testCase.id} should have the same constraints as were ` +
          `requested from gUM. Differences: ${differenceString}`);
      }

      // Once all streams are collected, make sure the constraints haven't been
      // mutated by another gUM call.
      for (let testCase of testCases) {
        let differenceString = getConstraintDifferenceString(
          testCase.requestedConstraints,
          testCase.gumStream.getAudioTracks()[0].getSettings());
        ok(!differenceString,
          `gUM stream for ${testCase.id} should not have had constraints altered after ` +
          `all gUM calls are done. Differences: ${differenceString}`);
      }

      // We do not currently have tests to verify the behaviour of the different
      // constraints. Once we do we should do further verification here. See
      // bug 1406372, bug 1406376, and bug 1406377.

      for (let testCase of testCases) {
        let testAudio = createMediaElement("audio", `testAudio.${testCase.id}`);
        let playback = new MediaStreamPlayback(testAudio, testCase.gumStream);
        await playback.playMediaWithoutStoppingTracks(false);
      }

      // Stop the tracks for each stream, we left them running above via
      // playMediaWithoutStoppingTracks to make sure they can play concurrently.
      for (let testCase of testCases) {
        testCase.gumStream.getTracks().map(t => t.stop());
        document.body.removeChild(testCase.iframe);
      }

      tone.stop();
    }
    await new Promise(r => SpecialPowers.exactGC(r));
  }
});
</script>
</pre>
</body>
</html>