test_getUserMedia_audioConstraints_concurrentIframes.html (6471B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <script type="application/javascript" src="mediaStreamPlayback.js"></script> 5 </head> 6 <body> 7 <pre id="test"> 8 <script type="application/javascript"> 9 createHTML({ 10 title: "getUserMedia in multiple iframes with different constraints", 11 bug: "1404977" 12 }); 13 /** 14 * Verify that we can successfully call getUserMedia for the same device in 15 * multiple iframes concurrently. This is checked by creating a number of 16 * iframes and performing a separate getUserMedia call in each. We verify the 17 * stream returned by that call has the same constraints as requested both 18 * immediately after the call and after all gUM calls have been made. The test 19 * then verifies the streams can be played. 20 */ 21 runTest(async function() { 22 // Compare constraints and return a string with the differences in 23 // echoCancellation, autoGainControl, and noiseSuppression. The string 24 // will be empty if there are no differences. 25 function getConstraintDifferenceString(constraints, otherConstraints) { 26 let diffString = ""; 27 if (constraints.echoCancellation != otherConstraints.echoCancellation) { 28 diffString += "echoCancellation different: " + 29 `${constraints.echoCancellation} != ${otherConstraints.echoCancellation}, `; 30 } 31 if (constraints.autoGainControl != otherConstraints.autoGainControl) { 32 diffString += "autoGainControl different: " + 33 `${constraints.autoGainControl} != ${otherConstraints.autoGainControl}, `; 34 } 35 if (constraints.noiseSuppression != otherConstraints.noiseSuppression) { 36 diffString += "noiseSuppression different: " + 37 `${constraints.noiseSuppression} != ${otherConstraints.noiseSuppression}, `; 38 } 39 // Replace trailing comma and space if any 40 return diffString.replace(/, $/, ""); 41 } 42 43 // We need a real device to get a MediaEngine supporting constraints 44 let audioDevice = SpecialPowers.getCharPref("media.audio_loopback_dev", ""); 45 if (!audioDevice) { 46 todo(false, "No device set by framework. Try --use-test-media-devices"); 47 return; 48 } 49 50 let egn = (e, g, n) => ({ 51 echoCancellation: e, 52 autoGainControl: g, 53 noiseSuppression: n 54 }); 55 56 let allConstraintCombinations = [ 57 egn(false, false, false), 58 egn(true, false, false), 59 egn(false, true, false), 60 egn(false, false, true), 61 egn(true, true, false), 62 egn(true, false, true), 63 egn(false, true, true), 64 egn(true, true, true), 65 ]; 66 67 // TODO: We would like to be able to perform an arbitrary number of gUM calls 68 // at once, but issues with pulse and audio IPC mean on some systems we're 69 // limited to as few as 2 concurrent calls. To avoid issues we chunk test runs 70 // to only two calls at a time. The while, splice and GC lines can be removed, 71 // the extra scope removed and allConstraintCombinations can be renamed to 72 // constraintCombinations once this issue is resolved. See bug 1480489. 73 while (allConstraintCombinations.length) { 74 { 75 let constraintCombinations = allConstraintCombinations.splice(0, 2); 76 // Array to store objects that associate information used in our test such as 77 // constraints, iframes, gum streams, and various promises. 78 let testCases = []; 79 80 for (let constraints of constraintCombinations) { 81 let testCase = {requestedConstraints: constraints}; 82 // Provide an id for logging, labeling related elements. 83 testCase.id = `testCase.` + 84 `e=${constraints.echoCancellation}.` + 85 `g=${constraints.noiseSuppression}.` + 86 `n=${constraints.noiseSuppression}`; 87 testCases.push(testCase); 88 testCase.iframe = document.createElement("iframe"); 89 testCase.iframeLoadedPromise = new Promise((resolve, reject) => { 90 testCase.iframe.onload = () => { resolve(); }; 91 }); 92 document.body.appendChild(testCase.iframe); 93 } 94 is(testCases.length, 95 constraintCombinations.length, 96 "Should have created a testcase for each constraint"); 97 98 // Wait for all iframes to be loaded 99 await Promise.all(testCases.map(tc => tc.iframeLoadedPromise)); 100 101 // One by one see if we can grab a gUM stream per iframe 102 for (let testCase of testCases) { 103 // Use normal gUM rather than our test helper as the test harness was 104 // not made to be used inside iframes. 105 testCase.gumStream = 106 await testCase.iframe.contentWindow.navigator.mediaDevices.getUserMedia({audio: testCase.requestedConstraints}) 107 .catch(e => Promise.reject(`getUserMedia calls should not fail! Failed at ${testCase.id} with: ${e}!`)); 108 let differenceString = getConstraintDifferenceString( 109 testCase.requestedConstraints, 110 testCase.gumStream.getAudioTracks()[0].getSettings()); 111 ok(!differenceString, 112 `gUM stream for ${testCase.id} should have the same constraints as were ` + 113 `requested from gUM. Differences: ${differenceString}`); 114 } 115 116 // Once all streams are collected, make sure the constraints haven't been 117 // mutated by another gUM call. 118 for (let testCase of testCases) { 119 let differenceString = getConstraintDifferenceString( 120 testCase.requestedConstraints, 121 testCase.gumStream.getAudioTracks()[0].getSettings()); 122 ok(!differenceString, 123 `gUM stream for ${testCase.id} should not have had constraints altered after ` + 124 `all gUM calls are done. Differences: ${differenceString}`); 125 } 126 127 // We do not currently have tests to verify the behaviour of the different 128 // constraints. Once we do we should do further verification here. See 129 // bug 1406372, bug 1406376, and bug 1406377. 130 131 for (let testCase of testCases) { 132 let testAudio = createMediaElement("audio", `testAudio.${testCase.id}`); 133 let playback = new MediaStreamPlayback(testAudio, testCase.gumStream); 134 await playback.playMediaWithoutStoppingTracks(false); 135 } 136 137 // Stop the tracks for each stream, we left them running above via 138 // playMediaWithoutStoppingTracks to make sure they can play concurrently. 139 for (let testCase of testCases) { 140 testCase.gumStream.getTracks().map(t => t.stop()); 141 document.body.removeChild(testCase.iframe); 142 } 143 } 144 await new Promise(r => SpecialPowers.exactGC(r)); 145 } 146 }); 147 </script> 148 </pre> 149 </body> 150 </html>