Source code

Revision control

Other Tools

Test Info: Warnings

  • This test gets skipped with pattern: (os == "win" && processor == "aarch64") toolkit == 'android'
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder recording a constructed MediaStream</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script src="/tests/dom/media/webrtc/tests/mochitests/head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<div id="content">
</div>
<script>
SimpleTest.waitForExplicitFinish();
runTestWhenReady(async () => {
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
document.getElementById("content").appendChild(canvas);
const helper = new CaptureStreamTestHelper2D(100, 100);
helper.drawColor(canvas, helper.red);
const audioCtx = new AudioContext();
const osc = audioCtx.createOscillator();
osc.frequency.value = 1000;
osc.start();
const dest = audioCtx.createMediaStreamDestination();
osc.connect(dest);
const stream = dest.stream;
// Timeouts are experienced intermittently in Linux due to no sound in the
// destination. As a workaround wait for the source sound to arrive.
const sourceAnalyser = new AudioStreamAnalyser(audioCtx, stream);
const sourceAudioReady = sourceAnalyser.waitForAnalysisSuccess(array => {
const freq = osc.frequency.value;
const lowerFreq = freq / 2;
const upperFreq = freq + 1000;
const lowerAmp = array[sourceAnalyser.binIndexForFrequency(lowerFreq)];
const freqAmp = array[sourceAnalyser.binIndexForFrequency(freq)];
const upperAmp = array[sourceAnalyser.binIndexForFrequency(upperFreq)];
info("Analysing source audio. "
+ lowerFreq + ": " + lowerAmp + ", "
+ freq + ": " + freqAmp + ", "
+ upperFreq + ": " + upperAmp);
return lowerAmp < 50 && freqAmp > 200 && upperAmp < 50;
});
await sourceAudioReady;
info("Source Audio content ok");
canvas.captureStream(0).getVideoTracks().forEach(t => stream.addTrack(t));
const blobs = [];
let mediaRecorder = new MediaRecorder(stream);
is(mediaRecorder.stream, stream,
"Media recorder stream = constructed stream at the start of recording");
mediaRecorder.ondataavailable = evt => {
info("ondataavailable fired");
is(mediaRecorder.state, "inactive", "Blob received after stopping");
is(blobs.length, 0, "This is the first and only blob");
ok(evt instanceof BlobEvent,
"Events fired from ondataavailable should be BlobEvent");
is(evt.type, "dataavailable",
"Event type should dataavailable");
ok(evt.data.size >= 0,
"Blob data size received is greater than or equal to zero");
blobs.push(evt.data);
};
const stopped = haveEvent(mediaRecorder, "stop", wait(5000, new Error("Timeout")));
const stoppedNoErrors = Promise.all([
stopped,
haveNoEvent(mediaRecorder, "warning", stopped),
haveNoEvent(mediaRecorder, "error", stopped)
]);
mediaRecorder.start();
is(mediaRecorder.state, "recording", "Media recorder should be recording");
await haveEvent(mediaRecorder, "start", wait(5000, new Error("Timeout")));
info("onstart fired");
// The recording can be too short to cause any checks with
// waitForAnalysisSuccess(). Waiting a bit here solves this.
await wait(500);
is(mediaRecorder.state, "recording",
"Media recorder is recording before being stopped");
mediaRecorder.stop();
is(mediaRecorder.state, "inactive",
"Media recorder is inactive after being stopped");
is(mediaRecorder.stream, stream,
"Media recorder stream = constructed stream post recording");
await stoppedNoErrors;
info("Got 'stop' event");
ok(blobs.length == 1, "Should have gotten one data blob");
// Clean up recording sources
osc.stop();
stream.getTracks().forEach(t => t.stop());
// Sanity check the recording
const video = document.createElement("video");
document.getElementById("content").appendChild(video);
video.id = "recorded-video";
const blob = new Blob(blobs);
ok(blob.size > 0, "Recorded blob should contain data");
video.src = URL.createObjectURL(blob);
video.preload = "metadata";
info("Waiting for metadata to be preloaded");
await haveEvent(video, "loadedmetadata", wait(5000, new Error("Timeout")));
info("Playback of recording loaded metadata");
const recordingStream = video.mozCaptureStream();
is(recordingStream.getVideoTracks().length, 1,
"Recording should have one video track");
is(recordingStream.getAudioTracks().length, 1,
"Recording should have one audio track");
const ended = haveEvent(video, "ended", wait(5000, new Error("Timeout")));
const endedNoError = Promise.all([
ended,
haveNoEvent(video, "error", ended),
]);
const analyser = new AudioStreamAnalyser(audioCtx, recordingStream);
const audioReady = analyser.waitForAnalysisSuccess(array => {
const freq = osc.frequency.value;
const lowerFreq = freq / 2;
const upperFreq = freq + 1000;
const lowerAmp = array[analyser.binIndexForFrequency(lowerFreq)];
const freqAmp = array[analyser.binIndexForFrequency(freq)];
const upperAmp = array[analyser.binIndexForFrequency(upperFreq)];
info("Analysing audio. "
+ lowerFreq + ": " + lowerAmp + ", "
+ freq + ": " + freqAmp + ", "
+ upperFreq + ": " + upperAmp);
return lowerAmp < 50 && freqAmp > 200 && upperAmp < 50;
}, endedNoError.then(() => new Error("Audio check failed")));
const videoReady = helper.pixelMustBecome(
video, helper.red, {
threshold: 128,
infoString: "Should become red",
cancelPromise: endedNoError.then(() => new Error("Video check failed")),
});
video.play();
try {
await endedNoError;
} finally {
analyser.disconnect();
let url = video.src;
video.src = "";
URL.revokeObjectURL(url);
}
info("Playback of recording ended without error");
await audioReady;
info("Audio content ok");
await videoReady;
info("Video content ok");
});
</script>
</pre>
</body>
</html>