209 lines
8.7 KiB
HTML
209 lines
8.7 KiB
HTML
<!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>
|