test_mediaStreamAudioSourceNodeNoGC.html (3663B)
1 <!DOCTYPE HTML> 2 <html> 3 <meta charset="utf-8"> 4 <head> 5 <title>Test that MediaStreamAudioSourceNode and its input MediaStream stays alive while there are active tracks</title> 6 <script src="/tests/SimpleTest/SimpleTest.js"></script> 7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 8 </head> 9 <body> 10 <pre id="test"> 11 <script class="testbody" type="text/javascript"> 12 SimpleTest.waitForExplicitFinish(); 13 SimpleTest.requestFlakyTimeout("gUM and WebAudio data is async to main thread. " + 14 "We need a timeout to see that something does " + 15 "NOT happen to data."); 16 17 let context = new AudioContext(); 18 let analyser = context.createAnalyser(); 19 20 function wait(millis, resolveWithThis) { 21 return new Promise(resolve => setTimeout(() => resolve(resolveWithThis), millis)); 22 } 23 24 function binIndexForFrequency(frequency) { 25 return 1 + Math.round(frequency * analyser.fftSize / context.sampleRate); 26 } 27 28 function waitForAudio(analysisFunction, cancelPromise) { 29 let data = new Uint8Array(analyser.frequencyBinCount); 30 let cancelled = false; 31 let cancelledMsg = ""; 32 cancelPromise.then(msg => { 33 cancelled = true; 34 cancelledMsg = msg; 35 }); 36 return new Promise((resolve, reject) => { 37 let loop = () => { 38 analyser.getByteFrequencyData(data); 39 if (cancelled) { 40 reject(new Error("waitForAudio cancelled: " + cancelledMsg)); 41 return; 42 } 43 if (analysisFunction(data)) { 44 resolve(); 45 return; 46 } 47 requestAnimationFrame(loop); 48 }; 49 loop(); 50 }); 51 } 52 53 async function test() { 54 try { 55 await analyser.connect(context.destination); 56 57 ok(true, "Waiting for audio to pass through the analyser") 58 await waitForAudio(arr => arr[binIndexForFrequency(1000)] > 200, 59 wait(60000, "Timeout waiting for audio")); 60 61 ok(true, "Audio was detected by the analyser. Forcing CC."); 62 SpecialPowers.forceCC(); 63 SpecialPowers.forceGC(); 64 SpecialPowers.forceCC(); 65 SpecialPowers.forceGC(); 66 67 info("Checking that GC didn't destroy the stream or source node"); 68 await waitForAudio(arr => arr[binIndexForFrequency(1000)] < 50, 69 wait(5000, "Timeout waiting for GC (timeout OK)")) 70 .then(() => Promise.reject("Audio stopped unexpectedly"), 71 () => Promise.resolve()); 72 73 ok(true, "Audio is still flowing"); 74 } catch(e) { 75 ok(false, "Error executing test: " + e + (e.stack ? "\n" + e.stack : "")); 76 SimpleTest.finish(); 77 } 78 } 79 80 (async function() { 81 try { 82 await SpecialPowers.pushPrefEnv({ 83 set: [ 84 // This test expects the fake audio device, specifically for the tones 85 // it outputs. Explicitly disable the audio loopback device and enable 86 // fake streams. 87 ['media.audio_loopback_dev', ''], 88 ['media.navigator.streams.fake', true], 89 ['media.navigator.permission.disabled', true] 90 ] 91 }); 92 93 // Test stream source GC 94 let stream = await navigator.mediaDevices.getUserMedia({audio: true}); 95 let source = context.createMediaStreamSource(stream); 96 stream = null; 97 source.connect(analyser); 98 await test(source); 99 100 // Test track source GC 101 stream = await navigator.mediaDevices.getUserMedia({audio: true}); 102 source = context.createMediaStreamTrackSource(stream.getAudioTracks()[0]); 103 stream = null; 104 source.connect(analyser); 105 await test(source); 106 } catch(e) { 107 ok(false, `Error executing test: ${e}${e.stack ? "\n" + e.stack : ""}`); 108 } finally { 109 context.close(); 110 SimpleTest.finish(); 111 } 112 })(); 113 </script> 114 </pre> 115 </body> 116 </html>