blob: b37603b3be13ec3a0f0f059df1a8ef522671b00a [file] [log] [blame]
// 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_device.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 "media/base/limits.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";
const char kDeviceID4[] = "fake_device_4";
const char kDeviceID5[] = "fake_device_5";
void CheckTrackAdapterSettingsEqualsResolution(
const VideoCaptureSettings& settings) {
EXPECT_EQ(settings.Format().frame_size.width(),
settings.track_adapter_settings().max_width);
EXPECT_EQ(settings.Format().frame_size.height(),
settings.track_adapter_settings().max_height);
EXPECT_EQ(1.0 / settings.Format().frame_size.height(),
settings.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(settings.Format().frame_size.width(),
settings.track_adapter_settings().max_aspect_ratio);
}
void CheckTrackAdapterSettingsEqualsFrameRate(
const VideoCaptureSettings& settings,
double value = 0.0) {
if (value >= settings.FrameRate())
value = 0.0;
EXPECT_EQ(value, settings.track_adapter_settings().max_frame_rate);
}
void CheckTrackAdapterSettingsEqualsFormat(
const VideoCaptureSettings& settings) {
CheckTrackAdapterSettingsEqualsResolution(settings);
CheckTrackAdapterSettingsEqualsFrameRate(settings);
}
double AspectRatio(const media::VideoCaptureFormat& format) {
return static_cast<double>(format.frame_size.width()) /
static_cast<double>(format.frame_size.height());
}
VideoCaptureSettings SelectSettingsVideoDeviceCapture(
const VideoDeviceCaptureCapabilities& capabilities,
const blink::WebMediaConstraints& constraints) {
return SelectSettingsVideoDeviceCapture(
capabilities, constraints, MediaStreamVideoSource::kDefaultWidth,
MediaStreamVideoSource::kDefaultHeight,
MediaStreamVideoSource::kDefaultFrameRate);
}
} // namespace
class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
public:
void SetUp() override {
// Default device. It is default because it is the first in the enumeration.
blink::mojom::VideoInputDeviceCapabilitiesPtr device =
blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID1;
device->facing_mode = blink::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 = blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID2;
device->facing_mode = blink::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 = blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID3;
device->facing_mode = blink::mojom::FacingMode::USER;
device->formats = {
media::VideoCaptureFormat(gfx::Size(600, 400), 10.0f,
media::PIXEL_FORMAT_I420),
media::VideoCaptureFormat(gfx::Size(640, 480), 10.0f,
media::PIXEL_FORMAT_I420),
// This format has default for all settings, except that the resolution
// is inverted.
media::VideoCaptureFormat(
gfx::Size(MediaStreamVideoSource::kDefaultHeight,
MediaStreamVideoSource::kDefaultWidth),
MediaStreamVideoSource::kDefaultFrameRate,
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));
// A depth capture device.
device = blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID4;
device->facing_mode = blink::mojom::FacingMode::ENVIRONMENT;
device->formats = {media::VideoCaptureFormat(gfx::Size(640, 480), 30.0f,
media::PIXEL_FORMAT_Y16)};
capabilities_.device_capabilities.push_back(std::move(device));
// A device that reports invalid frame rates. These devices exist and should
// be supported if no constraints are placed on the frame rate.
device = blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID5;
device->facing_mode = blink::mojom::FacingMode::NONE;
device->formats = {
media::VideoCaptureFormat(
gfx::Size(MediaStreamVideoSource::kDefaultWidth,
MediaStreamVideoSource::kDefaultHeight),
0.0f, media::PIXEL_FORMAT_I420),
media::VideoCaptureFormat(gfx::Size(500, 500), 0.1f,
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,
};
capabilities_.noise_reduction_capabilities = {
base::Optional<bool>(), base::Optional<bool>(true),
base::Optional<bool>(false),
};
default_device_ = capabilities_.device_capabilities[0].get();
low_res_device_ = capabilities_.device_capabilities[1].get();
high_res_device_ = capabilities_.device_capabilities[2].get();
invalid_frame_rate_device_ = capabilities_.device_capabilities[4].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[3];
high_res_highest_format_ = &high_res_device_->formats[6];
}
protected:
VideoCaptureSettings SelectSettings() {
blink::WebMediaConstraints constraints =
constraint_factory_.CreateWebMediaConstraints();
return SelectSettingsVideoDeviceCapture(capabilities_, constraints);
}
VideoDeviceCaptureCapabilities capabilities_;
const blink::mojom::VideoInputDeviceCapabilities* default_device_;
const blink::mojom::VideoInputDeviceCapabilities* low_res_device_;
const blink::mojom::VideoInputDeviceCapabilities* high_res_device_;
const blink::mojom::VideoInputDeviceCapabilities* invalid_frame_rate_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(MediaStreamConstraintsUtilVideoDeviceTest, Unconstrained) {
constraint_factory_.Reset();
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Should select the default device with closest-to-default settings.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// Should select default settings for other constraints.
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
EXPECT_EQ(base::Optional<bool>(), result.noise_reduction());
}
// The "Overconstrained" tests verify that failure of any single required
// constraint results in failure to select a candidate.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnDeviceID) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII("NONEXISTING"));
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().device_id.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnFacingMode) {
constraint_factory_.Reset();
// No device in |capabilities_| has facing mode equal to LEFT.
constraint_factory_.basic().facing_mode.SetExact(
blink::WebString::FromASCII("left"));
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().facing_mode.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnVideoKind) {
constraint_factory_.Reset();
// No device in |capabilities_| has video kind infrared.
constraint_factory_.basic().video_kind.SetExact(
blink::WebString::FromASCII("infrared"));
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().video_kind.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnHeight) {
constraint_factory_.Reset();
constraint_factory_.basic().height.SetExact(123467890);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().height.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().height.SetMin(123467890);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().height.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().height.SetMax(0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().height.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnWidth) {
constraint_factory_.Reset();
constraint_factory_.basic().width.SetExact(123467890);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().width.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().width.SetMin(123467890);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().width.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().width.SetMax(0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().width.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
OverconstrainedOnAspectRatio) {
constraint_factory_.Reset();
constraint_factory_.basic().aspect_ratio.SetExact(123467890.0);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().aspect_ratio.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().aspect_ratio.SetMin(123467890.0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().aspect_ratio.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
// This value is lower than the minimum supported by sources.
const double kLowAspectRatio = 0.01;
constraint_factory_.basic().aspect_ratio.SetMax(kLowAspectRatio);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().aspect_ratio.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, OverconstrainedOnFrameRate) {
constraint_factory_.Reset();
constraint_factory_.basic().frame_rate.SetExact(123467890.0);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().frame_rate.SetMin(123467890.0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().frame_rate.SetMax(0.0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
result.failed_constraint_name());
// Maximum frame rate must be at least 1.0.
constraint_factory_.Reset();
constraint_factory_.basic().frame_rate.SetMax(0.99);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().frame_rate.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
OverconstrainedOnPowerLineFrequency) {
constraint_factory_.Reset();
constraint_factory_.basic().goog_power_line_frequency.SetExact(123467890);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().goog_power_line_frequency.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().goog_power_line_frequency.SetMin(123467890);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().goog_power_line_frequency.GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().goog_power_line_frequency.SetMax(-1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().goog_power_line_frequency.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
OverconstrainedOnNoiseReduction) {
// Simulate a system that does not support noise reduction.
// Manually adding device capabilities because VideoDeviceCaptureCapabilities
// is move only.
VideoDeviceCaptureCapabilities capabilities;
blink::mojom::VideoInputDeviceCapabilitiesPtr device =
blink::mojom::VideoInputDeviceCapabilities::New();
device->device_id = kDeviceID1;
device->facing_mode = blink::mojom::FacingMode::NONE;
device->formats = {
media::VideoCaptureFormat(gfx::Size(200, 200), 40.0f,
media::PIXEL_FORMAT_I420),
};
capabilities.device_capabilities.push_back(std::move(device));
capabilities.power_line_capabilities = capabilities_.power_line_capabilities;
capabilities.noise_reduction_capabilities = {base::Optional<bool>(false)};
constraint_factory_.Reset();
constraint_factory_.basic().goog_noise_reduction.SetExact(true);
auto constraints = constraint_factory_.CreateWebMediaConstraints();
auto result = SelectSettingsVideoDeviceCapture(capabilities, constraints);
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().goog_noise_reduction.GetName(),
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(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryDeviceID) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(default_device_->device_id));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
CheckTrackAdapterSettingsEqualsFormat(result);
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(low_res_device_->device_id));
result = SelectSettings();
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(*low_res_closest_format_, result.Format());
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
CheckTrackAdapterSettingsEqualsFormat(result);
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(high_res_device_->device_id));
result = SelectSettings();
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_closest_format_, result.Format());
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryFacingMode) {
constraint_factory_.Reset();
constraint_factory_.basic().facing_mode.SetExact(
blink::WebString::FromASCII("environment"));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only the low-res device supports environment facing mode. Should select
// default settings for everything else.
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(blink::mojom::FacingMode::ENVIRONMENT,
low_res_device_->facing_mode);
EXPECT_EQ(*low_res_closest_format_, result.Format());
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
CheckTrackAdapterSettingsEqualsFormat(result);
constraint_factory_.basic().facing_mode.SetExact(
blink::WebString::FromASCII("user"));
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only the high-res device supports user facing mode. Should select default
// settings for everything else.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(blink::mojom::FacingMode::USER, high_res_device_->facing_mode);
EXPECT_EQ(*high_res_closest_format_, result.Format());
EXPECT_EQ(media::PowerLineFrequency::FREQUENCY_DEFAULT,
result.PowerLineFrequency());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryVideoKind) {
constraint_factory_.Reset();
constraint_factory_.basic().video_kind.SetExact(
blink::WebString::FromASCII("depth"));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(kDeviceID4, result.device_id());
EXPECT_EQ(media::PIXEL_FORMAT_Y16, result.Format().pixel_format);
CheckTrackAdapterSettingsEqualsFormat(result);
constraint_factory_.basic().video_kind.SetExact(
blink::WebString::FromASCII("color"));
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(default_device_->device_id, result.device_id());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, 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().goog_power_line_frequency.SetExact(
static_cast<long>(power_line_frequency));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(power_line_frequency, result.PowerLineFrequency());
// The default device and settings closest to the default should be
// selected.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryNoiseReduction) {
constraint_factory_.Reset();
const bool kNoiseReductionValues[] = {true, false};
for (auto noise_reduction : kNoiseReductionValues) {
constraint_factory_.basic().goog_noise_reduction.SetExact(noise_reduction);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(noise_reduction, result.noise_reduction());
// The default device and settings closest to the default should be
// selected.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryExactHeight) {
constraint_factory_.Reset();
const int kHeight = MediaStreamVideoSource::kDefaultHeight;
constraint_factory_.basic().height.SetExact(kHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(kHeight, result.Height());
EXPECT_EQ(kHeight, result.track_adapter_settings().max_height);
const int kLargeHeight = 1500;
constraint_factory_.basic().height.SetExact(kLargeHeight);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_EQ(kLargeHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(kLargeHeight * AspectRatio(*high_res_highest_format_)),
result.track_adapter_settings().max_width);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMinHeight) {
constraint_factory_.Reset();
const int kHeight = MediaStreamVideoSource::kDefaultHeight;
constraint_factory_.basic().height.SetMin(kHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// All devices in |capabilities_| support the requested height range. The
// algorithm should prefer the default device.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_LE(kHeight, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()) / kHeight,
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kLargeHeight = 1500;
constraint_factory_.basic().height.SetMin(kLargeHeight);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only the high-res device at the highest resolution supports the requested
// height range.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_LE(kHeight, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()) / kLargeHeight,
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMaxHeight) {
constraint_factory_.Reset();
const int kLowHeight = 20;
constraint_factory_.basic().height.SetMax(kLowHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(low_res_device_->formats[0], result.Format());
EXPECT_EQ(kLowHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(kLowHeight * AspectRatio(result.Format())),
result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()),
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / kLowHeight, result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, 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.HasValue());
EXPECT_GE(result.Height(), kMinHeight);
EXPECT_LE(result.Height(), 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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()) / kMinHeight,
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
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.HasValue());
EXPECT_GE(result.Height(), kMinHeight);
EXPECT_LE(result.Height(), 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.device_id());
EXPECT_EQ(800, result.Width());
EXPECT_EQ(600, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()) / kMinHeight,
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
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.HasValue());
EXPECT_GE(result.Height(), kMinHeight);
EXPECT_LE(result.Height(), 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(static_cast<double>(result.Width()) / kMinHeight,
result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, IdealHeight) {
constraint_factory_.Reset();
{
const int kIdealHeight = 480;
constraint_factory_.basic().height.SetIdeal(kIdealHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should select the first device that supports the ideal
// height natively.
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(kIdealHeight, result.Height());
CheckTrackAdapterSettingsEqualsFormat(result);
}
{
const int kIdealHeight = 481;
constraint_factory_.basic().height.SetIdeal(kIdealHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// The track is cropped to the ideal height, maintaining the source aspect
// ratio.
EXPECT_EQ(kIdealHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(kIdealHeight * AspectRatio(result.Format())),
result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const int kIdealHeight = 1079;
constraint_factory_.basic().height.SetIdeal(kIdealHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(1920, result.Width());
EXPECT_EQ(1080, result.Height());
EXPECT_EQ(kIdealHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(kIdealHeight * AspectRatio(result.Format())),
result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const int kIdealHeight = 1200;
constraint_factory_.basic().height.SetIdeal(kIdealHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_EQ(kIdealHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(kIdealHeight * AspectRatio(result.Format())),
result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryExactWidth) {
constraint_factory_.Reset();
const int kWidth = 640;
constraint_factory_.basic().width.SetExact(kWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(kWidth, result.Width());
EXPECT_EQ(std::round(kWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(kWidth, result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kLargeWidth = 2000;
constraint_factory_.basic().width.SetExact(kLargeWidth);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_LE(kLargeWidth, result.Width());
// 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.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_EQ(std::round(kLargeWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kLargeWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(kLargeWidth, result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kLargeWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMinWidth) {
constraint_factory_.Reset();
const int kWidth = 640;
constraint_factory_.basic().width.SetMin(kWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_LE(kWidth, result.Width());
EXPECT_EQ(1000, result.Width());
EXPECT_EQ(1000, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kLargeWidth = 2000;
constraint_factory_.basic().width.SetMin(kLargeWidth);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only the high-res device at the highest resolution supports the requested
// minimum width.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_LE(kLargeWidth, result.Width());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kLargeWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMaxWidth) {
constraint_factory_.Reset();
const int kLowWidth = 30;
constraint_factory_.basic().width.SetMax(kLowWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(low_res_device_->formats[0], result.Format());
// The track is cropped to kLowWidth and keeps the source aspect ratio.
EXPECT_EQ(std::round(kLowWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kLowWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(kLowWidth, result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, 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.HasValue());
EXPECT_GE(result.Width(), kMinWidth);
EXPECT_LE(result.Width(), 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.device_id());
EXPECT_EQ(1000, result.Width());
EXPECT_EQ(1000, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kMinWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
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.HasValue());
EXPECT_GE(result.Width(), kMinWidth);
EXPECT_LE(result.Width(), 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.device_id());
EXPECT_EQ(800, result.Width());
EXPECT_EQ(600, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kMinWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
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.HasValue());
EXPECT_GE(result.Width(), kMinWidth);
EXPECT_LE(result.Width(), 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.device_id());
EXPECT_EQ(1920, result.Width());
EXPECT_EQ(1080, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(static_cast<double>(kMinWidth) / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, IdealWidth) {
constraint_factory_.Reset();
{
const int kIdealWidth = 320;
constraint_factory_.basic().width.SetIdeal(kIdealWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(kIdealWidth, result.Width());
EXPECT_EQ(std::round(kIdealWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kIdealWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(kIdealWidth, result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const int kIdealWidth = 321;
constraint_factory_.basic().width.SetIdeal(kIdealWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// In this case, the high_res device is selected because it has a mode that
// can satisfy the ideal at a lower cost than other devices (480 vs 500).
// Note that a native resolution of 320 is further from the ideal value of
// 321 than 480 cropped to 321.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(480, result.Width());
// The track is cropped to kIdealWidth and keeps the source aspect ratio.
EXPECT_EQ(std::round(kIdealWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kIdealWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const int kIdealWidth = 2000;
constraint_factory_.basic().width.SetIdeal(kIdealWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm must the select the only device that can satisfy the ideal.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
// The track is cropped to kIdealWidth and keeps the source aspect ratio.
EXPECT_EQ(std::round(kIdealWidth / AspectRatio(result.Format())),
result.track_adapter_settings().max_height);
EXPECT_EQ(kIdealWidth, result.track_adapter_settings().max_width);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const int kIdealWidth = 3000;
constraint_factory_.basic().width.SetIdeal(kIdealWidth);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm must the select the device and setting with less distance
// to the ideal.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryExactFrameRate) {
constraint_factory_.Reset();
const double kFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
constraint_factory_.basic().frame_rate.SetExact(kFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(kFrameRate, result.FrameRate());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kFrameRate);
const double kLargeFrameRate = 50;
constraint_factory_.basic().frame_rate.SetExact(kLargeFrameRate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(60.0, result.FrameRate());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kLargeFrameRate);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMinFrameRate) {
// MinFrameRate equal to default frame rate.
{
constraint_factory_.Reset();
const double kMinFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// All devices in |capabilities_| support the requested frame-rate range.
// The algorithm should prefer the default device.
EXPECT_EQ(default_device_->device_id, result.device_id());
// The format closest to the default satisfies the constraint.
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
EXPECT_TRUE(result.min_frame_rate().has_value());
EXPECT_EQ(result.min_frame_rate(), kMinFrameRate);
EXPECT_FALSE(result.max_frame_rate().has_value());
}
// MinFrameRate greater than default frame rate.
{
const double kMinFrameRate = 50;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_LE(kMinFrameRate, result.FrameRate());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
CheckTrackAdapterSettingsEqualsFormat(result);
EXPECT_TRUE(result.min_frame_rate().has_value());
EXPECT_EQ(result.min_frame_rate(), kMinFrameRate);
EXPECT_FALSE(result.max_frame_rate().has_value());
}
// MinFrameRate lower than the minimum allowed value.
{
const double kMinFrameRate = -0.01;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The minimum frame rate is ignored. Default settings should be used.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_FALSE(result.max_frame_rate().has_value());
}
// MinFrameRate equal to the minimum allowed value.
{
const double kMinFrameRate = 0.0;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_TRUE(result.min_frame_rate().has_value());
EXPECT_EQ(result.min_frame_rate(), kMinFrameRate);
EXPECT_FALSE(result.max_frame_rate().has_value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMaxFrameRate) {
// MaxFrameRate within valid range.
{
constraint_factory_.Reset();
const double kMaxFrameRate = 10;
constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(kMaxFrameRate, result.FrameRate());
EXPECT_EQ(MediaStreamVideoSource::kDefaultHeight, result.Height());
EXPECT_EQ(MediaStreamVideoSource::kDefaultWidth, result.Width());
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_TRUE(result.max_frame_rate().has_value());
EXPECT_EQ(kMaxFrameRate, result.max_frame_rate());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kMaxFrameRate);
}
// MaxFrameRate greater than the maximum allowed.
{
constraint_factory_.Reset();
const double kMaxFrameRate = media::limits::kMaxFramesPerSecond + 0.1;
constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The maximum frame rate should be ignored. Default settings apply.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_FALSE(result.max_frame_rate().has_value());
}
// MaxFrameRate equal to the maximum and minimum allowed MaxFrameRate.
{
const double kMaxFrameRates[] = {1.0, media::limits::kMaxFramesPerSecond};
for (double max_frame_rate : kMaxFrameRates) {
constraint_factory_.Reset();
constraint_factory_.basic().frame_rate.SetMax(max_frame_rate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_TRUE(result.max_frame_rate().has_value());
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_EQ(result.max_frame_rate(), max_frame_rate);
}
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryFrameRateRange) {
constraint_factory_.Reset();
{
const double kMinFrameRate = 10;
const double kMaxFrameRate = 40;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_LE(kMinFrameRate, result.FrameRate());
EXPECT_GE(kMaxFrameRate, result.FrameRate());
// 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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
}
{
const double kMinFrameRate = 25;
const double kMaxFrameRate = 35;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_GE(result.FrameRate(), kMinFrameRate);
EXPECT_LE(result.FrameRate(), 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.device_id());
EXPECT_EQ(*low_res_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
}
{
const double kMinFrameRate = 50;
const double kMaxFrameRate = 70;
constraint_factory_.basic().frame_rate.SetMin(kMinFrameRate);
constraint_factory_.basic().frame_rate.SetMax(kMaxFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_GE(result.FrameRate(), kMinFrameRate);
EXPECT_LE(result.FrameRate(), 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
CheckTrackAdapterSettingsEqualsFormat(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, IdealFrameRate) {
constraint_factory_.Reset();
{
const double kIdealFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
constraint_factory_.basic().frame_rate.SetIdeal(kIdealFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*low_res_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kIdealFrameRate);
}
{
const double kIdealFrameRate = 31;
constraint_factory_.basic().frame_rate.SetIdeal(kIdealFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kIdealFrameRate);
}
{
const double kIdealFrameRate = 55;
constraint_factory_.basic().frame_rate.SetIdeal(kIdealFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
EXPECT_EQ(60, result.FrameRate());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result, kIdealFrameRate);
}
{
const double kIdealFrameRate = 100;
constraint_factory_.basic().frame_rate.SetIdeal(kIdealFrameRate);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
EXPECT_EQ(60, result.FrameRate());
CheckTrackAdapterSettingsEqualsFormat(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryExactAspectRatio) {
constraint_factory_.Reset();
const double kAspectRatio = 4.0 / 3.0;
constraint_factory_.basic().aspect_ratio.SetExact(kAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double min_width = 1.0;
double max_width = result.Width();
double min_height = 1.0;
double max_height = result.Height();
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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_EQ(std::round(result.Width() / kAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kMinWidth = 500;
const int kMaxWidth = 1000;
const int kMaxHeight = 500;
constraint_factory_.basic().height.SetMax(kMaxHeight);
constraint_factory_.basic().width.SetMin(kMinWidth);
constraint_factory_.basic().width.SetMax(kMaxWidth);
constraint_factory_.basic().aspect_ratio.SetExact(kAspectRatio);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
min_width = std::max(1, kMinWidth);
max_width = std::min(result.Width(), kMaxWidth);
min_height = 1.0;
max_height = std::min(result.Height(), 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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_EQ(std::round(result.Width() / kAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int 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().aspect_ratio.SetExact(kAspectRatio);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
min_width = std::max(1, kMinWidth);
max_width = std::min(result.Width(), kMaxWidth);
min_height = std::max(1, kMinHeight);
max_height = std::min(result.Height(), 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.device_id());
EXPECT_EQ(*low_res_closest_format_, result.Format());
EXPECT_EQ(std::round(result.Width() / kAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMinAspectRatio) {
constraint_factory_.Reset();
const double kAspectRatio = 4.0 / 3.0;
constraint_factory_.basic().aspect_ratio.SetMin(kAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double max_width = result.Width();
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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// Adjust the track resolution to use the minimum aspect ratio, which is
// greater than the source's aspect ratio.
EXPECT_EQ(std::round(result.Width() / kAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kMinWidth = 500;
const int kMaxWidth = 1000;
const int kMinHeight = 480;
const int 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().aspect_ratio.SetMin(kAspectRatio);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
max_width = std::min(result.Width(), kMaxWidth);
min_height = std::max(1, 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.device_id());
EXPECT_EQ(*low_res_closest_format_, result.Format());
// The source's native aspect ratio equals the minimum aspect ratio.
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(max_aspect_ratio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMaxAspectRatio) {
constraint_factory_.Reset();
const double kAspectRatio = 0.5;
constraint_factory_.basic().aspect_ratio.SetMax(kAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double min_width = 1.0;
double max_height = result.Height();
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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// The track's aspect ratio is adjusted to the maximum, which is lower than
// the source's native aspect ratio.
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(result.Height() * kAspectRatio),
result.track_adapter_settings().max_width);
EXPECT_EQ(min_aspect_ratio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
const int kExactWidth = 360;
const int kMinHeight = 360;
const int kMaxHeight = 720;
constraint_factory_.basic().width.SetExact(kExactWidth);
constraint_factory_.basic().height.SetMin(kMinHeight);
constraint_factory_.basic().height.SetMax(kMaxHeight);
constraint_factory_.basic().aspect_ratio.SetMax(kAspectRatio);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
min_width = std::max(1, kExactWidth);
max_height = std::min(result.Height(), 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
// The track's aspect ratio is adjusted to the maximum, which is lower than
// the source's native aspect ratio.
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(result.Height() * kAspectRatio),
result.track_adapter_settings().max_width);
EXPECT_EQ(min_aspect_ratio, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kAspectRatio, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryAspectRatioRange) {
constraint_factory_.Reset();
{
const double kMinAspectRatio = 0.5;
const double kMaxAspectRatio = 1.0;
constraint_factory_.basic().aspect_ratio.SetMin(kMinAspectRatio);
constraint_factory_.basic().aspect_ratio.SetMax(kMaxAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double min_width = 1.0;
double max_width = result.Width();
double min_height = 1.0;
double max_height = result.Height();
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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// The source's aspect ratio matches the maximum aspect ratio. No adjustment
// is required.
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kMinAspectRatio,
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(kMaxAspectRatio,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kMinAspectRatio = 3.0;
const double kMaxAspectRatio = 4.0;
const long kMinHeight = 600;
constraint_factory_.Reset();
constraint_factory_.basic().height.SetMin(kMinHeight);
constraint_factory_.basic().aspect_ratio.SetMin(kMinAspectRatio);
constraint_factory_.basic().aspect_ratio.SetMax(kMaxAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double min_width = 1.0;
double max_width = result.Width();
double min_height = 1.0;
double max_height = result.Height();
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.device_id());
EXPECT_EQ(1920, result.Width());
EXPECT_EQ(1080, result.Height());
// The track is cropped to support the minimum aspect ratio.
EXPECT_EQ(std::round(result.Width() / kMinAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(kMinAspectRatio,
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(static_cast<double>(result.Width()) / kMinHeight,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, IdealAspectRatio) {
constraint_factory_.Reset();
{
const double kIdealAspectRatio = 0.5;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
double min_width = 1.0;
double max_width = result.Width();
double min_height = 1.0;
double max_height = result.Height();
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.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
// The track is cropped to support the ideal aspect ratio.
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(std::round(result.Height() * kIdealAspectRatio),
result.track_adapter_settings().max_width);
EXPECT_EQ(min_aspect_ratio,
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(max_aspect_ratio,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kIdealAspectRatio = 1500.0;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The only device that supports the ideal aspect ratio is the high-res
// device.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(1920, result.Width());
EXPECT_EQ(1080, result.Height());
// The most exact way to support the ideal aspect ratio would be to crop to
// 1500x1. However, the algorithm tries to crop to 1920x1.28 and rounds.
// In this case, the effect of rounding is noticeable because of the
// resulting low value for height. For more typical resolution values,
// the at-most 1-pixel error caused by rounding is not an issue.
EXPECT_EQ(std::round(result.Width() / kIdealAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kIdealAspectRatio = 2000.0;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The only device that supports the ideal aspect ratio is the high-res
// device with its highest resolution.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
EXPECT_EQ(std::round(result.Width() / kIdealAspectRatio),
result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kIdealAspectRatio = 4000.0;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
// In this case there is no rounding error.
EXPECT_EQ(1, result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kIdealAspectRatio = 2.0;
const int kExactHeight = 400;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
constraint_factory_.basic().height.SetExact(kExactHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(800, result.Width());
EXPECT_EQ(600, result.Height());
EXPECT_EQ(kExactHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(kExactHeight * kIdealAspectRatio,
result.track_adapter_settings().max_width);
EXPECT_EQ(1.0 / kExactHeight,
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(static_cast<double>(result.Width()) / kExactHeight,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
{
const double kIdealAspectRatio = 3.0;
const int kExactHeight = 400;
constraint_factory_.basic().aspect_ratio.SetIdeal(kIdealAspectRatio);
constraint_factory_.basic().height.SetExact(kExactHeight);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(1280, result.Width());
EXPECT_EQ(720, result.Height());
EXPECT_EQ(kExactHeight, result.track_adapter_settings().max_height);
EXPECT_EQ(kExactHeight * kIdealAspectRatio,
result.track_adapter_settings().max_width);
EXPECT_EQ(1.0 / kExactHeight,
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(static_cast<double>(result.Width()) / kExactHeight,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
// The "Advanced" tests check selection criteria involving advanced constraint
// sets.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedMinMaxResolutionFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(4000);
advanced1.height.SetMin(4000);
// No device supports the first advanced set. This first advanced constraint
// set is therefore ignored in all calls to SelectSettings().
// Tie-breaker rule that applies is closeness to default settings.
auto result = SelectSettings();
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
CheckTrackAdapterSettingsEqualsFormat(result);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(320);
advanced2.height.SetMin(240);
advanced2.width.SetMax(640);
advanced2.height.SetMax(480);
result = SelectSettings();
// The device that best supports this advanced set is the low-res device,
// which natively supports the maximum resolution.
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(320.0 / 480.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(640.0 / 240.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
blink::WebMediaTrackConstraintSet& advanced3 =
constraint_factory_.AddAdvanced();
advanced3.frame_rate.SetMax(10.0);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The high-res device natively supports the third advanced set in addition
// to the previous set and should be selected.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(320.0 / 480.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(640.0 / 240.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result, 10.0);
blink::WebMediaTrackConstraintSet& advanced4 =
constraint_factory_.AddAdvanced();
advanced4.width.SetMax(1000);
advanced4.height.SetMax(1000);
result = SelectSettings();
// Even though the default device supports the resolution in the fourth
// advanced natively, having better support for the previous sets has
// precedence, so the high-res device is selected.
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(320.0 / 480.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(640.0 / 240.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result, 10.0);
constraint_factory_.basic().width.SetIdeal(100);
constraint_factory_.basic().height.SetIdeal(100);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The high-res device at 600x400@10Hz supports all advanced sets and is
// better at supporting the ideal value.
// It beats 320x240@30Hz because the penalty for the native frame rate takes
// precedence over the native fitness distance.
// Both support standard fitness distance equally, since 600x400 can be
// cropped to 320x240.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(600, result.Width());
EXPECT_EQ(400, result.Height());
EXPECT_EQ(320, result.track_adapter_settings().max_width);
EXPECT_EQ(240, result.track_adapter_settings().max_height);
EXPECT_EQ(320.0 / 400.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(600.0 / 240.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result, 10.0);
constraint_factory_.basic().width.SetIdeal(2000);
constraint_factory_.basic().height.SetIdeal(1500);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The high-res device at 640x480@10Hz is closer to the large ideal
// resolution.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
EXPECT_EQ(640, result.track_adapter_settings().max_width);
EXPECT_EQ(480, result.track_adapter_settings().max_height);
EXPECT_EQ(320.0 / 480.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(640.0 / 240.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result, 10.0);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
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.frame_rate.SetExact(60.0);
blink::WebMediaTrackConstraintSet& advanced3 =
constraint_factory_.AddAdvanced();
advanced3.width.SetExact(2304);
advanced3.height.SetExact(1536);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// 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.device_id());
EXPECT_EQ(1920, result.Width());
EXPECT_EQ(1080, result.Height());
EXPECT_EQ(60.0, result.FrameRate());
EXPECT_EQ(1920, result.track_adapter_settings().max_width);
EXPECT_EQ(1080, result.track_adapter_settings().max_height);
EXPECT_EQ(1920.0 / 1080.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(1920.0 / 1080.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result, 60.0);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, AdvancedNoiseReduction) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(640);
advanced1.height.SetMin(480);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(1920);
advanced2.height.SetMin(1080);
advanced2.goog_noise_reduction.SetExact(false);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_LE(1920, result.Width());
EXPECT_LE(1080, result.Height());
EXPECT_TRUE(result.noise_reduction() && !*result.noise_reduction());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(1920.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width() / 1080.0,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryNoiseReduction) {
{
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(640);
advanced1.height.SetMin(480);
advanced1.goog_noise_reduction.SetExact(true);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(1920);
advanced2.height.SetMin(1080);
advanced2.goog_noise_reduction.SetExact(false);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set cannot be satisfied because it contradicts the
// first set. The default device supports the first set and should be
// selected.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_LE(640, result.Width());
EXPECT_LE(480, result.Height());
EXPECT_TRUE(result.noise_reduction() && *result.noise_reduction());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(640.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width() / 480.0,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
// Same test without noise reduction
{
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(640);
advanced1.height.SetMin(480);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(1920);
advanced2.height.SetMin(1080);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only the high-res device can satisfy the second advanced set.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_LE(1920, result.Width());
EXPECT_LE(1080, result.Height());
// Should select default noise reduction setting.
EXPECT_TRUE(!result.noise_reduction());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(1920.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width() / 1080.0,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryExactResolution) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetExact(640);
advanced1.height.SetExact(480);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetExact(1920);
advanced2.height.SetExact(1080);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set. The low-res device is the one that best supports the requested
// resolution.
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(640, result.Width());
EXPECT_EQ(480, result.Height());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(640.0 / 480.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(640.0 / 480.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryMaxMinResolutionFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMax(640);
advanced1.height.SetMax(480);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(1920);
advanced2.height.SetMin(1080);
advanced2.frame_rate.SetExact(60.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set. The default device with the 200x200@40Hz format should be selected.
// That format satisfies the first advanced set as well as any other, so the
// tie breaker rule that applies is default device ID.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(200, result.Width());
EXPECT_EQ(200, result.Height());
EXPECT_EQ(40, result.FrameRate());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(1.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryMinMaxResolutionFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(800);
advanced1.height.SetMin(600);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMax(640);
advanced2.height.SetMax(480);
advanced2.frame_rate.SetExact(60.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set. The default device with the 1000x1000@20Hz format should be selected.
// That format satisfies the first advanced set as well as any other, so the
// tie breaker rule that applies is default device ID.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(1000, result.Width());
EXPECT_EQ(1000, result.Height());
EXPECT_EQ(20, result.FrameRate());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(800.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width() / 600.0,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryExactAspectRatio) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.aspect_ratio.SetExact(2300.0);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.aspect_ratio.SetExact(3.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set. Only the high-res device in the highest-resolution format supports the
// requested aspect ratio.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
// The track is cropped to support the exact aspect ratio.
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(std::round(result.Height() / 2300.0),
result.track_adapter_settings().max_height);
EXPECT_EQ(2300.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(2300.0, result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryAspectRatioRange) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.aspect_ratio.SetMin(2300.0);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.aspect_ratio.SetMax(3.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set. Only the high-res device in the highest-resolution format supports the
// requested aspect ratio.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(*high_res_highest_format_, result.Format());
// The track is cropped to support the min aspect ratio.
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(std::round(result.Height() / 2300.0),
result.track_adapter_settings().max_height);
EXPECT_EQ(2300.0, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryExactFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.frame_rate.SetExact(40.0);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.frame_rate.SetExact(45.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set.
EXPECT_EQ(40.0, result.FrameRate());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryFrameRateRange) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.frame_rate.SetMin(40.0);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.frame_rate.SetMax(35.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set.
EXPECT_LE(40.0, result.FrameRate());
CheckTrackAdapterSettingsEqualsResolution(result);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryWidthFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMax(1920);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(2000);
advanced2.frame_rate.SetExact(10.0);
blink::WebMediaTrackConstraintSet& advanced3 =
constraint_factory_.AddAdvanced();
advanced3.frame_rate.SetExact(30.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The low-res device at 320x240@30Hz satisfies advanced sets 1 and 3.
// The high-res device at 2304x1536@10.0f can satisfy sets 1 and 2, but not
// both at the same time. Thus, low-res device must be preferred.
EXPECT_EQ(low_res_device_->device_id, result.device_id());
EXPECT_EQ(30.0, result.FrameRate());
EXPECT_GE(1920, result.Width());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryHeightFrameRate) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.height.SetMax(1080);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.height.SetMin(1500);
advanced2.frame_rate.SetExact(10.0);
blink::WebMediaTrackConstraintSet& advanced3 =
constraint_factory_.AddAdvanced();
advanced3.frame_rate.SetExact(60.0);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The high-res device at 1280x768@60Hz and 1920x1080@60Hz satisfies advanced
// sets 1 and 3. The same device at 2304x1536@10.0f can satisfy sets 1 and 2,
// but not both at the same time. Thus, the format closest to default that
// satisfies sets 1 and 3 must be chosen.
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(60.0, result.FrameRate());
EXPECT_GE(1080, result.Height());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, AdvancedDeviceID) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
blink::WebString id_vector1[] = {blink::WebString::FromASCII(kDeviceID1),
blink::WebString::FromASCII(kDeviceID2)};
advanced1.device_id.SetExact(
blink::WebVector<blink::WebString>(id_vector1, arraysize(id_vector1)));
blink::WebString id_vector2[] = {blink::WebString::FromASCII(kDeviceID2),
blink::WebString::FromASCII(kDeviceID3)};
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.device_id.SetExact(
blink::WebVector<blink::WebString>(id_vector2, arraysize(id_vector2)));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// kDeviceID2 must be selected because it is the only one that satisfies both
// advanced sets.
EXPECT_EQ(std::string(kDeviceID2), result.device_id());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryDeviceID) {
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
blink::WebString id_vector1[] = {blink::WebString::FromASCII(kDeviceID1),
blink::WebString::FromASCII(kDeviceID2)};
advanced1.device_id.SetExact(
blink::WebVector<blink::WebString>(id_vector1, arraysize(id_vector1)));
blink::WebString id_vector2[] = {blink::WebString::FromASCII(kDeviceID3),
blink::WebString::FromASCII(kDeviceID4)};
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.device_id.SetExact(
blink::WebVector<blink::WebString>(id_vector2, arraysize(id_vector2)));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set must be ignored because it contradicts the first
// set.
EXPECT_EQ(std::string(kDeviceID1), result.device_id());
CheckTrackAdapterSettingsEqualsFormat(result);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryPowerLineFrequency) {
{
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.width.SetMin(640);
advanced1.height.SetMin(480);
advanced1.goog_power_line_frequency.SetExact(50);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMin(1920);
advanced2.height.SetMin(1080);
advanced2.goog_power_line_frequency.SetExact(60);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set cannot be satisfied because it contradicts the
// first set. The default device supports the first set and should be
// selected.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_LE(640, result.Width());
EXPECT_LE(480, result.Height());
EXPECT_EQ(50, static_cast<int>(result.PowerLineFrequency()));
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(result.Height(), result.track_adapter_settings().max_height);
EXPECT_EQ(640.0 / result.Height(),
result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width() / 480.0,
result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryAspectRatioWidth) {
{
constraint_factory_.Reset();
blink::WebMediaTrackConstraintSet& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.aspect_ratio.SetMin(17);
blink::WebMediaTrackConstraintSet& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.width.SetMax(1);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The second advanced set cannot be satisfied because it contradicts the
// second set. The default device supports the first set and should be
// selected.
EXPECT_EQ(default_device_->device_id, result.device_id());
EXPECT_EQ(*default_closest_format_, result.Format());
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_width);
EXPECT_EQ(std::round(result.Width() / 17.0),
result.track_adapter_settings().max_height);
EXPECT_EQ(17, result.track_adapter_settings().min_aspect_ratio);
EXPECT_EQ(result.Width(), result.track_adapter_settings().max_aspect_ratio);
CheckTrackAdapterSettingsEqualsFrameRate(result);
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, BasicContradictoryWidth) {
constraint_factory_.Reset();
constraint_factory_.basic().width.SetMin(10);
constraint_factory_.basic().width.SetMax(9);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().width.GetName(),
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
BasicContradictoryWidthAspectRatio) {
constraint_factory_.Reset();
constraint_factory_.basic().width.SetMax(1);
constraint_factory_.basic().aspect_ratio.SetExact(100.0);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(constraint_factory_.basic().aspect_ratio.GetName(),
result.failed_constraint_name());
}
// The "NoDevices" tests verify that the algorithm returns the expected result
// when there are no candidates to choose from.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, NoDevicesNoConstraints) {
constraint_factory_.Reset();
VideoDeviceCaptureCapabilities capabilities;
auto result = SelectSettingsVideoDeviceCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, NoDevicesWithConstraints) {
constraint_factory_.Reset();
constraint_factory_.basic().height.SetExact(100);
VideoDeviceCaptureCapabilities capabilities;
auto result = SelectSettingsVideoDeviceCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
}
// This test verifies that having a device that reports a frame rate lower than
// 1 fps works.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, InvalidFrameRateDevice) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(invalid_frame_rate_device_->device_id));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(invalid_frame_rate_device_->device_id, result.device_id());
EXPECT_EQ(invalid_frame_rate_device_->formats[0].frame_rate,
result.FrameRate());
EXPECT_EQ(result.FrameRate(), 0.0);
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_FALSE(result.max_frame_rate().has_value());
// Select the second format with invalid frame rate.
constraint_factory_.basic().width.SetExact(500);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(invalid_frame_rate_device_->device_id, result.device_id());
EXPECT_EQ(invalid_frame_rate_device_->formats[1].frame_rate,
result.FrameRate());
EXPECT_LT(result.FrameRate(), 1.0);
EXPECT_FALSE(result.min_frame_rate().has_value());
EXPECT_FALSE(result.max_frame_rate().has_value());
}
// This test verifies that an inverted default resolution is not preferred over
// the actual default resolution.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, InvertedDefaultResolution) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(high_res_device_->device_id));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(high_res_device_->device_id, result.device_id());
EXPECT_EQ(result.Width(), MediaStreamVideoSource::kDefaultWidth);
EXPECT_EQ(result.Height(), MediaStreamVideoSource::kDefaultHeight);
}
} // namespace content