blob: 657237ec2c949c1dcfb2840e7038adccbb186fe6 [file] [log] [blame]
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
async function getFrameStatsUntil(track, condition) {
while (true) {
const stats = await track.getFrameStats();
if (condition(stats)) {
return stats;
}
}
}
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({video: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);
}, `totalFrames increases over time`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({video:true});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
// `deliveredFrames` increments for each deliverable frame, even if the
// `track` does not have any sink.
const firstStats = await getFrameStatsUntil(
track, stats => stats.deliveredFrames > 0);
await getFrameStatsUntil(
track, stats => stats.deliveredFrames > firstStats.deliveredFrames);
}, `deliveredFrames increases, even without sinks`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(track.getSettings().frameRate, 10);
await track.applyConstraints({frameRate:{ideal:10}});
await getFrameStatsUntil(track, stats => stats.discardedFrames > 0);
}, `discardedFrames increases when frameRate decimation is happening`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(track.getSettings().frameRate, 10);
await track.applyConstraints({frameRate:{ideal:10}});
// Wait until we have both delivered and discarded frames.
const stats = await getFrameStatsUntil(track, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// This test assumes that no frames are dropped, otherwise `totalFrames` can
// be greater than the sum of `deliveredFrames` and `discardedFrames`.
assert_equals(stats.totalFrames,
stats.deliveredFrames + stats.discardedFrames);
}, `totalFrames is the sum of deliveredFrames and discardedFrames`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(track.getSettings().frameRate, 10);
await track.applyConstraints({frameRate:{ideal:10}});
// Wait for media to flow before disabling the `track`.
const initialStats = await getFrameStatsUntil(track, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0 &&
stats.totalFrames > 10);
track.enabled = false;
// Upon disabling, the counters are not reset.
const disabledSnapshot = await track.getFrameStats();
assert_greater_than_equal(disabledSnapshot.deliveredFrames,
initialStats.deliveredFrames);
assert_greater_than_equal(disabledSnapshot.discardedFrames,
initialStats.discardedFrames);
assert_greater_than_equal(disabledSnapshot.totalFrames,
initialStats.totalFrames);
// Wait enough time that frames should have been produced.
await new Promise(r => t.step_timeout(r, 500));
// Frame metrics should be frozen, but because `enabled = false` does not
// return a promise, we allow some lee-way in case a frame was still in flight
// during the disabling.
const stats = await track.getFrameStats();
assert_approx_equals(
stats.deliveredFrames, disabledSnapshot.deliveredFrames, 1);
assert_approx_equals(
stats.discardedFrames, disabledSnapshot.discardedFrames, 1);
assert_approx_equals(stats.totalFrames, disabledSnapshot.totalFrames, 1);
}, `Stats are frozen while disabled`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(track.getSettings().frameRate, 10);
await track.applyConstraints({frameRate:{ideal:10}});
// Wait for media to flow before disabling the `track`.
const initialStats = await getFrameStatsUntil(track, stats =>
stats.deliveredFrames > 10 && stats.discardedFrames > 10);
track.enabled = false;
// Re-enable the track. The stats counters should be greater than or equal to
// what they were previously.
track.enabled = true;
const stats = await track.getFrameStats();
assert_greater_than_equal(stats.deliveredFrames,
initialStats.deliveredFrames);
assert_greater_than_equal(stats.discardedFrames,
initialStats.discardedFrames);
assert_greater_than_equal(stats.totalFrames,
initialStats.totalFrames);
}, `Disabling and re-enabling does not reset the counters`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [originalTrack] = stream.getTracks();
t.add_cleanup(() => originalTrack.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(originalTrack.getSettings().frameRate, 10);
await originalTrack.applyConstraints({frameRate:{ideal:10}});
// Wait for media to flow before disabling the `track`.
await getFrameStatsUntil(originalTrack, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
originalTrack.enabled = false;
const originalTrackInitialStats = await originalTrack.getFrameStats();
// Clone the track, its counters should be zero initially.
// This is not racy because the cloned track is also disabled.
const clonedTrack = originalTrack.clone();
t.add_cleanup(() => clonedTrack.stop());
const clonedTrackStats = await clonedTrack.getFrameStats();
assert_equals(clonedTrackStats.deliveredFrames, 0);
assert_equals(clonedTrackStats.discardedFrames, 0);
assert_equals(clonedTrackStats.totalFrames, 0);
// Enabled the cloned track and wait for media to flow.
clonedTrack.enabled = true;
await getFrameStatsUntil(clonedTrack, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// This does not affect the original track's stats, which are still frozen due
// to the original track being disabled.
const originalTrackSecondStats = await originalTrack.getFrameStats();
assert_equals(originalTrackSecondStats.deliveredFrames,
originalTrackInitialStats.deliveredFrames);
assert_equals(originalTrackSecondStats.discardedFrames,
originalTrackInitialStats.discardedFrames);
assert_equals(originalTrackSecondStats.totalFrames,
originalTrackInitialStats.totalFrames);
}, `New stats baselines when a track is cloned from a disabled track`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [originalTrack] = stream.getTracks();
t.add_cleanup(() => originalTrack.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(originalTrack.getSettings().frameRate, 10);
await originalTrack.applyConstraints({frameRate:{ideal:10}});
// Wait for media to flow.
await getFrameStatsUntil(originalTrack, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// 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.deliveredFrames > 0 && stats.discardedFrames > 0);
const originalTrackStats = await originalTrack.getFrameStats();
assert_less_than(clonedTrackStats.deliveredFrames,
originalTrackStats.deliveredFrames);
assert_less_than(clonedTrackStats.discardedFrames,
originalTrackStats.discardedFrames);
assert_less_than(clonedTrackStats.totalFrames,
originalTrackStats.totalFrames);
}, `New stats baselines when a track is cloned from an enabled track`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({
video:{frameRate:{ideal:20}}
});
const [originalTrack] = stream.getTracks();
t.add_cleanup(() => originalTrack.stop());
// Assert test prerequisite is met: frames will be discarded if the track is
// opened with a higher frame rate than we apply after it is opened.
assert_greater_than(originalTrack.getSettings().frameRate, 10);
await originalTrack.applyConstraints({frameRate:{ideal:10}});
// Wait for media to flow.
await getFrameStatsUntil(originalTrack, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// Clone and wait for media to flow.
const cloneA = originalTrack.clone();
t.add_cleanup(() => cloneA.stop());
await getFrameStatsUntil(cloneA, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// Clone the clone and wait for media to flow.
const cloneB = cloneA.clone();
t.add_cleanup(() => cloneB.stop());
await getFrameStatsUntil(cloneB, stats =>
stats.deliveredFrames > 0 && stats.discardedFrames > 0);
// Because every clone reset its counters and every waits for media before
// cloning, this must be true: originalStats > cloneAStats > cloneBStats.
const originalStats = await originalTrack.getFrameStats();
const cloneAStats = await cloneA.getFrameStats();
const cloneBStats = await cloneB.getFrameStats();
assert_greater_than(originalStats.totalFrames, cloneAStats.totalFrames);
assert_greater_than(cloneAStats.totalFrames, cloneBStats.totalFrames);
}, `New stats baselines for the clone of a clone`);
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia({audio:true});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
try {
await track.getFrameStats();
assert_not_reached();
} catch (e) {
assert_equals(e.name, 'NotSupportedError');
}
}, `getFrameStats throws on audio tracks`);
</script>