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