Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1605)

Unified Diff: content/renderer/media/media_stream_constraints_util_video_source_unittest.cc

Issue 2668023002: Spec-compatible algorithm for selecting video source and settings. (Closed)
Patch Set: address hbos' comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/renderer/media/media_stream_constraints_util_video_source.cc ('k') | content/test/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/renderer/media/media_stream_constraints_util_video_source_unittest.cc
diff --git a/content/renderer/media/media_stream_constraints_util_video_source_unittest.cc b/content/renderer/media/media_stream_constraints_util_video_source_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c8e697ce5a5dfa1d75879fb233b23eb24b219875
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_video_source_unittest.cc
@@ -0,0 +1,1277 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/media_stream_constraints_util_video_source.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/optional.h"
+#include "content/renderer/media/media_stream_video_source.h"
+#include "content/renderer/media/mock_constraint_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+
+namespace content {
+
+namespace {
+
+const char kDeviceID1[] = "fake_device_1";
+const char kDeviceID2[] = "fake_device_2";
+const char kDeviceID3[] = "fake_device_3";
+}
+
+class MediaStreamConstraintsUtilVideoSourceTest : public testing::Test {
+ public:
+ void SetUp() override {
+ // Default device. It is default because it is the first in the enumeration.
+ ::mojom::VideoInputDeviceCapabilitiesPtr device =
+ ::mojom::VideoInputDeviceCapabilities::New();
+ device->device_id = kDeviceID1;
+ device->facing_mode = ::mojom::FacingMode::NONE;
+ device->formats = {
+ media::VideoCaptureFormat(gfx::Size(200, 200), 40.0f,
+ media::PIXEL_FORMAT_I420),
+ // This entry is is the closest to defaults.
+ media::VideoCaptureFormat(gfx::Size(500, 500), 40.0f,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(1000, 1000), 20.0f,
+ media::PIXEL_FORMAT_I420),
+ };
+ capabilities_.device_capabilities.push_back(std::move(device));
+
+ // A low-resolution device.
+ device = ::mojom::VideoInputDeviceCapabilities::New();
+ device->device_id = kDeviceID2;
+ device->facing_mode = ::mojom::FacingMode::ENVIRONMENT;
+ device->formats = {
+ media::VideoCaptureFormat(gfx::Size(40, 30), 20.0f,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(320, 240), 30.0f,
+ media::PIXEL_FORMAT_I420),
+ // This format has defaults for all settings
+ media::VideoCaptureFormat(
+ gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+ MediaStreamVideoSource::kDefaultHeight),
+ MediaStreamVideoSource::kDefaultFrameRate,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(800, 600), 20.0f,
+ media::PIXEL_FORMAT_I420),
+ };
+ capabilities_.device_capabilities.push_back(std::move(device));
+
+ // A high-resolution device.
+ device = ::mojom::VideoInputDeviceCapabilities::New();
+ device->device_id = kDeviceID3;
+ device->facing_mode = ::mojom::FacingMode::USER;
+ device->formats = {
+ media::VideoCaptureFormat(gfx::Size(320, 240), 10.0f,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(640, 480), 10.0f,
+ media::PIXEL_FORMAT_I420),
+ // This format has defaults for all settings
+ media::VideoCaptureFormat(
+ gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+ MediaStreamVideoSource::kDefaultHeight),
+ MediaStreamVideoSource::kDefaultFrameRate,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(1280, 720), 60.0f,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(1920, 1080), 60.0f,
+ media::PIXEL_FORMAT_I420),
+ media::VideoCaptureFormat(gfx::Size(2304, 1536), 10.0f,
+ media::PIXEL_FORMAT_I420),
+ };
+ capabilities_.device_capabilities.push_back(std::move(device));
+
+ capabilities_.power_line_capabilities = {
+ media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ media::PowerLineFrequency::FREQUENCY_50HZ,
+ media::PowerLineFrequency::FREQUENCY_60HZ,
+ };
+
+ default_device_ = capabilities_.device_capabilities[0].get();
+ low_res_device_ = capabilities_.device_capabilities[1].get();
+ high_res_device_ = capabilities_.device_capabilities[2].get();
+ default_closest_format_ = &default_device_->formats[1];
+ low_res_closest_format_ = &low_res_device_->formats[2];
+ high_res_closest_format_ = &high_res_device_->formats[2];
+ high_res_highest_format_ = &high_res_device_->formats[5];
+ }
+
+ protected:
+ VideoCaptureSourceSelectionResult SelectSettings() {
+ blink::WebMediaConstraints constraints =
+ constraint_factory_.CreateWebMediaConstraints();
+ return SelectVideoCaptureSourceSettings(capabilities_, constraints);
+ }
+
+ VideoCaptureCapabilities capabilities_;
+ const mojom::VideoInputDeviceCapabilities* default_device_;
+ const mojom::VideoInputDeviceCapabilities* low_res_device_;
+ const mojom::VideoInputDeviceCapabilities* high_res_device_;
+ // Closest formats to the default settings.
+ const media::VideoCaptureFormat* default_closest_format_;
+ const media::VideoCaptureFormat* low_res_closest_format_;
+ const media::VideoCaptureFormat* high_res_closest_format_;
+ const media::VideoCaptureFormat* high_res_highest_format_;
+
+ MockConstraintFactory constraint_factory_;
+};
+
+// The Unconstrained test checks the default selection criteria.
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, Unconstrained) {
+ constraint_factory_.Reset();
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Should select the default device with closest-to-default settings.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(default_device_->facing_mode, result.settings.facing_mode());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+}
+
+// The "Overconstrained" tests verify that failure of any single required
+// constraint results in failure to select a candidate.
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, OverconstrainedOnDeviceID) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().deviceId.setExact(
+ blink::WebString::fromASCII("NONEXISTING"));
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().deviceId.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, OverconstrainedOnFacingMode) {
+ constraint_factory_.Reset();
+ // No device in |capabilities_| has facing mode equal to LEFT.
+ constraint_factory_.basic().facingMode.setExact(
+ blink::WebString::fromASCII("left"));
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().facingMode.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, OverconstrainedOnHeight) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().height.setExact(123467890);
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().height.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().height.setMin(123467890);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().height.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().height.setMax(0);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().height.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, OverconstrainedOnWidth) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().width.setExact(123467890);
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().width.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().width.setMin(123467890);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().width.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().width.setMax(0);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().width.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest,
+ OverconstrainedOnAspectRatio) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().aspectRatio.setExact(123467890.0);
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().aspectRatio.setMin(123467890.0);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ // This value is lower than the minimum supported by sources.
+ double kLowAspectRatio = 0.01;
+ constraint_factory_.basic().aspectRatio.setMax(kLowAspectRatio);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().aspectRatio.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, OverconstrainedOnFrameRate) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().frameRate.setExact(123467890.0);
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().frameRate.setMin(123467890.0);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().frameRate.setMax(0.0);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().frameRate.name(),
+ result.failed_constraint_name);
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest,
+ OverconstrainedOnPowerLineFrequency) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().googPowerLineFrequency.setExact(123467890);
+ auto result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().googPowerLineFrequency.setMin(123467890);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
+ result.failed_constraint_name);
+
+ constraint_factory_.Reset();
+ constraint_factory_.basic().googPowerLineFrequency.setMax(-1);
+ result = SelectSettings();
+ EXPECT_FALSE(result.has_value());
+ EXPECT_EQ(constraint_factory_.basic().googPowerLineFrequency.name(),
+ result.failed_constraint_name);
+}
+
+// The "Mandatory" and "Ideal" tests check that various selection criteria work
+// for each individual constraint in the basic constraint set.
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryDeviceID) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().deviceId.setExact(
+ blink::WebString::fromASCII(default_device_->device_id));
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ result.settings.power_line_frequency());
+
+ constraint_factory_.basic().deviceId.setExact(
+ blink::WebString::fromASCII(low_res_device_->device_id));
+ result = SelectSettings();
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+ EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ result.settings.power_line_frequency());
+
+ constraint_factory_.basic().deviceId.setExact(
+ blink::WebString::fromASCII(high_res_device_->device_id));
+ result = SelectSettings();
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_closest_format_, result.settings.format());
+ EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ result.settings.power_line_frequency());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryFacingMode) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().facingMode.setExact(
+ blink::WebString::fromASCII("environment"));
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(::mojom::FacingMode::ENVIRONMENT, result.settings.facing_mode());
+ // Only the low-res device supports environment facing mode. Should select
+ // default settings for everything else.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+ EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ result.settings.power_line_frequency());
+
+ constraint_factory_.basic().facingMode.setExact(
+ blink::WebString::fromASCII("user"));
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(::mojom::FacingMode::USER, result.settings.facing_mode());
+ // Only the high-res device supports user facing mode. Should select default
+ // settings for everything else.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_closest_format_, result.settings.format());
+ EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
+ result.settings.power_line_frequency());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryPowerLineFrequency) {
+ constraint_factory_.Reset();
+ const media::PowerLineFrequency kPowerLineFrequencies[] = {
+ media::PowerLineFrequency::FREQUENCY_50HZ,
+ media::PowerLineFrequency::FREQUENCY_60HZ};
+ for (auto power_line_frequency : kPowerLineFrequencies) {
+ constraint_factory_.basic().googPowerLineFrequency.setExact(
+ static_cast<long>(power_line_frequency));
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(power_line_frequency, result.settings.power_line_frequency());
+ // The default device and settings closest to the default should be
+ // selected.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(default_device_->facing_mode, result.settings.facing_mode());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryExactHeight) {
+ constraint_factory_.Reset();
+ const int kHeight = MediaStreamVideoSource::kDefaultHeight;
+ constraint_factory_.basic().height.setExact(kHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested height. The algorithm
+ // should prefer the first device that supports the requested height natively,
+ // which is the low-res device.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kHeight, result.settings.GetHeight());
+
+ const int kLargeHeight = 1500;
+ constraint_factory_.basic().height.setExact(kLargeHeight);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Only the high-res device at the highest resolution supports the requested
+ // height, even if not natively.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMinHeight) {
+ constraint_factory_.Reset();
+ const int kHeight = MediaStreamVideoSource::kDefaultHeight;
+ constraint_factory_.basic().height.setMin(kHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested height range. The
+ // algorithm should prefer the default device.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_LE(kHeight, result.settings.GetHeight());
+
+ const int kLargeHeight = 1500;
+ constraint_factory_.basic().height.setMin(kLargeHeight);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Only the high-res device at the highest resolution supports the requested
+ // height range.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMaxHeight) {
+ constraint_factory_.Reset();
+ const int kLowHeight = 20;
+ constraint_factory_.basic().height.setMax(kLowHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested height range. The
+ // algorithm should prefer the settings that natively exceed the requested
+ // maximum by the lowest amount. In this case it is the low-res device.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(low_res_device_->formats[0], result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryHeightRange) {
+ constraint_factory_.Reset();
+ {
+ const int kMinHeight = 480;
+ const int kMaxHeight = 720;
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetHeight(), kMinHeight);
+ EXPECT_LE(result.settings.GetHeight(), kMaxHeight);
+ // All devices in |capabilities_| support the constraint range. The
+ // algorithm should prefer the default device since it has at least one
+ // native format (the closest-to-default format) included in the requested
+ // range.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const int kMinHeight = 550;
+ const int kMaxHeight = 650;
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetHeight(), kMinHeight);
+ EXPECT_LE(result.settings.GetHeight(), kMaxHeight);
+ // In this case, the algorithm should prefer the low-res device since it is
+ // the first device with a native format (800x600) included in the requested
+ // range.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(800, result.settings.GetWidth());
+ EXPECT_EQ(600, result.settings.GetHeight());
+ }
+
+ {
+ const int kMinHeight = 700;
+ const int kMaxHeight = 800;
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetHeight(), kMinHeight);
+ EXPECT_LE(result.settings.GetHeight(), kMaxHeight);
+ // In this case, the algorithm should prefer the high-res device since it is
+ // the only device with a native format (1280x720) included in the requested
+ // range.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, IdealHeight) {
+ constraint_factory_.Reset();
+ {
+ const int kIdealHeight = 480;
+ constraint_factory_.basic().height.setIdeal(kIdealHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm should select the first device that supports the ideal
+ // height natively.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kIdealHeight, result.settings.GetHeight());
+ }
+
+ {
+ const int kIdealHeight = 481;
+ constraint_factory_.basic().height.setIdeal(kIdealHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // In this case, the default device is selected because it can satisfy the
+ // ideal at a lower cost than the other devices (500 vs 600 or 720).
+ // Note that a native resolution of 480 is further from the ideal than
+ // 500 cropped to 480.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const int kIdealHeight = 1079;
+ constraint_factory_.basic().height.setIdeal(kIdealHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // In this case, the high-res device has two configurations that satisfy
+ // the ideal value (1920x1080 and 2304x1536). Select the one with shortest
+ // native distance to the ideal value (1920x1080).
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+ }
+
+ {
+ const int kIdealHeight = 1200;
+ constraint_factory_.basic().height.setIdeal(kIdealHeight);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm must the select the only device that can satisfy the ideal,
+ // which is the high-res device at the highest resolution.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryExactWidth) {
+ constraint_factory_.Reset();
+ const int kWidth = 640;
+ constraint_factory_.basic().width.setExact(kWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested width. The algorithm
+ // should prefer the first device that supports the requested width natively,
+ // which is the low-res device.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kWidth, result.settings.GetWidth());
+
+ const int kLargeWidth = 2000;
+ constraint_factory_.basic().width.setExact(kLargeWidth);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_LE(kLargeWidth, result.settings.GetWidth());
+ // Only the high-res device at the highest resolution supports the requested
+ // width, even if not natively.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMinWidth) {
+ constraint_factory_.Reset();
+ const int kWidth = 640;
+ constraint_factory_.basic().width.setMin(kWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested width range. The
+ // algorithm should prefer the default device at 1000x1000, which is the
+ // first configuration that satisfies the minimum width.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_LE(kWidth, result.settings.GetWidth());
+ EXPECT_EQ(1000, result.settings.GetWidth());
+ EXPECT_EQ(1000, result.settings.GetHeight());
+
+ const int kLargeWidth = 2000;
+ constraint_factory_.basic().width.setMin(kLargeWidth);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Only the high-res device at the highest resolution supports the requested
+ // minimum width.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_LE(kLargeWidth, result.settings.GetWidth());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMaxWidth) {
+ constraint_factory_.Reset();
+ const int kLowWidth = 30;
+ constraint_factory_.basic().width.setMax(kLowWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested width range. The
+ // algorithm should prefer the settings that natively exceed the requested
+ // maximum by the lowest amount. In this case it is the low-res device at its
+ // lowest resolution.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(low_res_device_->formats[0], result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryWidthRange) {
+ constraint_factory_.Reset();
+ {
+ const int kMinWidth = 640;
+ const int kMaxWidth = 1280;
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetWidth(), kMinWidth);
+ EXPECT_LE(result.settings.GetWidth(), kMaxWidth);
+ // All devices in |capabilities_| support the constraint range. The
+ // algorithm should prefer the default device since it has at least one
+ // native format (1000x1000) included in the requested range.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1000, result.settings.GetWidth());
+ EXPECT_EQ(1000, result.settings.GetHeight());
+ }
+
+ {
+ const int kMinWidth = 750;
+ const int kMaxWidth = 850;
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetWidth(), kMinWidth);
+ EXPECT_LE(result.settings.GetWidth(), kMaxWidth);
+ // In this case, the algorithm should prefer the low-res device since it is
+ // the first device with a native format (800x600) included in the requested
+ // range.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(800, result.settings.GetWidth());
+ EXPECT_EQ(600, result.settings.GetHeight());
+ }
+
+ {
+ const int kMinWidth = 1900;
+ const int kMaxWidth = 2000;
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetWidth(), kMinWidth);
+ EXPECT_LE(result.settings.GetWidth(), kMaxWidth);
+ // In this case, the algorithm should prefer the high-res device since it is
+ // the only device with a native format (1920x1080) included in the
+ // requested range.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, IdealWidth) {
+ constraint_factory_.Reset();
+ {
+ const int kIdealWidth = 320;
+ constraint_factory_.basic().width.setIdeal(kIdealWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm should select the first device that supports the ideal
+ // width natively, which is the low-res device at 320x240.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kIdealWidth, result.settings.GetWidth());
+ }
+
+ {
+ const int kIdealWidth = 321;
+ constraint_factory_.basic().width.setIdeal(kIdealWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // In this case, the default device is selected because it can satisfy the
+ // ideal at a lower cost than the other devices (500 vs 640).
+ // Note that a native resolution of 320 is further from the ideal value of
+ // 321 than 500 cropped to 321.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const int kIdealWidth = 2000;
+ constraint_factory_.basic().width.setIdeal(kIdealWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm must the select the only device that can satisfy the ideal.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+
+ {
+ const int kIdealWidth = 3000;
+ constraint_factory_.basic().width.setIdeal(kIdealWidth);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm must the select the device and setting with less distance
+ // to the ideal.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryExactFrameRate) {
+ constraint_factory_.Reset();
+ const double kFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
+ constraint_factory_.basic().frameRate.setExact(kFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested frame rate. The
+ // algorithm should prefer the first device that supports the requested frame
+ // rate natively, which is the low-res device at 640x480x30Hz.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kFrameRate, result.settings.GetFrameRate());
+ EXPECT_EQ(640, result.settings.GetWidth());
+ EXPECT_EQ(480, result.settings.GetHeight());
+
+ const double kLargeFrameRate = 50;
+ constraint_factory_.basic().frameRate.setExact(kLargeFrameRate);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Only the high-res device supports the requested frame rate, even if not
+ // natively. The least expensive configuration that supports the requested
+ // frame rate is 1280x720x60Hz.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(60.0, result.settings.GetFrameRate());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMinFrameRate) {
+ constraint_factory_.Reset();
+ const double kFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
+ constraint_factory_.basic().frameRate.setMin(kFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested frame-rate range. The
+ // algorithm should prefer the default device.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ // The format closest to the default satisfies the constraint.
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ const double kLargeFrameRate = 50;
+ constraint_factory_.basic().frameRate.setMin(kLargeFrameRate);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // Only the high-res device supports the requested frame-rate range.
+ // The least expensive configuration is 1280x720x60Hz.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_LE(kLargeFrameRate, result.settings.GetFrameRate());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMaxFrameRate) {
+ constraint_factory_.Reset();
+ const double kLowFrameRate = 10;
+ constraint_factory_.basic().frameRate.setMax(kLowFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // All devices in |capabilities_| support the requested frame-rate range. The
+ // algorithm should prefer the settings that natively exceed the requested
+ // maximum by the lowest amount. In this case it is the high-res device with
+ // default resolution .
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(kLowFrameRate, result.settings.GetFrameRate());
+ EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight,
+ result.settings.GetHeight());
+ EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.settings.GetWidth());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryFrameRateRange) {
+ constraint_factory_.Reset();
+ {
+ const double kMinFrameRate = 10;
+ const double kMaxFrameRate = 40;
+ constraint_factory_.basic().frameRate.setMin(kMinFrameRate);
+ constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_LE(kMinFrameRate, result.settings.GetFrameRate());
+ EXPECT_GE(kMaxFrameRate, result.settings.GetFrameRate());
+ // All devices in |capabilities_| support the constraint range. The
+ // algorithm should prefer the default device since its closest-to-default
+ // format has a frame rate included in the requested range.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kMinFrameRate = 25;
+ const double kMaxFrameRate = 35;
+ constraint_factory_.basic().frameRate.setMin(kMinFrameRate);
+ constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetFrameRate(), kMinFrameRate);
+ EXPECT_LE(result.settings.GetFrameRate(), kMaxFrameRate);
+ // In this case, the algorithm should prefer the low-res device since it is
+ // the first device with a native frame rate included in the requested
+ // range. The default resolution should be preferred as secondary criterion.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kMinFrameRate = 50;
+ const double kMaxFrameRate = 70;
+ constraint_factory_.basic().frameRate.setMin(kMinFrameRate);
+ constraint_factory_.basic().frameRate.setMax(kMaxFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ EXPECT_GE(result.settings.GetFrameRate(), kMinFrameRate);
+ EXPECT_LE(result.settings.GetFrameRate(), kMaxFrameRate);
+ // In this case, the algorithm should prefer the high-res device since it is
+ // the only device with a native format included in the requested range.
+ // The 1280x720 resolution should be selected due to closeness to default
+ // settings, which is the second tie-breaker criterion that applies.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, IdealFrameRate) {
+ constraint_factory_.Reset();
+ {
+ const double kIdealFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
+ constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm should select the first configuration that supports the
+ // ideal frame rate natively, which is the low-res device. Default
+ // resolution should be selected as secondary criterion.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kIdealFrameRate = 31;
+ constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // In this case, the default device is selected because it can satisfy the
+ // ideal at a lower cost than the other devices (40 vs 60).
+ // Note that a native frame rate of 30 is further from the ideal than
+ // 31 adjusted to 30.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kIdealFrameRate = 55;
+ constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The high-res device format 1280x720x60.0 must be selected because its
+ // frame rate can satisfy the ideal frame rate and has resolution closest
+ // to the default.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+ EXPECT_EQ(60, result.settings.GetFrameRate());
+ }
+
+ {
+ const double kIdealFrameRate = 100;
+ constraint_factory_.basic().frameRate.setIdeal(kIdealFrameRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The algorithm must select settings with frame rate closest to the ideal.
+ // The high-res device format 1280x720x60.0 must be selected because its
+ // frame rate it closest to the ideal value and it has resolution closest to
+ // the default.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+ EXPECT_EQ(60, result.settings.GetFrameRate());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryExactAspectRatio) {
+ constraint_factory_.Reset();
+ const double kAspectRatio = 4.0 / 3.0;
+ constraint_factory_.basic().aspectRatio.setExact(kAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double min_width = 1.0;
+ double max_width = result.settings.GetWidth();
+ double min_height = 1.0;
+ double max_height = result.settings.GetHeight();
+ double min_aspect_ratio = min_width / max_height;
+ double max_aspect_ratio = max_width / min_height;
+ // The requested aspect ratio must be within the supported range.
+ EXPECT_GE(kAspectRatio, min_aspect_ratio);
+ EXPECT_LE(kAspectRatio, max_aspect_ratio);
+ // All devices in |capabilities_| support the requested aspect ratio.
+ // The algorithm should prefer the first device that supports the requested
+ // aspect ratio.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ const long kMinWidth = 500;
+ const long kMaxWidth = 1000;
+ const long kMaxHeight = 500;
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ constraint_factory_.basic().aspectRatio.setExact(kAspectRatio);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ min_width = std::max(1L, kMinWidth);
+ max_width = std::min(result.settings.GetWidth(), kMaxWidth);
+ min_height = 1.0;
+ max_height = std::min(result.settings.GetHeight(), kMaxHeight);
+ min_aspect_ratio = min_width / max_height;
+ max_aspect_ratio = max_width / min_height;
+ // The requested aspect ratio must be within the supported range.
+ EXPECT_GE(kAspectRatio, min_aspect_ratio);
+ EXPECT_LE(kAspectRatio, max_aspect_ratio);
+ // The default device can support the requested aspect ratio with the default
+ // settings (500x500) using cropping.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ const long kMinHeight = 480;
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ constraint_factory_.basic().aspectRatio.setExact(kAspectRatio);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ min_width = std::max(1L, kMinWidth);
+ max_width = std::min(result.settings.GetWidth(), kMaxWidth);
+ min_height = std::max(1L, kMinHeight);
+ max_height = std::min(result.settings.GetHeight(), kMaxHeight);
+ min_aspect_ratio = min_width / max_height;
+ max_aspect_ratio = max_width / min_height;
+ // The requested aspect ratio must be within the supported range.
+ EXPECT_GE(kAspectRatio, min_aspect_ratio);
+ EXPECT_LE(kAspectRatio, max_aspect_ratio);
+ // Given resolution constraints, the default device with closest-to-default
+ // settings cannot satisfy the required aspect ratio.
+ // The first device that can do it is the low-res device with a native
+ // resolution of 640x480. Higher resolutions for the default device are more
+ // penalized by the constraints than the default native resolution of the
+ // low-res device.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMinAspectRatio) {
+ constraint_factory_.Reset();
+ const double kAspectRatio = 4.0 / 3.0;
+ constraint_factory_.basic().aspectRatio.setMin(kAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double max_width = result.settings.GetWidth();
+ double min_height = 1.0;
+ double max_aspect_ratio = max_width / min_height;
+ // Minimum constraint aspect ratio must be less than or equal to the maximum
+ // supported by the source.
+ EXPECT_LE(kAspectRatio, max_aspect_ratio);
+ // All devices in |capabilities_| support the requested aspect-ratio range.
+ // The algorithm should prefer the first device that supports the requested
+ // aspect-ratio range, which in this case is the default device.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ const long kMinWidth = 500;
+ const long kMaxWidth = 1000;
+ const long kMinHeight = 480;
+ const long kMaxHeight = 500;
+ constraint_factory_.basic().width.setMin(kMinWidth);
+ constraint_factory_.basic().width.setMax(kMaxWidth);
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ constraint_factory_.basic().aspectRatio.setMin(kAspectRatio);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ max_width = std::min(result.settings.GetWidth(), kMaxWidth);
+ min_height = std::max(1L, kMinHeight);
+ max_aspect_ratio = max_width / min_height;
+ // Minimum constraint aspect ratio must be less than or equal to the minimum
+ // supported by the source.
+ EXPECT_LE(kAspectRatio, max_aspect_ratio);
+ // Given resolution constraints, the default device with closest-to-default
+ // settings cannot satisfy the required minimum aspect ratio (maximum would
+ // be 500/480). The first device that can is the low-res device with a native
+ // resolution of 640x480.
+ // Higher resolutions for the default device are more penalized by the
+ // constraints than the default native resolution of the low-res device.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*low_res_closest_format_, result.settings.format());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryMaxAspectRatio) {
+ constraint_factory_.Reset();
+ const double kAspectRatio = 0.5;
+ constraint_factory_.basic().aspectRatio.setMax(kAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double min_width = 1.0;
+ double max_height = result.settings.GetHeight();
+ double min_aspect_ratio = min_width / max_height;
+ // Minimum constraint aspect ratio must be less than or equal to the maximum
+ // supported by the source.
+ EXPECT_GE(kAspectRatio, min_aspect_ratio);
+ // All devices in |capabilities_| support the requested aspect-ratio range.
+ // The algorithm should prefer the first device that supports the requested
+ // aspect-ratio range, which in this case is the default device.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ const long kExactWidth = 360;
+ const long kMinHeight = 360;
+ const long kMaxHeight = 720;
+ constraint_factory_.basic().width.setExact(kExactWidth);
+ constraint_factory_.basic().height.setMin(kMinHeight);
+ constraint_factory_.basic().height.setMax(kMaxHeight);
+ constraint_factory_.basic().aspectRatio.setMax(kAspectRatio);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ min_width = std::max(1L, kExactWidth);
+ max_height = std::min(result.settings.GetHeight(), kMaxHeight);
+ min_aspect_ratio = min_width / max_height;
+ // Minimum constraint aspect ratio must be less than or equal to the minimum
+ // supported by the source.
+ EXPECT_GE(kAspectRatio, min_aspect_ratio);
+ // Given resolution constraints, the default device with closest-to-default
+ // settings cannot satisfy the required maximum aspect ratio (maximum would
+ // be 360/500).
+ // The high-res device with a native resolution of 1280x720 can support
+ // 360x720 with cropping with less penalty than the default device at
+ // 1000x1000.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, MandatoryAspectRatioRange) {
+ constraint_factory_.Reset();
+ {
+ const double kMinAspectRatio = 0.5;
+ const double kMaxAspectRatio = 1.0;
+
+ constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+ constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double min_width = 1.0;
+ double max_width = result.settings.GetWidth();
+ double min_height = 1.0;
+ double max_height = result.settings.GetHeight();
+ double min_aspect_ratio = min_width / max_height;
+ double max_aspect_ratio = max_width / min_height;
+ // Constraint aspect-ratio range must have nonempty intersection with
+ // supported range.
+ EXPECT_LE(kMinAspectRatio, max_aspect_ratio);
+ EXPECT_GE(kMaxAspectRatio, min_aspect_ratio);
+ // All devices in |capabilities_| support the requested aspect-ratio range.
+ // The algorithm should prefer the first device that supports the requested
+ // aspect-ratio range, which in this case is the default device.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kMinAspectRatio = 3.0;
+ const double kMaxAspectRatio = 4.0;
+
+ const long kExactHeight = 600;
+ constraint_factory_.Reset();
+ constraint_factory_.basic().height.setMin(kExactHeight);
+ constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+ constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double min_width = 1.0;
+ double max_width = result.settings.GetWidth();
+ double min_height = 1.0;
+ double max_height = result.settings.GetHeight();
+ double min_aspect_ratio = min_width / max_height;
+ double max_aspect_ratio = max_width / min_height;
+ // Constraint aspect-ratio range must have nonempty intersection with
+ // supported range.
+ EXPECT_LE(kMinAspectRatio, max_aspect_ratio);
+ EXPECT_GE(kMaxAspectRatio, min_aspect_ratio);
+ // The only device that supports the resolution and aspect ratio constraint
+ // is the high-res device. The 1920x1080 is the least expensive format.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, IdealAspectRatio) {
+ constraint_factory_.Reset();
+ {
+ const double kIdealAspectRatio = 0.5;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ double min_width = 1.0;
+ double max_width = result.settings.GetWidth();
+ double min_height = 1.0;
+ double max_height = result.settings.GetHeight();
+ double min_aspect_ratio = min_width / max_height;
+ double max_aspect_ratio = max_width / min_height;
+ // All devices in |capabilities_| support the ideal aspect-ratio.
+ // The algorithm should prefer the default device with closest-to-default
+ // settings.
+ EXPECT_LE(kIdealAspectRatio, max_aspect_ratio);
+ EXPECT_GE(kIdealAspectRatio, min_aspect_ratio);
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+ }
+
+ {
+ const double kIdealAspectRatio = 1500.0;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The only device that supports the ideal aspect ratio is the high-res
+ // device. The least expensive way to support it with the 1920x1080 format
+ // cropped to 1500x1.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+ }
+
+ {
+ const double kIdealAspectRatio = 2000.0;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The only device that supports the ideal aspect ratio is the high-res
+ // device with its highest resolution, cropped to 2000x1.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+
+ {
+ const double kIdealAspectRatio = 4000.0;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The configuration closest to the ideal aspect ratio is is the high-res
+ // device with its highest resolution, cropped to 2304x1.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+
+ {
+ const double kIdealAspectRatio = 2.0;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ constraint_factory_.basic().height.setExact(400);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The first device to support the ideal aspect ratio and the resolution
+ // constraint is the low-res device. The 800x600 format cropped to 800x400
+ // is the lest expensive way to achieve it.
+ EXPECT_EQ(low_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(800, result.settings.GetWidth());
+ EXPECT_EQ(600, result.settings.GetHeight());
+ }
+
+ {
+ const double kIdealAspectRatio = 3.0;
+ constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
+ constraint_factory_.basic().height.setExact(400);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The only device that supports the ideal aspect ratio and the resolution
+ // constraint is the high-res device. The 1280x720 cropped to 1200x400 is
+ // the lest expensive way to achieve it.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1280, result.settings.GetWidth());
+ EXPECT_EQ(720, result.settings.GetHeight());
+ }
+}
+
+// The "Advanced" tests check selection criteria involving advanced constraint
+// sets.
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, AdvancedExactResolution) {
+ {
+ constraint_factory_.Reset();
+ blink::WebMediaTrackConstraintSet& advanced1 =
+ constraint_factory_.AddAdvanced();
+ advanced1.width.setExact(4000);
+ advanced1.height.setExact(4000);
+ blink::WebMediaTrackConstraintSet& advanced2 =
+ constraint_factory_.AddAdvanced();
+ advanced2.width.setExact(3000);
+ advanced2.height.setExact(3000);
+ auto result = SelectSettings();
+ // No device supports the advanced constraint sets.
+ // Tie-breaker rule that applies is closeness to default settings.
+ EXPECT_EQ(default_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*default_closest_format_, result.settings.format());
+
+ blink::WebMediaTrackConstraintSet& advanced3 =
+ constraint_factory_.AddAdvanced();
+ advanced3.width.setExact(1920);
+ advanced3.height.setExact(1080);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The high-res device natively supports the third advanced constraint set
+ // and should be selected.
+ // First tie-breaker rule that applies is support for advanced constraints
+ // that appear first. Second tie-breaker rule is custom distance to advanced
+ // constraint sets that appear first.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+
+ blink::WebMediaTrackConstraintSet& advanced4 =
+ constraint_factory_.AddAdvanced();
+ advanced4.width.setExact(640);
+ advanced4.height.setExact(480);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // First tie-breaker rule that applies is support for advanced constraints
+ // that appear first, which leaves out configurations that only support the
+ // fourth advanced constraint set in favor of configurations that support
+ // the third set.
+ // Second tie-breaker rule is custom distance to advanced constraint sets
+ // that appear first.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+
+ constraint_factory_.basic().width.setIdeal(800);
+ constraint_factory_.basic().height.setIdeal(600);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The ideal value is supported by the same configuration, so nothing
+ // changes.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+
+ constraint_factory_.basic().width.setIdeal(2000);
+ constraint_factory_.basic().height.setIdeal(1500);
+ result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The closest configuration to the ideal resolution is the high-res device
+ // at the highest resolution.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(*high_res_highest_format_, result.settings.format());
+ }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest,
+ AdvancedResolutionAndFrameRate) {
+ {
+ constraint_factory_.Reset();
+ blink::WebMediaTrackConstraintSet& advanced1 =
+ constraint_factory_.AddAdvanced();
+ advanced1.width.setExact(1920);
+ advanced1.height.setExact(1080);
+ blink::WebMediaTrackConstraintSet& advanced2 =
+ constraint_factory_.AddAdvanced();
+ advanced2.frameRate.setExact(60.0);
+ blink::WebMediaTrackConstraintSet& advanced3 =
+ constraint_factory_.AddAdvanced();
+ advanced3.width.setExact(2304);
+ advanced3.height.setExact(1536);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.has_value());
+ // The high-res device is the only one that satisfies the first advanced
+ // set. 2304x1536x10.0 satisfies sets 1 and 3, while 1920x1080x60.0
+ // satisfies sets 1, and 2. The latter must be selected, regardless of
+ // any other criteria.
+ EXPECT_EQ(high_res_device_->device_id, result.settings.device_id());
+ EXPECT_EQ(1920, result.settings.GetWidth());
+ EXPECT_EQ(1080, result.settings.GetHeight());
+ EXPECT_EQ(60.0, result.settings.GetFrameRate());
+ }
+}
+
+// The "NoDevices" tests verify that the algorithm returns the expected result
+// when there are no candidates to choose from.
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, NoDevicesNoConstraints) {
+ constraint_factory_.Reset();
+ VideoCaptureCapabilities capabilities;
+ auto result = SelectVideoCaptureSourceSettings(
+ capabilities, constraint_factory_.CreateWebMediaConstraints());
+ EXPECT_FALSE(result.has_value());
+ EXPECT_TRUE(std::string(result.failed_constraint_name).empty());
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoSourceTest, NoDevicesWithConstraints) {
+ constraint_factory_.Reset();
+ constraint_factory_.basic().height.setExact(100);
+ VideoCaptureCapabilities capabilities;
+ auto result = SelectVideoCaptureSourceSettings(
+ capabilities, constraint_factory_.CreateWebMediaConstraints());
+ EXPECT_FALSE(result.has_value());
+ EXPECT_TRUE(std::string(result.failed_constraint_name).empty());
+}
+
+} // namespace content
« no previous file with comments | « content/renderer/media/media_stream_constraints_util_video_source.cc ('k') | content/test/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698