Source code

Revision control

Other Tools

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(sourceNode) {
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>