tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaRecorder-stop.html (12379B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4    <title>MediaRecorder Stop</title>
      5    <meta name=variant content="?mimeType=''">
      6    <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
      7    <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
      8    <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
      9    <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
     10    <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
     11    <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
     12    <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
     13    <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
     14    <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
     15    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
     16    <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
     17    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
     18    <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
     19    <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
     20    <meta name=variant content="?mimeType=video/mp4">
     21    <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
     22    <script src="/resources/testharness.js"></script>
     23    <script src="/resources/testharnessreport.js"></script>
     24    <script src="utils/sources.js"></script>
     25 </head>
     26 <body>
     27 <script>
     28    function recordEvents(target, events) {
     29        let arr = [];
     30        for (let ev of events) {
     31            target.addEventListener(ev, _ => arr.push(ev));
     32        }
     33        return arr;
     34    }
     35 
     36    // This function is used to check that elements of |actual| is a sub
     37    // sequence in the |expected| sequence.
     38    function assertSequenceIn(actual, expected) {
     39      let i = 0;
     40      for (event of actual) {
     41        const j = expected.slice(i).indexOf(event);
     42        assert_greater_than_equal(
     43            j, 0, "Sequence element " + event + " is not included in " +
     44            expected.slice(i));
     45        // Ensure duplicates in actual aren't silently accepted by skipping
     46        // past the matched element.
     47        i = j + 1;
     48      }
     49      return true;
     50    }
     51 
     52    function isMimetypeSupported(mimeType, t) {
     53      if (mimeType && !MediaRecorder.isTypeSupported(mimeType)) {
     54        t.done();
     55        return false;
     56      }
     57      return true;
     58    }
     59 
     60    const params = new URLSearchParams(window.location.search);
     61    const mimeType = params.get('mimeType');
     62    const tag = `mimeType "${mimeType}"`;
     63    promise_test(async t => {
     64        if (!isMimetypeSupported(mimeType, t)) {
     65          return;
     66        }
     67 
     68        const {stream: video} = createVideoStream(t);
     69        const recorder = new MediaRecorder(video, {mimeType});
     70        const events = recordEvents(recorder,
     71            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
     72        assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track");
     73        recorder.start();
     74        assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
     75        video.getVideoTracks()[0].stop();
     76        assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following last track ending");
     77        const event = await new Promise(r => recorder.onstop = r);
     78 
     79        assert_equals(event.type, "stop", "the event type should be stop");
     80        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
     81        assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
     82 
     83        // As the test is written, it's not guaranteed that
     84        // onstart/ondataavailable is invoked, but it's fine if they are.
     85        // The stop element is guaranteed to be in events when we get here.
     86        assertSequenceIn(events, ["start", "dataavailable", "stop"]);
     87    }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended");
     88 
     89    promise_test(async t => {
     90        if (!isMimetypeSupported(mimeType, t)) {
     91          return;
     92        }
     93 
     94        const {stream: video} = createVideoStream(t);
     95        const recorder = new MediaRecorder(video, {mimeType});
     96        const events = recordEvents(recorder,
     97            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
     98        recorder.start();
     99        assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
    100        recorder.stop();
    101        assert_equals(recorder.state, "inactive", "MediaRecorder state should be inactive immediately following stop() call");
    102 
    103        const event = await new Promise (r => recorder.onstop = r);
    104        assert_equals(event.type, "stop", "the event type should be stop");
    105        assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
    106        assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
    107 
    108        // As the test is written, it's not guaranteed that
    109        // onstart/ondataavailable is invoked, but it's fine if they are.
    110        // The stop element is guaranteed to be in events when we get here.
    111        assertSequenceIn(events, ["start", "dataavailable", "stop"]);
    112    }, "MediaRecorder will stop recording and fire a stop event when stop() is called");
    113 
    114    promise_test(async t => {
    115        if (!isMimetypeSupported(mimeType, t)) {
    116          return;
    117        }
    118 
    119        const recorder = new MediaRecorder(createVideoStream(t).stream, {mimeType});
    120        recorder.stop();
    121        await Promise.race([
    122            new Promise((_, reject) => recorder.onstop =
    123                _ => reject(new Error("onstop should never have been called"))),
    124            new Promise(r => t.step_timeout(r, 0))]);
    125    }, "MediaRecorder will not fire an exception when stopped after creation");
    126 
    127    promise_test(async t => {
    128        if (!isMimetypeSupported(mimeType, t)) {
    129          return;
    130        }
    131 
    132        const recorder = new MediaRecorder(createVideoStream(t).stream, {mimeType});
    133        recorder.start();
    134        recorder.stop();
    135        const event = await new Promise(r => recorder.onstop = r);
    136        recorder.stop();
    137        await Promise.race([
    138            new Promise((_, reject) => recorder.onstop =
    139                _ => reject(new Error("onstop should never have been called"))),
    140            new Promise(r => t.step_timeout(r, 0))]);
    141    }, "MediaRecorder will not fire an exception when stopped after having just been stopped");
    142 
    143    promise_test(async t => {
    144        if (!isMimetypeSupported(mimeType, t)) {
    145          return;
    146        }
    147 
    148        const {stream} = createVideoStream(t);
    149        const recorder = new MediaRecorder(stream, {mimeType});
    150        recorder.start();
    151        stream.getVideoTracks()[0].stop();
    152        const event = await new Promise(r => recorder.onstop = r);
    153        recorder.stop();
    154        await Promise.race([
    155            new Promise((_, reject) => recorder.onstop =
    156                _ => reject(new Error("onstop should never have been called"))),
    157            new Promise(r => t.step_timeout(r, 0))]);
    158    }, "MediaRecorder will not fire an exception when stopped after having just been spontaneously stopped");
    159 
    160    promise_test(async t => {
    161        if (!isMimetypeSupported(mimeType, t)) {
    162          return;
    163        }
    164 
    165        const {stream} = createAudioVideoStream(t);
    166        const recorder = new MediaRecorder(stream, {mimeType});
    167        const events = [];
    168        const startPromise = new Promise(resolve => recorder.onstart = resolve);
    169        const stopPromise = new Promise(resolve => recorder.onstop = resolve);
    170 
    171        startPromise.then(() => events.push("start"));
    172        stopPromise.then(() => events.push("stop"));
    173 
    174        recorder.start();
    175        recorder.stop();
    176 
    177        await stopPromise;
    178        assert_array_equals(events, ["start", "stop"]);
    179    }, "MediaRecorder will fire start event even if stopped synchronously");
    180 
    181    promise_test(async t => {
    182        if (!isMimetypeSupported(mimeType, t)) {
    183          return;
    184        }
    185 
    186        const {stream} = createAudioVideoStream(t);
    187        const recorder = new MediaRecorder(stream, {mimeType});
    188        const events = [];
    189        const startPromise = new Promise(resolve => recorder.onstart = resolve);
    190        const stopPromise = new Promise(resolve => recorder.onstop = resolve);
    191        const errorPromise = new Promise(resolve => recorder.onerror = resolve);
    192        const dataPromise = new Promise(resolve => recorder.ondataavailable = resolve);
    193 
    194        startPromise.then(() => events.push("start"));
    195        stopPromise.then(() => events.push("stop"));
    196        errorPromise.then(() => events.push("error"));
    197        dataPromise.then(() => events.push("data"));
    198 
    199        recorder.start();
    200        stream.removeTrack(stream.getAudioTracks()[0]);
    201 
    202        await stopPromise;
    203        assert_array_equals(events, ["start", "error", "data", "stop"]);
    204    }, "MediaRecorder will fire start event even if a track is removed synchronously");
    205 
    206    promise_test(async t => {
    207        if (!isMimetypeSupported(mimeType, t)) {
    208          return;
    209        }
    210 
    211        const {stream} = createFlowingAudioVideoStream(t);
    212        const recorder = new MediaRecorder(stream, {mimeType});
    213        const events = recordEvents(recorder,
    214            ["start", "stop", "dataavailable", "pause", "resume", "error"]);
    215        const dataPromise = new Promise(r => recorder.ondataavailable = r);
    216        recorder.start(0);
    217        await dataPromise;
    218        recorder.stop();
    219        await new Promise (r => recorder.onstop = r);
    220        assertSequenceIn(events, ["start", "dataavailable", "stop"]);
    221    }, "MediaRecorder will fire only start and stop events in a basic recording flow.");
    222 
    223    promise_test(async t => {
    224        const {stream} = createFlowingAudioVideoStream(t);
    225        const audioTrack = stream.getAudioTracks()[0];
    226        const videoTrack = stream.getVideoTracks()[0];
    227        t.add_cleanup(() => {
    228            audioTrack.stop();
    229            videoTrack.stop();
    230        });
    231        const recorder = new MediaRecorder(stream);
    232 
    233        let resolve;
    234        const promise = new Promise((resolve_, reject) => {
    235            resolve = resolve_;
    236            t.step_timeout(() => reject("stop event time out"), 2000);
    237        });
    238 
    239        let events = [];
    240        recorder.onstart = () => events.push("start");
    241        recorder.onstop = () => {
    242            events.push("stop");
    243            resolve();
    244        }
    245        recorder.onerror = () => events.push("error");
    246        recorder.ondataavailable = ()=> events.push("data");
    247 
    248        recorder.start();
    249        await new Promise(resolve => t.step_timeout(resolve, 0));
    250 
    251        stream.removeTrack(stream.getAudioTracks()[0]);
    252        recorder.stop();
    253        await promise;
    254        assert_array_equals(events, ["start", "data", "stop"]);
    255    }, "MediaRecorder will not fire an error event if a recorder is stopped synchronously after a track is removed");
    256 
    257    promise_test(async t => {
    258        const {stream} = createFlowingAudioVideoStream(t);
    259        const audioTrack = stream.getAudioTracks()[0];
    260        const videoTrack = stream.getVideoTracks()[0];
    261        t.add_cleanup(() => {
    262            audioTrack.stop();
    263            videoTrack.stop();
    264        });
    265        const recorder = new MediaRecorder(stream);
    266 
    267        let resolve;
    268        const promise = new Promise((resolve_, reject) => {
    269            resolve = resolve_;
    270            t.step_timeout(() => reject("stop event time out"), 2000);
    271        });
    272 
    273        let events = [];
    274        recorder.onstart = ()=> events.push("start");
    275        recorder.onstop = () => {
    276            events.push("stop");
    277            resolve();
    278        }
    279        recorder.onerror = ()=> events.push("error");
    280        recorder.ondataavailable = ()=> events.push("data");
    281 
    282        recorder.start();
    283        await new Promise(resolve => t.step_timeout(resolve, 0));
    284 
    285        audioTrack.stop();
    286        videoTrack.stop();
    287        recorder.stop();
    288        await promise;
    289        assert_array_equals(events, ["start", "data", "stop"]);
    290    }, "MediaRecorder will not fire an error event if a recorder is stopped synchronously after all tracks are ended");
    291 </script>
    292 </body>
    293 </html>