Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'mac' && debug
- Manifest: dom/media/webaudio/test/mochitest_media.toml
<!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() {
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>