| <!doctype html> |
| <html> |
| <head> |
| <title>MediaRecorder Stop</title> |
| <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| </head> |
| <body> |
| <canvas id="canvas" width="200" height="200"> |
| </canvas> |
| <script> |
| function createVideoStream() { |
| let canvas = document.getElementById("canvas"); |
| canvas.getContext('2d'); |
| return canvas.captureStream(); |
| } |
| |
| function recordEvents(target, events) { |
| let arr = []; |
| for (let ev of events) { |
| target.addEventListener(ev, _ => arr.push(ev)); |
| } |
| return arr; |
| } |
| |
| // This function is used to check that elements of |actual| is a sub |
| // sequence in the |expected| sequence. |
| function assertSequenceIn(actual, expected) { |
| let i = 0; |
| for (event of actual) { |
| const j = expected.slice(i).indexOf(event); |
| assert_greater_than_equal( |
| j, 0, "Sequence element " + event + " is not included in " + |
| expected.slice(i)); |
| i = j; |
| } |
| return true; |
| } |
| |
| promise_test(async t => { |
| let video = createVideoStream(); |
| let recorder = new MediaRecorder(video); |
| let events = recordEvents(recorder, |
| ["start", "stop", "dataavailable", "pause", "resume", "error"]); |
| assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track"); |
| recorder.start(); |
| assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully"); |
| video.getVideoTracks()[0].stop(); |
| assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following last track ending"); |
| let event = await new Promise(r => recorder.onstop = r); |
| |
| assert_equals(event.type, "stop", "the event type should be stop"); |
| assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++"); |
| assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event"); |
| |
| // As the test is written, it's not guaranteed that |
| // onstart/ondataavailable is invoked, but it's fine if they are. |
| // The stop element is guaranteed to be in events when we get here. |
| assertSequenceIn(events, ["start", "dataavailable", "stop"]); |
| }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended"); |
| |
| promise_test(async t => { |
| let video = createVideoStream(); |
| let recorder = new MediaRecorder(video); |
| let events = recordEvents(recorder, |
| ["start", "stop", "dataavailable", "pause", "resume", "error"]); |
| recorder.start(); |
| assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully"); |
| recorder.stop(); |
| assert_equals(recorder.state, "inactive", "MediaRecorder state should be inactive immediately following stop() call"); |
| |
| let event = await new Promise (r => recorder.onstop = r); |
| assert_equals(event.type, "stop", "the event type should be stop"); |
| assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++"); |
| assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event"); |
| |
| // As the test is written, it's not guaranteed that |
| // onstart/ondataavailable is invoked, but it's fine if they are. |
| // The stop element is guaranteed to be in events when we get here. |
| assertSequenceIn(events, ["start", "dataavailable", "stop"]); |
| }, "MediaRecorder will stop recording and fire a stop event when stop() is called"); |
| |
| promise_test(async t => { |
| const recorder = new MediaRecorder(createVideoStream()); |
| recorder.stop(); |
| await Promise.race([ |
| new Promise((_, reject) => recorder.onstop = |
| _ => reject(new Error("onstop should never have been called"))), |
| new Promise(r => t.step_timeout(r, 0))]); |
| }, "MediaRecorder will not fire an exception when stopped after creation"); |
| |
| promise_test(async t => { |
| const recorder = new MediaRecorder(createVideoStream()); |
| recorder.start(); |
| recorder.stop(); |
| let event = await new Promise(r => recorder.onstop = r); |
| recorder.stop(); |
| await Promise.race([ |
| new Promise((_, reject) => recorder.onstop = |
| _ => reject(new Error("onstop should never have been called"))), |
| new Promise(r => t.step_timeout(r, 0))]); |
| }, "MediaRecorder will not fire an exception when stopped after having just been stopped"); |
| |
| promise_test(async t => { |
| const stream = createVideoStream(); |
| const recorder = new MediaRecorder(stream); |
| recorder.start(); |
| stream.getVideoTracks()[0].stop(); |
| let event = await new Promise(r => recorder.onstop = r); |
| recorder.stop(); |
| await Promise.race([ |
| new Promise((_, reject) => recorder.onstop = |
| _ => reject(new Error("onstop should never have been called"))), |
| new Promise(r => t.step_timeout(r, 0))]); |
| }, "MediaRecorder will not fire an exception when stopped after having just been spontaneously stopped"); |
| |
| promise_test(async t => { |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); |
| const recorder = new MediaRecorder(stream); |
| let events = []; |
| const startPromise = new Promise(resolve => recorder.onstart = resolve); |
| const stopPromise = new Promise(resolve => recorder.onstop = resolve); |
| |
| startPromise.then(() => events.push("start")); |
| stopPromise.then(() => events.push("stop")); |
| |
| recorder.start(); |
| recorder.stop(); |
| |
| await stopPromise; |
| assert_array_equals(events, ["start", "stop"]); |
| }, "MediaRecorder will fire start event even if stopped synchronously"); |
| |
| promise_test(async t => { |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); |
| const recorder = new MediaRecorder(stream); |
| let events = []; |
| const startPromise = new Promise(resolve => recorder.onstart = resolve); |
| const stopPromise = new Promise(resolve => recorder.onstop = resolve); |
| const errorPromise = new Promise(resolve => recorder.onerror = resolve); |
| const dataPromise = new Promise(resolve => recorder.ondataavailable = resolve); |
| |
| startPromise.then(() => events.push("start")); |
| stopPromise.then(() => events.push("stop")); |
| errorPromise.then(() => events.push("error")); |
| dataPromise.then(() => events.push("data")); |
| |
| recorder.start(); |
| stream.removeTrack(stream.getAudioTracks()[0]); |
| |
| await stopPromise; |
| assert_array_equals(events, ["start", "error", "data", "stop"]); |
| }, "MediaRecorder will fire start event even if a track is removed synchronously"); |
| |
| </script> |
| </body> |
| </html> |