summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/mediacapture-extensions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/mediacapture-extensions
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/mediacapture-extensions')
-rw-r--r--testing/web-platform/tests/mediacapture-extensions/GUM-backgroundBlur.https.html150
-rw-r--r--testing/web-platform/tests/mediacapture-extensions/GUM-faceFraming.https.html150
-rw-r--r--testing/web-platform/tests/mediacapture-extensions/MediaStreamTrack-video-stats.https.html353
3 files changed, 653 insertions, 0 deletions
diff --git a/testing/web-platform/tests/mediacapture-extensions/GUM-backgroundBlur.https.html b/testing/web-platform/tests/mediacapture-extensions/GUM-backgroundBlur.https.html
new file mode 100644
index 0000000000..605a4e0831
--- /dev/null
+++ b/testing/web-platform/tests/mediacapture-extensions/GUM-backgroundBlur.https.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test background blur support</title>
+<link rel="help" href="https://w3c.github.io/mediacapture-extensions/">
+</head>
+<body>
+<h1 class="instructions">Description</h1>
+<p class="instructions">This test checks background blur support.</p>
+<div id='log'></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+"use strict";
+
+const constraintSet = {
+ backgroundBlur: true
+};
+
+Object.keys(constraintSet).forEach(property => {
+ test(t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+ assert_implements_optional(
+ supportedConstraints[property],
+ `Optional property ${property} not in supported constraints`);
+ }, `Test getSupportedConstraints().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ assert_equals(typeof videoTrack.getCapabilities, 'function');
+ const capabilities = videoTrack.getCapabilities();
+ assert_equals(typeof capabilities, 'object');
+
+ if (!supportedConstraints[property]) {
+ assert_false(property in capabilities);
+ }
+
+ assert_implements_optional(
+ property in capabilities,
+ `Optional property ${property} not in capabilities`);
+
+ // Accept [false], [false, true], [true] and [true, false].
+ assert_array_equals(
+ capabilities[property],
+ capabilities[property].length == 1
+ ? [!!capabilities[property][0]]
+ : [!!capabilities[property][0], !capabilities[property][0]]);
+ }, `Test getCapabilities().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ const capabilities = videoTrack.getCapabilities();
+
+ assert_equals(typeof videoTrack.getSettings, 'function');
+ const settings = videoTrack.getSettings();
+ assert_equals(typeof settings, 'object');
+
+ if (!supportedConstraints[property] || !(property in capabilities))
+ assert_false(property in settings);
+
+ assert_implements_optional(
+ property in capabilities,
+ `Optional property ${property} not in capabilities`);
+
+ assert_in_array(settings[property], capabilities[property]);
+ }, `Test getSettings().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ const capabilities = videoTrack.getCapabilities();
+ const constraints = {advanced: [{
+ [property]: constraintSet[property]
+ }]};
+ const oldSettings = videoTrack.getSettings();
+
+ if (supportedConstraints[property] && !(property in capabilities)) {
+ // The user agent supports |constraints| but |videoTrack| is not capable
+ // to apply them.
+ await videoTrack.applyConstraints(constraints).then(
+ () => {
+ assert_unreached('Unexpected success applying constraints');
+ },
+ error => {});
+ } else {
+ // The user agent does not support |constraints| and will ignore them or
+ // the user agent supports |constraints| and |videoTrack| is capable to
+ // apply them.
+ await videoTrack.applyConstraints(constraints).then(
+ () => {},
+ error => {
+ assert_unreached(`Error applying constraints: ${error.message}`);
+ });
+ }
+
+ assert_equals(typeof videoTrack.getConstraints, 'function');
+ const appliedConstraints = videoTrack.getConstraints();
+ assert_equals(typeof appliedConstraints, 'object');
+ const newSettings = videoTrack.getSettings();
+
+ if (!supportedConstraints[property] || !(property in capabilities)) {
+ // The user agent does not support |constraints| and ignored them or
+ // the user agent supports |constraints| but |videoTrack| was not capable
+ // to apply them.
+ assert_object_equals(appliedConstraints, {});
+ } else {
+ // The user agent supports |constraints| and |videoTrack| was capable to
+ // apply them.
+ assert_object_equals(appliedConstraints, constraints);
+ }
+
+ if (!supportedConstraints[property] || !(property in capabilities) ||
+ !capabilities[property].includes(constraintSet[property])) {
+ // The user agent does not support |constraints| and ignored them or
+ // the user agent supports |constraints| but |videoTrack| was not capable
+ // to apply them or the user agent supports |constraints| and
+ // |videoTrack| was capable to apply them but could not satisfy advanced
+ // constraints and skipped them.
+ assert_object_equals(newSettings, oldSettings);
+ } else {
+ // The user agent supports |constraints| and |videoTrack| was capable to
+ // apply them and could satisfy advanced constraints.
+ assert_equals(newSettings[property], constraintSet[property]);
+ }
+ }, `Test applyConstraints() with ${property}`);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/mediacapture-extensions/GUM-faceFraming.https.html b/testing/web-platform/tests/mediacapture-extensions/GUM-faceFraming.https.html
new file mode 100644
index 0000000000..ef45720621
--- /dev/null
+++ b/testing/web-platform/tests/mediacapture-extensions/GUM-faceFraming.https.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test face framing support</title>
+<link rel="help" href="https://w3c.github.io/mediacapture-extensions/">
+</head>
+<body>
+<h1 class="instructions">Description</h1>
+<p class="instructions">This test checks face framing support.</p>
+<div id='log'></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+"use strict";
+
+const constraintSet = {
+ faceFraming: true
+};
+
+Object.keys(constraintSet).forEach(property => {
+ test(t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+ assert_implements_optional(
+ supportedConstraints[property],
+ `Optional property ${property} not in supported constraints`);
+ }, `Test getSupportedConstraints().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ assert_equals(typeof videoTrack.getCapabilities, 'function');
+ const capabilities = videoTrack.getCapabilities();
+ assert_equals(typeof capabilities, 'object');
+
+ if (!supportedConstraints[property]) {
+ assert_false(property in capabilities);
+ }
+
+ assert_implements_optional(
+ property in capabilities,
+ `Optional property ${property} not in capabilities`);
+
+ // Accept [false], [false, true], [true] and [true, false].
+ assert_array_equals(
+ capabilities[property],
+ capabilities[property].length == 1
+ ? [!!capabilities[property][0]]
+ : [!!capabilities[property][0], !capabilities[property][0]]);
+ }, `Test getCapabilities().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ const capabilities = videoTrack.getCapabilities();
+
+ assert_equals(typeof videoTrack.getSettings, 'function');
+ const settings = videoTrack.getSettings();
+ assert_equals(typeof settings, 'object');
+
+ if (!supportedConstraints[property] || !(property in capabilities))
+ assert_false(property in settings);
+
+ assert_implements_optional(
+ property in capabilities,
+ `Optional property ${property} not in capabilities`);
+
+ assert_in_array(settings[property], capabilities[property]);
+ }, `Test getSettings().${property}`);
+
+ promise_test(async t => {
+ const supportedConstraints =
+ navigator.mediaDevices.getSupportedConstraints();
+
+ const stream = await navigator.mediaDevices.getUserMedia({video: true});
+ assert_equals(stream.getAudioTracks().length, 0);
+ assert_equals(stream.getVideoTracks().length, 1);
+ const [videoTrack] = stream.getVideoTracks();
+
+ const capabilities = videoTrack.getCapabilities();
+ const constraints = {
+ [property]: {exact: constraintSet[property]}
+ };
+ const oldSettings = videoTrack.getSettings();
+
+ if (supportedConstraints[property] && !(property in capabilities)) {
+ // The user agent supports |constraints| but |videoTrack| is not capable
+ // to apply them.
+ await videoTrack.applyConstraints(constraints).then(
+ () => {
+ assert_unreached('Unexpected success applying constraints');
+ },
+ error => {});
+ } else {
+ // The user agent does not support |constraints| and will ignore them or
+ // the user agent supports |constraints| and |videoTrack| is capable to
+ // apply them.
+ await videoTrack.applyConstraints(constraints).then(
+ () => {},
+ error => {
+ assert_unreached(`Error applying constraints: ${error.message}`);
+ });
+ }
+
+ assert_equals(typeof videoTrack.getConstraints, 'function');
+ const appliedConstraints = videoTrack.getConstraints();
+ assert_equals(typeof appliedConstraints, 'object');
+ const newSettings = videoTrack.getSettings();
+
+ if (!supportedConstraints[property] || !(property in capabilities)) {
+ // The user agent does not support |constraints| and ignored them or
+ // the user agent supports |constraints| but |videoTrack| was not capable
+ // to apply them.
+ assert_object_equals(appliedConstraints, {});
+ } else {
+ // The user agent supports |constraints| and |videoTrack| was capable to
+ // apply them.
+ assert_object_equals(appliedConstraints, constraints);
+ }
+
+ if (!supportedConstraints[property] || !(property in capabilities) ||
+ !capabilities[property].includes(constraintSet[property])) {
+ // The user agent does not support |constraints| and ignored them or
+ // the user agent supports |constraints| but |videoTrack| was not capable
+ // to apply them or the user agent supports |constraints| and
+ // |videoTrack| was capable to apply them but could not satisfy advanced
+ // constraints and skipped them.
+ assert_object_equals(newSettings, oldSettings);
+ } else {
+ // The user agent supports |constraints| and |videoTrack| was capable to
+ // apply them and could satisfy advanced constraints.
+ assert_equals(newSettings[property], constraintSet[property]);
+ }
+ }, `Test applyConstraints() with ${property}`);
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/mediacapture-extensions/MediaStreamTrack-video-stats.https.html b/testing/web-platform/tests/mediacapture-extensions/MediaStreamTrack-video-stats.https.html
new file mode 100644
index 0000000000..f1b6a2074a
--- /dev/null
+++ b/testing/web-platform/tests/mediacapture-extensions/MediaStreamTrack-video-stats.https.html
@@ -0,0 +1,353 @@
+<!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({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: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({video:true});
+ const [track] = stream.getTracks();
+ t.add_cleanup(() => track.stop());
+
+ const firstTotalFrames = track.stats.totalFrames;
+ // Busy-loop for 100 ms, all within the same task execution cycle.
+ const firstTimeMs = performance.now();
+ while (performance.now() - firstTimeMs < 100) {}
+ // The frame counter should not have changed.
+ assert_equals(track.stats.totalFrames, firstTotalFrames);
+}, `Counters do not increase in the same task execution cycle`);
+
+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: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({
+ 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 = track.stats.toJSON();
+ 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.
+ assert_approx_equals(
+ track.stats.deliveredFrames, disabledSnapshot.deliveredFrames, 1);
+ assert_approx_equals(
+ track.stats.discardedFrames, disabledSnapshot.discardedFrames, 1);
+ assert_approx_equals(
+ track.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;
+ assert_greater_than_equal(track.stats.deliveredFrames,
+ initialStats.deliveredFrames);
+ assert_greater_than_equal(track.stats.discardedFrames,
+ initialStats.discardedFrames);
+ assert_greater_than_equal(track.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 = originalTrack.stats.toJSON();
+
+ // 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 = clonedTrack.stats.toJSON();
+ 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.
+ assert_equals(originalTrack.stats.deliveredFrames,
+ originalTrackInitialStats.deliveredFrames);
+ assert_equals(originalTrack.stats.discardedFrames,
+ originalTrackInitialStats.discardedFrames);
+ assert_equals(originalTrack.stats.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);
+ assert_less_than(clonedTrackStats.deliveredFrames,
+ originalTrack.stats.deliveredFrames);
+ assert_less_than(clonedTrackStats.discardedFrames,
+ originalTrack.stats.discardedFrames);
+ assert_less_than(clonedTrackStats.totalFrames,
+ originalTrack.stats.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 = originalTrack.stats.toJSON();
+ const cloneAStats = cloneA.stats.toJSON();
+ const cloneBStats = cloneB.stats.toJSON();
+ 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({video:true});
+ const [originalTrack] = stream.getTracks();
+ t.add_cleanup(() => originalTrack.stop());
+
+ // Wait for some frames and assert that no frames are discarded.
+ const firstStats = await getFrameStatsUntil(originalTrack, stats =>
+ stats.deliveredFrames > 20);
+ assert_equals(firstStats.discardedFrames, 0);
+ // Make a clone that discards almost all frames. This should not affect the
+ // discarded frames counter of the original track.
+ const clonedTrack = originalTrack.clone();
+ await clonedTrack.applyConstraints({frameRate:{ideal:1}});
+ // Wait for some more frames. There should still be no frames discarded.
+ const secondStats = await getFrameStatsUntil(originalTrack, stats =>
+ stats.deliveredFrames > 40);
+ assert_equals(secondStats.discardedFrames, 0);
+}, `A low FPS clone does not affect the original track's discardedFrames`);
+
+promise_test(async t => {
+ const stream = await navigator.mediaDevices.getUserMedia({audio:true});
+ const [track] = stream.getTracks();
+ t.add_cleanup(() => track.stop());
+
+ assert_equals(track.stats, null);
+}, `track.stats is null on audio tracks`);
+
+promise_test(async t => {
+ const canvas = document.createElement('canvas');
+ const stream = canvas.captureStream(10);
+ const [track] = stream.getTracks();
+ t.add_cleanup(() => track.stop());
+
+ assert_equals(track.stats, null);
+}, `track.stats is null on non-device tracks, such as canvas`);
+
+promise_test(async t => {
+ // getDisplayMedia() requires inducing a user gesture.
+ const p = new Promise(r => button.onclick = r);
+ await test_driver.click(button);
+ await p;
+
+ const stream = await navigator.mediaDevices.getDisplayMedia({video:true});
+ const [track] = stream.getTracks();
+ t.add_cleanup(() => track.stop());
+
+ await getFrameStatsUntil(track, stats => stats.totalFrames > 0)
+}, `track.stats is supported on getDisplayMedia tracks`);
+</script>