summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/tests/mochitests/test_getUserMedia_audioConstraints_concurrentIframes.html
blob: d07dbc41f1b428cd488787cc748024355fb0fdf1 (plain)
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>