tor-browser

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

MediaStreamTrack-audio-stats.https.html (8936B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <meta name="timeout" content="long">
      4 <button id="button">User gesture</button>
      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>
     10 'use strict';
     11 
     12 async function getFrameStatsUntil(track, condition) {
     13  while (true) {
     14    const stats = track.stats.toJSON();
     15    if (condition(stats)) {
     16      return stats;
     17    }
     18    // Repeat in the next task execution cycle.
     19    await Promise.resolve();
     20  }
     21 };
     22 
     23 promise_test(async t => {
     24  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
     25  const [track] = stream.getTracks();
     26  t.add_cleanup(() => track.stop());
     27 
     28  const firstStats =
     29    await getFrameStatsUntil(track, stats => stats.totalFrames > 0);
     30  await getFrameStatsUntil(track,
     31    stats => stats.totalFrames > firstStats.totalFrames && stats.totalFramesDuration > firstStats.totalFramesDuration);
     32 }, `totalFrames and totalFramesDuration increase over time`);
     33 
     34 promise_test(async t => {
     35  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
     36  const [track] = stream.getTracks();
     37  t.add_cleanup(() => track.stop());
     38 
     39  // Wait one second for stats
     40  const stats =
     41    await getFrameStatsUntil(track, stats => stats.totalFramesDuration > 1000);
     42  assert_less_than_equal(stats.deliveredFrames, stats.totalFrames);
     43  assert_less_than_equal(stats.deliveredFramesDuration, stats.totalFramesDuration);
     44  assert_greater_than_equal(stats.deliveredFrames, 0);
     45  assert_greater_than_equal(stats.deliveredFramesDuration, 0);
     46 }, `deliveredFrames and deliveredFramesDuration are at most as large as totalFrames and totalFramesDuration`);
     47 
     48 promise_test(async t => {
     49  function assertLatencyStatsInBounds(stats) {
     50    assert_greater_than_equal(stats.maximumLatency, stats.latency);
     51    assert_greater_than_equal(stats.maximumLatency, stats.averageLatency);
     52    assert_less_than_equal(stats.minimumLatency, stats.latency);
     53    assert_less_than_equal(stats.minimumLatency, stats.averageLatency);
     54  };
     55 
     56  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
     57  const [track] = stream.getTracks();
     58  t.add_cleanup(() => track.stop());
     59  const firstStats = track.stats.toJSON();
     60  assertLatencyStatsInBounds(firstStats);
     61 
     62  // Wait one second for a second stats object.
     63  const secondStats =
     64    await getFrameStatsUntil(track, stats => stats.totalFramesDuration - firstStats.totalFramesDuration >= 1000);
     65  assertLatencyStatsInBounds(secondStats);
     66 
     67  // Reset the latency stats and wait one second for a third stats object.
     68  track.stats.resetLatency();
     69  const thirdStats =
     70    await getFrameStatsUntil(track, stats => stats.totalFramesDuration - secondStats.totalFramesDuration >= 1000);
     71  assertLatencyStatsInBounds(thirdStats);
     72 }, `Latency and averageLatency is within the bounds of minimumLatency and maximumLatency`);
     73 
     74 promise_test(async t => {
     75  // This behaviour is defined in
     76  // https://w3c.github.io/mediacapture-extensions/#dom-mediastreamtrackaudiostats-resetlatency
     77  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
     78  const [track] = stream.getTracks();
     79  t.add_cleanup(() => track.stop());
     80 
     81  // Wait one second for stats
     82  const stats =
     83    await getFrameStatsUntil(track, stats => stats.totalFramesDuration > 1000);
     84  track.stats.resetLatency();
     85  assert_equals(track.stats.latency, stats.latency);
     86  assert_equals(track.stats.averageLatency, stats.latency);
     87  assert_equals(track.stats.minimumLatency, stats.latency);
     88  assert_equals(track.stats.maximumLatency, stats.latency);
     89 }, `Immediately after resetLatency(), latency, averageLatency, minimumLatency and maximumLatency are equal to the most recent latency.`);
     90 
     91 promise_test(async t => {
     92  // This behaviour is defined in
     93  // https://w3c.github.io/mediacapture-extensions/#dfn-expose-audio-frame-counters-steps
     94  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
     95  const [track] = stream.getTracks();
     96  t.add_cleanup(() => track.stop());
     97 
     98  // Wait until we have meaningful data
     99  await getFrameStatsUntil(track, stats => stats.totalFrames > 0);
    100  const firstStats = track.stats.toJSON();
    101 
    102  // Synchronously wait 500 ms.
    103  const start = performance.now();
    104  while(performance.now() - start < 500);
    105 
    106  // The stats should still be the same, despite the time difference.
    107  const secondStats = track.stats.toJSON();
    108  assert_equals(JSON.stringify(firstStats), JSON.stringify(secondStats));
    109 }, `Stats do not change within the same task execution cycle.`);
    110 
    111 promise_test(async t => {
    112  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    113  const [track] = stream.getTracks();
    114  t.add_cleanup(() => track.stop());
    115 
    116  // Wait for media to flow before disabling the `track`.
    117  const initialStats = await getFrameStatsUntil(track, stats => stats.totalFrames > 0);
    118  track.enabled = false;
    119  // Upon disabling, the counters are not reset.
    120  const disabledSnapshot = track.stats.toJSON();
    121  assert_greater_than_equal(disabledSnapshot.totalFramesDuration,
    122                            initialStats.totalFramesDuration);
    123 
    124  await new Promise(r => t.step_timeout(r, 4000));
    125 
    126  // Frame metrics should be frozen, but because `enabled = false` does not
    127  // return a promise, we allow some lee-way in case som buffers were still in flight
    128  // during the disabling.
    129  assert_approx_equals(
    130      track.stats.totalFramesDuration, disabledSnapshot.totalFramesDuration, 1000);
    131 }, `Stats are frozen while disabled`);
    132 
    133 promise_test(async t => {
    134  const stream = await navigator.mediaDevices.getUserMedia({audio:true});
    135  const [track] = stream.getTracks();
    136  t.add_cleanup(() => track.stop());
    137 
    138  const a = track.stats;
    139  await getFrameStatsUntil(track, stats => stats.totalFrames > 0);
    140  const b = track.stats;
    141  // The counters may have changed, but `a` and `b` are still the same object.
    142  assert_equals(a, b);
    143 }, `SameObject policy applies`);
    144 
    145 promise_test(async t => {
    146  const stream = await navigator.mediaDevices.getUserMedia({audio:true});
    147  const [track] = stream.getTracks();
    148  t.add_cleanup(() => track.stop());
    149 
    150  // Hold a reference directly to the [SameObject] stats, bypassing the
    151  // `track.stats` getter in the subsequent getting of `totalFrames`.
    152  const stats = track.stats;
    153  const firstTotalFrames = stats.totalFrames;
    154  while (stats.totalFrames == firstTotalFrames) {
    155    await Promise.resolve();
    156  }
    157  assert_greater_than(stats.totalFrames, firstTotalFrames);
    158 }, `Counters increase even if we don't call the track.stats getter`);
    159 
    160 promise_test(async t => {
    161  const stream = await navigator.mediaDevices.getUserMedia({audio:true});
    162  const [track] = stream.getTracks();
    163  t.add_cleanup(() => track.stop());
    164 
    165  // Wait for 500 ms of audio to flow before disabling the `track`.
    166  const initialStats = await getFrameStatsUntil(track, stats =>
    167      stats.totalFramesDuration > 500);
    168  track.enabled = false;
    169 
    170  // Re-enable the track. The stats counters should be greater than or equal to
    171  // what they were previously.
    172  track.enabled = true;
    173  assert_greater_than_equal(track.stats.totalFrames,
    174                            initialStats.totalFrames);
    175  assert_greater_than_equal(track.stats.totalFramesDuration,
    176                            initialStats.totalFramesDuration);
    177  // This should be true even in the next task execution cycle.
    178  await Promise.resolve();
    179  assert_greater_than_equal(track.stats.totalFrames,
    180                            initialStats.totalFrames);
    181  assert_greater_than_equal(track.stats.totalFramesDuration,
    182                            initialStats.totalFramesDuration);
    183 }, `Disabling and re-enabling does not reset the counters`);
    184 
    185 promise_test(async t => {
    186  const stream = await navigator.mediaDevices.getUserMedia({audio:true});
    187  const [originalTrack] = stream.getTracks();
    188  t.add_cleanup(() => originalTrack.stop());
    189 
    190  // Wait for 500 ms of audio to flow.
    191  await getFrameStatsUntil(originalTrack, stats =>
    192      stats.totalFramesDuration > 500);
    193 
    194  // Clone the track. While its counters should initially be zero, it would be
    195  // racy to assert that they are exactly zero because media is flowing.
    196  const clonedTrack = originalTrack.clone();
    197  t.add_cleanup(() => clonedTrack.stop());
    198 
    199  // Ensure that as media continues to flow, the cloned track will necessarily
    200  // have less frames than the original track on all accounts since its counters
    201  // will have started from zero.
    202  const clonedTrackStats = await getFrameStatsUntil(clonedTrack, stats =>
    203      stats.totalFramesDuration > 0);
    204  assert_less_than(clonedTrackStats.totalFrames,
    205                    originalTrack.stats.totalFrames);
    206  assert_less_than(clonedTrackStats.totalFramesDuration,
    207                    originalTrack.stats.totalFramesDuration);
    208 }, `New stats baselines when a track is cloned from an enabled track`);
    209 </script>