diff options
Diffstat (limited to 'dom/media/test/test_setSinkId_after_loop.html')
-rw-r--r-- | dom/media/test/test_setSinkId_after_loop.html | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/dom/media/test/test_setSinkId_after_loop.html b/dom/media/test/test_setSinkId_after_loop.html new file mode 100644 index 0000000000..6224daa417 --- /dev/null +++ b/dom/media/test/test_setSinkId_after_loop.html @@ -0,0 +1,126 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test setSinkId() after looping</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<script type="application/javascript"> +"use strict"; + +/** +This test captures loopback device audio to test that a media element produces +audio output soon after a setSinkId() call after looping. In bug 1838756, +incorrect timekeeping caused silence in the output as long as the duration of +loops completed. + **/ +add_task(async () => { + const audio = new Audio(); + audio.src = "sin-441-1s-44100.flac"; + audio.loop = true; + audio.controls = true; + // Mute initially so that delayed audio from before setSinkId does not + // trigger a false pass. + audio.muted = true; + document.body.appendChild(audio); + const canplaythroughPromise = new Promise(r => audio.oncanplaythrough = r); + // Let the audio loop at least twice, which would result in 2 extra seconds + // of silence with bug 1838756. + let count = 0; + const loopedPromise = new Promise(r => audio.onseeked = () => { + if (++count == 2) { + r(); + } + }); + + await SpecialPowers.pushPrefEnv({set: [ + // skip gUM permission prompt + ["media.navigator.permission.disabled", true], + // enumerateDevices() without focus + ["media.devices.unfocused.enabled", true], + ]}); + let loopbackInputLabel = + SpecialPowers.getCharPref("media.audio_loopback_dev", ""); + if (!navigator.userAgent.includes("Linux")) { + todo_isnot(loopbackInputLabel, "", "audio_loopback_dev"); + return; + } + isnot(loopbackInputLabel, "", + "audio_loopback_dev. Try --use-test-media-devices."); + const loopbackStream = + await navigator.mediaDevices.getUserMedia({audio: true}); + is(loopbackStream.getTracks()[0].label, loopbackInputLabel, + "loopback track label"); + + await canplaythroughPromise; + let loopbackNode; + try { + audio.play(); + await new Promise(r => audio.onplaying = r); + const ac = new AudioContext; + loopbackNode = ac.createMediaStreamSource(loopbackStream); + const processor1 = ac.createScriptProcessor(4096, 1, 0); + loopbackNode.connect(processor1); + + // Check that the loopback stream contains silence now. + const {inputBuffer} = await new Promise(r => processor1.onaudioprocess = r); + loopbackNode.disconnect(); + is(inputBuffer.getChannelData(0).find(value => value != 0.0), undefined, + "should have silence"); + + // Find output device + const devices = await navigator.mediaDevices.enumerateDevices(); + let loopbackOutputLabel = + SpecialPowers.getCharPref("media.cubeb.output_device", ""); + const outputDeviceInfo = devices.find( + ({kind, label}) => kind == "audiooutput" && label == loopbackOutputLabel + ); + ok(outputDeviceInfo, `found ${loopbackOutputLabel}`); + + await loopedPromise; + await audio.setSinkId(outputDeviceInfo.deviceId); + audio.muted = false; + // Use a new ScriptProcessor so that the first audioprocess event provides + // the rendering thread time after unmuting. + const processor2 = ac.createScriptProcessor(4096, 1, 0); + loopbackNode.connect(processor2); + + let seenAudioCount = 0; + let lastSample, firstTime; + while (seenAudioCount < ac.sampleRate) { + const event = await new Promise(r => processor2.onaudioprocess = r); + let samples = event.inputBuffer.getChannelData(0); + for (const sample of samples) { + if (!seenAudioCount) { + if (!firstTime) { + firstTime = event.playbackTime; + } + if (sample != 0.0) { + seenAudioCount = 1; + } else { + const delay = event.playbackTime - firstTime; + if (delay > 1.0) { + ok(false, `${delay} seconds passed without receiving audio`); + return; + } + } + } else if (sample != 0.0) { + ++seenAudioCount; + } else if (lastSample == 0.0) { + ok(false, "should not have two consecutive zero sample in audio"); + return; + } + lastSample = sample; + } + } + ok(true, "have one continuous second of audio"); + } finally { + if (loopbackNode) { + loopbackNode.disconnect(); + } + audio.pause(); + loopbackStream.getTracks()[0].stop(); + } +}); +</script> +</html> |