diff options
Diffstat (limited to 'dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html')
-rw-r--r-- | dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html new file mode 100644 index 0000000000..7920af9f7b --- /dev/null +++ b/dom/media/webaudio/test/test_mediaStreamAudioSourceNodeNoGC.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<meta charset="utf-8"> +<head> + <title>Test that MediaStreamAudioSourceNode and its input MediaStream stays alive while there are active tracks</title> + <script 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(); +SimpleTest.requestFlakyTimeout("gUM and WebAudio data is async to main thread. " + + "We need a timeout to see that something does " + + "NOT happen to data."); + +let context = new AudioContext(); +let analyser = context.createAnalyser(); + +function wait(millis, resolveWithThis) { + return new Promise(resolve => setTimeout(() => resolve(resolveWithThis), millis)); +} + +function binIndexForFrequency(frequency) { + return 1 + Math.round(frequency * analyser.fftSize / context.sampleRate); +} + +function waitForAudio(analysisFunction, cancelPromise) { + let data = new Uint8Array(analyser.frequencyBinCount); + let cancelled = false; + let cancelledMsg = ""; + cancelPromise.then(msg => { + cancelled = true; + cancelledMsg = msg; + }); + return new Promise((resolve, reject) => { + let loop = () => { + analyser.getByteFrequencyData(data); + if (cancelled) { + reject(new Error("waitForAudio cancelled: " + cancelledMsg)); + return; + } + if (analysisFunction(data)) { + resolve(); + return; + } + requestAnimationFrame(loop); + }; + loop(); + }); +} + +async function test(sourceNode) { + try { + await analyser.connect(context.destination); + + ok(true, "Waiting for audio to pass through the analyser") + await waitForAudio(arr => arr[binIndexForFrequency(1000)] > 200, + wait(60000, "Timeout waiting for audio")); + + ok(true, "Audio was detected by the analyser. Forcing CC."); + SpecialPowers.forceCC(); + SpecialPowers.forceGC(); + SpecialPowers.forceCC(); + SpecialPowers.forceGC(); + + info("Checking that GC didn't destroy the stream or source node"); + await waitForAudio(arr => arr[binIndexForFrequency(1000)] < 50, + wait(5000, "Timeout waiting for GC (timeout OK)")) + .then(() => Promise.reject("Audio stopped unexpectedly"), + () => Promise.resolve()); + + ok(true, "Audio is still flowing"); + } catch(e) { + ok(false, "Error executing test: " + e + (e.stack ? "\n" + e.stack : "")); + SimpleTest.finish(); + } +} + +(async function() { + try { + await SpecialPowers.pushPrefEnv({ + set: [ + // This test expects the fake audio device, specifically for the tones + // it outputs. Explicitly disable the audio loopback device and enable + // fake streams. + ['media.audio_loopback_dev', ''], + ['media.navigator.streams.fake', true], + ['media.navigator.permission.disabled', true] + ] + }); + + // Test stream source GC + let stream = await navigator.mediaDevices.getUserMedia({audio: true}); + let source = context.createMediaStreamSource(stream); + stream = null; + source.connect(analyser); + await test(source); + + // Test track source GC + stream = await navigator.mediaDevices.getUserMedia({audio: true}); + source = context.createMediaStreamTrackSource(stream.getAudioTracks()[0]); + stream = null; + source.connect(analyser); + await test(source); + } catch(e) { + ok(false, `Error executing test: ${e}${e.stack ? "\n" + e.stack : ""}`); + } finally { + context.close(); + SimpleTest.finish(); + } +})(); +</script> +</pre> +</body> +</html> |