Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!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
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
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>