tor-browser

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

MediaStreamTrack-independent-clones.https.html (8182B)


      1 <!doctype html>
      2 <title>MediaStreamTrack clones have independent settings. Assumes Mozilla's fake camera source with 480p and 720p capabilities.</title>
      3 <meta name="timeout" content="long">
      4 <p class="instructions">When prompted, accept to share your video stream.</p>
      5 <script src=/resources/testharness.js></script>
      6 <script src=/resources/testharnessreport.js></script>
      7 <script src=/resources/testdriver.js></script>
      8 <script src=/resources/testdriver-vendor.js></script>
      9 <script src=video-test-helper.js></script>
     10 <script>
     11  "use strict"
     12 
     13  // Native capabilities supported by the fake camera.
     14  const nativeLow = {width: 640, height: 480, frameRate: 30, resizeMode: "none"};
     15  const nativeHigh = {width: 1280, height: 720, frameRate: 10, resizeMode: "none"};
     16 
     17  [
     18    [
     19      [{resizeMode: "none", width: 1000}, {resizeMode: "none", width: 500}],
     20      [nativeHigh, nativeLow],
     21    ],
     22    [
     23      [{resizeMode: "none", height: 500}, {resizeMode: "none", width: 1000}],
     24      [nativeLow, nativeHigh],
     25    ],
     26    [
     27      [{resizeMode: "none", width: 500, height: 500}, {resizeMode: "crop-and-scale", width: 1000, height: 750}],
     28      [nativeLow, {resizeMode: "crop-and-scale", width: 1000, height: 720, frameRate: 10}],
     29    ],
     30    [
     31      [{resizeMode: "crop-and-scale", width: 800, height: 600}, {resizeMode: "none"}],
     32      [{resizeMode: "crop-and-scale", width: 800, height: 600, frameRate: 10}, nativeLow],
     33    ],
     34    [
     35      [{resizeMode: "crop-and-scale", height: 500}, {resizeMode: "crop-and-scale", height: 300}],
     36      [
     37        {resizeMode: "crop-and-scale", width: 889, height: 500, frameRate: 10},
     38        {resizeMode: "crop-and-scale", width: 400, height: 300, frameRate: 30}
     39      ],
     40    ],
     41    [
     42      [
     43        {resizeMode: "crop-and-scale", frameRate: {exact: 5}},
     44        {resizeMode: "crop-and-scale", frameRate: {exact: 1}},
     45      ],
     46      [
     47        {resizeMode: "crop-and-scale", width: 640, height: 480, frameRate: 5},
     48        {resizeMode: "crop-and-scale", width: 640, height: 480, frameRate: 1},
     49      ],
     50      [[2, 7], [0.5, 3]],
     51    ],
     52  ].forEach(
     53    ([
     54      [video, cloneVideo],
     55      [expected, cloneExpected],
     56      [testFramerate, cloneTestFramerate] = [null, null]
     57    ]) => promise_test(async t => {
     58      const stream = await navigator.mediaDevices.getUserMedia({video});
     59      const [track] = stream.getTracks();
     60      const clone = track.clone();
     61      t.add_cleanup(() => {
     62        track.stop();
     63        clone.stop();
     64      });
     65      let settings = track.getSettings();
     66      let cloneSettings = clone.getSettings();
     67      for (const key of Object.keys(expected)) {
     68        assert_equals(settings[key], expected[key], `original: ${key}`);
     69        assert_equals(cloneSettings[key], expected[key], `clone: ${key}`);
     70      }
     71      await clone.applyConstraints(cloneVideo);
     72      settings = track.getSettings();
     73      cloneSettings = clone.getSettings();
     74      for (const key of Object.keys(expected)) {
     75        assert_equals(settings[key], expected[key], `original-post: ${key}`);
     76      }
     77      for (const key of Object.keys(cloneExpected)) {
     78        assert_equals(cloneSettings[key], cloneExpected[key], `clone-post: ${key}`);
     79      }
     80      await test_resolution_equals(t, track, settings.width, settings.height);
     81      await test_resolution_equals(t, clone, cloneSettings.width, cloneSettings.height);
     82      if (testFramerate) {
     83        const [low, high] = testFramerate;
     84        await test_framerate_between_exclusive(t, track, low, high);
     85      }
     86      if (cloneTestFramerate) {
     87        const [low, high] = cloneTestFramerate;
     88        await test_framerate_between_exclusive(t, clone, low, high);
     89      }
     90    }, `gUM gets ${JSON.stringify(expected)} + clone ` +
     91       `${JSON.stringify(cloneExpected)} by ${JSON.stringify(video)} ` +
     92       `and ${JSON.stringify(cloneVideo)}`));
     93 
     94  [
     95    [
     96      [{echoCancellation: true}, {echoCancellation: false, noiseSuppression: true, autoGainControl: true}],
     97      [
     98        {echoCancellation: true, noiseSuppression: true, autoGainControl: true},
     99        {echoCancellation: false, noiseSuppression: true, autoGainControl: true},
    100      ],
    101    ],
    102    [
    103      [{echoCancellation: false, noiseSuppression: false, autoGainControl: false}, {}],
    104      [
    105        {echoCancellation: false, noiseSuppression: false, autoGainControl: false},
    106        {echoCancellation: true, noiseSuppression: true, autoGainControl: true},
    107      ],
    108    ],
    109  ].forEach(
    110    ([
    111      [audio, cloneAudio],
    112      [expected, cloneExpected],
    113    ]) => promise_test(async t => {
    114      const stream = await navigator.mediaDevices.getUserMedia({audio});
    115      const [track] = stream.getTracks();
    116      const clone = track.clone();
    117      t.add_cleanup(() => {
    118        track.stop();
    119        clone.stop();
    120      });
    121      let settings = track.getSettings();
    122      let cloneSettings = clone.getSettings();
    123      for (const key of Object.keys(expected)) {
    124        assert_equals(settings[key], expected[key], `original: ${key}`);
    125        assert_equals(cloneSettings[key], expected[key], `clone: ${key}`);
    126      }
    127      await clone.applyConstraints(cloneAudio);
    128      settings = track.getSettings();
    129      cloneSettings = clone.getSettings();
    130      for (const key of Object.keys(expected)) {
    131        assert_equals(settings[key], expected[key], `original-post: ${key}`);
    132      }
    133      for (const key of Object.keys(cloneExpected)) {
    134        assert_equals(cloneSettings[key], cloneExpected[key], `clone-post: ${key}`);
    135      }
    136    }, `gUM gets ${JSON.stringify(expected)} + clone ` +
    137       `${JSON.stringify(cloneExpected)} by ${JSON.stringify(audio)} ` +
    138       `and ${JSON.stringify(cloneAudio)}`));
    139 
    140 
    141 
    142  promise_test(async t => {
    143    const stream = await navigator.mediaDevices.getUserMedia({video: true});
    144    const [track] = stream.getTracks();
    145    const clone = track.clone();
    146    t.add_cleanup(() => {
    147      track.stop();
    148      clone.stop();
    149    });
    150    try {
    151      await clone.applyConstraints({resizeMode: "none", width: {min: 2000}})
    152    } catch(e) {
    153      assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`);
    154      return;
    155    }
    156    assert_unreached("applyConstraints is rejected with impossible width");
    157  }, "applyConstraints on gUM clone is rejected by resizeMode none and impossible min-width");
    158 
    159  promise_test(async t => {
    160    const stream = await navigator.mediaDevices.getUserMedia({video: true});
    161    const [track] = stream.getTracks();
    162    const clone = track.clone();
    163    t.add_cleanup(() => {
    164      track.stop();
    165      clone.stop();
    166    });
    167    try {
    168      await clone.applyConstraints({resizeMode: "none", width: {max: 200}})
    169    } catch(e) {
    170      assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`);
    171      return;
    172    }
    173    assert_unreached("applyConstraints is rejected with impossible width");
    174  }, "applyConstraints on gUM clone is rejected by resizeMode none and impossible max-width");
    175 
    176  promise_test(async t => {
    177    const stream = await navigator.mediaDevices.getUserMedia({video: true});
    178    const [track] = stream.getTracks();
    179    const clone = track.clone();
    180    t.add_cleanup(() => {
    181      track.stop();
    182      clone.stop();
    183    });
    184    try {
    185      await clone.applyConstraints({resizeMode: "crop-and-scale", width: {min: 2000}})
    186    } catch(e) {
    187      assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`);
    188      return;
    189    }
    190    assert_unreached("applyConstraints is rejected with impossible width");
    191  }, "applyConstraints on gUM clone is rejected by resizeMode crop-and-scale and impossible width");
    192 
    193  promise_test(async t => {
    194    const stream = await navigator.mediaDevices.getUserMedia({video: true});
    195    const [track] = stream.getTracks();
    196    const clone = track.clone();
    197    t.add_cleanup(() => {
    198      track.stop();
    199      clone.stop();
    200    });
    201    try {
    202      await clone.applyConstraints({resizeMode: "crop-and-scale", frameRate: {min: 50}});
    203    } catch(e) {
    204      assert_equals(e.name, "OverconstrainedError", `got error ${e.message}`);
    205      return;
    206    }
    207    assert_unreached("applyConstraints is rejected with impossible fps");
    208  }, "applyConstraints on gUM clone is rejected by resizeMode crop-and-scale impossible fps");
    209 </script>