Commit a893356e authored by Francois Beaufort's avatar Francois Beaufort Committed by Commit Bot

[PTZ] Handle PTZ when processing constraints

This CL makes sure an OverConstrainedError is returned in getUserMedia
if constraints require PTZ and system does not support PTZ.

Bug: 934063
Change-Id: Ia17167bce8eac0d45f4c97d30edbebb032fa55d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2247820Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
Cr-Commit-Position: refs/heads/master@{#779974}
parent 0d911112
......@@ -114,14 +114,20 @@ VideoCaptureSettings::VideoCaptureSettings(
base::Optional<bool> noise_reduction,
const VideoTrackAdapterSettings& track_adapter_settings,
base::Optional<double> min_frame_rate,
base::Optional<double> max_frame_rate)
base::Optional<double> max_frame_rate,
base::Optional<double> pan,
base::Optional<double> tilt,
base::Optional<double> zoom)
: failed_constraint_name_(nullptr),
device_id_(std::move(device_id)),
capture_params_(capture_params),
noise_reduction_(noise_reduction),
track_adapter_settings_(track_adapter_settings),
min_frame_rate_(min_frame_rate),
max_frame_rate_(max_frame_rate) {
max_frame_rate_(max_frame_rate),
pan_(pan),
tilt_(tilt),
zoom_(zoom) {
DCHECK(!min_frame_rate ||
*min_frame_rate_ <= capture_params.requested_format.frame_rate);
DCHECK(!track_adapter_settings.target_size() ||
......
......@@ -69,7 +69,10 @@ class MODULES_EXPORT VideoCaptureSettings {
base::Optional<bool> noise_reduction_,
const VideoTrackAdapterSettings& track_adapter_settings,
base::Optional<double> min_frame_rate,
base::Optional<double> max_frame_rate);
base::Optional<double> max_frame_rate,
base::Optional<double> pan = base::nullopt,
base::Optional<double> tilt = base::nullopt,
base::Optional<double> zoom = base::nullopt);
VideoCaptureSettings(const VideoCaptureSettings& other);
VideoCaptureSettings& operator=(const VideoCaptureSettings& other);
......@@ -127,6 +130,18 @@ class MODULES_EXPORT VideoCaptureSettings {
DCHECK(HasValue());
return max_frame_rate_;
}
const base::Optional<double>& pan() const {
DCHECK(HasValue());
return pan_;
}
const base::Optional<double>& tilt() const {
DCHECK(HasValue());
return tilt_;
}
const base::Optional<double>& zoom() const {
DCHECK(HasValue());
return zoom_;
}
private:
const char* failed_constraint_name_;
......@@ -136,6 +151,9 @@ class MODULES_EXPORT VideoCaptureSettings {
VideoTrackAdapterSettings track_adapter_settings_;
base::Optional<double> min_frame_rate_;
base::Optional<double> max_frame_rate_;
base::Optional<double> pan_;
base::Optional<double> tilt_;
base::Optional<double> zoom_;
};
// This class represents the output the SelectSettings algorithm for audio
......
......@@ -52,9 +52,9 @@ WebString ToWebString(media::VideoFacingMode facing_mode) {
}
}
// Returns the fitness distance between the ideal value of |constraint| and the
// closest value to it in the range [min, max].
// Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
// Returns the fitness distance between the ideal value of |constraint| and
// |value|. Based on
// https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
template <typename NumericConstraint>
double NumericValueFitness(const NumericConstraint& constraint,
decltype(constraint.Min()) value) {
......@@ -63,6 +63,29 @@ double NumericValueFitness(const NumericConstraint& constraint,
: 0.0;
}
// Returns the fitness distance between the ideal value of |constraint| and the
// closest value to it in the range [min, max].
// If the ideal value is contained in the range, returns 1.
// If there is no ideal value, returns 0;
// Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance.
template <typename NumericConstraint>
double NumericRangeFitness(
const NumericConstraint& constraint,
const media_constraints::NumericRangeSet<decltype(constraint.Min())>&
range) {
DCHECK(!range.IsEmpty());
if (!constraint.HasIdeal())
return 0.0;
auto ideal = constraint.Ideal();
if (range.Max().has_value() && ideal > *range.Max())
return NumericConstraintFitnessDistance(ideal, *range.Max());
else if (range.Min().has_value() && ideal < *range.Min())
return NumericConstraintFitnessDistance(ideal, *range.Min());
return 1.0;
}
// Returns a custom distance between |native_value| and the ideal value and
// allowed range for a constrainable property. The ideal value is obtained from
// |constraint| and the allowed range is specified by |min| and |max|.
......@@ -402,6 +425,112 @@ bool FacingModeSatisfiesConstraint(media::VideoFacingMode value,
return constraint.Matches(string_value);
}
class PTZDeviceState {
public:
explicit PTZDeviceState(const MediaTrackConstraintSetPlatform& constraint_set)
: pan_set_(DoubleRangeSet::FromConstraint(constraint_set.pan)),
tilt_set_(DoubleRangeSet::FromConstraint(constraint_set.tilt)),
zoom_set_(DoubleRangeSet::FromConstraint(constraint_set.zoom)) {}
PTZDeviceState(const DoubleRangeSet& pan_set,
const DoubleRangeSet& tilt_set,
const DoubleRangeSet& zoom_set)
: pan_set_(pan_set), tilt_set_(tilt_set), zoom_set_(zoom_set) {}
PTZDeviceState(const PTZDeviceState& other) = default;
PTZDeviceState& operator=(const PTZDeviceState& other) = default;
PTZDeviceState Intersection(
const MediaTrackConstraintSetPlatform& constraint_set) const {
DoubleRangeSet pan_intersection = pan_set_.Intersection(
DoubleRangeSet::FromConstraint(constraint_set.pan));
DoubleRangeSet tilt_intersection = tilt_set_.Intersection(
DoubleRangeSet::FromConstraint(constraint_set.tilt));
DoubleRangeSet zoom_intersection = zoom_set_.Intersection(
DoubleRangeSet::FromConstraint(constraint_set.zoom));
return PTZDeviceState(pan_intersection, tilt_intersection,
zoom_intersection);
}
bool IsEmpty() const {
return pan_set_.IsEmpty() || tilt_set_.IsEmpty() || zoom_set_.IsEmpty();
}
double Fitness(const MediaTrackConstraintSetPlatform& basic_set) const {
return NumericRangeFitness(basic_set.pan, pan_set_) +
NumericRangeFitness(basic_set.tilt, tilt_set_) +
NumericRangeFitness(basic_set.zoom, zoom_set_);
}
const char* FailedConstraintName() const {
MediaTrackConstraintSetPlatform dummy;
if (!pan_set_.IsEmpty())
return dummy.pan.GetName();
if (!tilt_set_.IsEmpty())
return dummy.tilt.GetName();
if (!zoom_set_.IsEmpty())
return dummy.zoom.GetName();
// No failed constraint.
return nullptr;
}
base::Optional<double> SelectPan(
const MediaTrackConstraintSetPlatform& basic_set) const {
return SelectProperty(&PTZDeviceState::pan_set_, basic_set,
&MediaTrackConstraintSetPlatform::pan);
}
base::Optional<double> SelectTilt(
const MediaTrackConstraintSetPlatform& basic_set) const {
return SelectProperty(&PTZDeviceState::tilt_set_, basic_set,
&MediaTrackConstraintSetPlatform::tilt);
}
base::Optional<double> SelectZoom(
const MediaTrackConstraintSetPlatform& basic_set) const {
return SelectProperty(&PTZDeviceState::zoom_set_, basic_set,
&MediaTrackConstraintSetPlatform::zoom);
}
private:
// Select the target value of a property based on the ideal value in
// |basic_set| as follows:
// If an ideal value is provided, return the value in the range closest to
// ideal.
// If no ideal value is provided:
// * If minimum is provided, return minimum.
// * Otherwise, if maximum is provided, return maximum.
// * Otherwise, return nullopt.
base::Optional<double> SelectProperty(
DoubleRangeSet PTZDeviceState::*ptz_field,
const MediaTrackConstraintSetPlatform& basic_set,
DoubleConstraint MediaTrackConstraintSetPlatform::*basic_set_field)
const {
if (!(basic_set.*basic_set_field).HasIdeal()) {
return (this->*ptz_field).Min().has_value() ? (this->*ptz_field).Min()
: (this->*ptz_field).Max();
}
auto ideal = (basic_set.*basic_set_field).Ideal();
if ((this->*ptz_field).Min().has_value() &&
ideal < (this->*ptz_field).Min().value()) {
return (this->*ptz_field).Min();
}
if ((this->*ptz_field).Max().has_value() &&
ideal > (this->*ptz_field).Max().value()) {
return (this->*ptz_field).Max();
}
return ideal;
}
DoubleRangeSet pan_set_;
DoubleRangeSet tilt_set_;
DoubleRangeSet zoom_set_;
};
// Returns true if |constraint_set| can be satisfied by |device|. Otherwise,
// returns false and, if |failed_constraint_name| is not null, updates
// |failed_constraint_name| with the name of a constraint that could not be
......@@ -428,6 +557,21 @@ bool DeviceSatisfiesConstraintSet(
return false;
}
if (constraint_set.pan.IsPresent() && !device.pan_tilt_zoom_supported) {
UpdateFailedConstraintName(constraint_set.pan, failed_constraint_name);
return false;
}
if (constraint_set.tilt.IsPresent() && !device.pan_tilt_zoom_supported) {
UpdateFailedConstraintName(constraint_set.tilt, failed_constraint_name);
return false;
}
if (constraint_set.zoom.IsPresent() && !device.pan_tilt_zoom_supported) {
UpdateFailedConstraintName(constraint_set.zoom, failed_constraint_name);
return false;
}
return true;
}
......@@ -464,11 +608,13 @@ double DeviceFitness(const DeviceInfo& device,
// The track settings for |candidate| that correspond to the returned fitness
// are returned in |track_settings|.
double CandidateFitness(const DeviceInfo& device,
const PTZDeviceState& ptz_state,
const CandidateFormat& candidate_format,
const base::Optional<bool>& noise_reduction,
const MediaTrackConstraintSetPlatform& constraint_set,
VideoTrackAdapterSettings* track_settings) {
return DeviceFitness(device, constraint_set) +
ptz_state.Fitness(constraint_set) +
candidate_format.Fitness(constraint_set, track_settings) +
OptionalBoolFitness(noise_reduction,
constraint_set.goog_noise_reduction);
......@@ -602,7 +748,14 @@ VideoCaptureSettings SelectSettingsVideoDeviceCapture(
continue;
}
PTZDeviceState ptz_device_state(constraints.Basic());
if (ptz_device_state.IsEmpty()) {
failed_constraint_name = ptz_device_state.FailedConstraintName();
continue;
}
for (auto& format : device.formats) {
PTZDeviceState ptz_state_for_format = ptz_device_state;
CandidateFormat candidate_format(format);
if (!candidate_format.ApplyConstraintSet(constraints.Basic(),
&failed_constraint_name)) {
......@@ -624,8 +777,11 @@ VideoCaptureSettings SelectSettingsVideoDeviceCapture(
// First criteria for valid candidates is satisfaction of advanced
// constraint sets.
for (const auto& advanced_set : constraints.Advanced()) {
PTZDeviceState ptz_advanced_state =
ptz_state_for_format.Intersection(advanced_set);
bool satisfies_advanced_set =
DeviceSatisfiesConstraintSet(device, advanced_set) &&
!ptz_advanced_state.IsEmpty() &&
OptionalBoolSatisfiesConstraint(
noise_reduction, advanced_set.goog_noise_reduction) &&
// This must be the last in the condition since it is the only
......@@ -633,15 +789,18 @@ VideoCaptureSettings SelectSettingsVideoDeviceCapture(
// previous two are true.
candidate_format.ApplyConstraintSet(advanced_set);
if (satisfies_advanced_set)
ptz_state_for_format = ptz_advanced_state;
candidate_distance_vector.push_back(
satisfies_advanced_set ? 0 : HUGE_VAL);
}
VideoTrackAdapterSettings track_settings;
// Second criterion is fitness distance.
candidate_distance_vector.push_back(
CandidateFitness(device, candidate_format, noise_reduction,
constraints.Basic(), &track_settings));
candidate_distance_vector.push_back(CandidateFitness(
device, ptz_state_for_format, candidate_format, noise_reduction,
constraints.Basic(), &track_settings));
// Third criterion is native fitness distance.
candidate_distance_vector.push_back(
......@@ -662,7 +821,10 @@ VideoCaptureSettings SelectSettingsVideoDeviceCapture(
result = VideoCaptureSettings(
device.device_id.Utf8(), capture_params, noise_reduction,
track_settings, candidate_format.constrained_frame_rate().Min(),
candidate_format.constrained_frame_rate().Max());
candidate_format.constrained_frame_rate().Max(),
ptz_state_for_format.SelectPan(constraints.Basic()),
ptz_state_for_format.SelectTilt(constraints.Basic()),
ptz_state_for_format.SelectZoom(constraints.Basic()));
}
}
}
......
......@@ -23,12 +23,21 @@ const char kDeviceID2[] = "fake_device_2";
const char kDeviceID3[] = "fake_device_3";
const char kDeviceID4[] = "fake_device_4";
const char kDeviceID5[] = "fake_device_5";
const char kDeviceID6[] = "fake_device_6";
const char kGroupID1[] = "fake_group_1";
const char kGroupID2[] = "fake_group_2";
const char kGroupID3[] = "fake_group_3";
const char kGroupID4[] = "fake_group_4";
const char kGroupID5[] = "fake_group_5";
const char kGroupID6[] = "fake_group_6";
const std::vector<DoubleConstraint MediaTrackConstraintSetPlatform::*>
kPanTiltZoomConstraints = {
&MediaTrackConstraintSetPlatform::pan,
&MediaTrackConstraintSetPlatform::tilt,
&MediaTrackConstraintSetPlatform::zoom,
};
void CheckTrackAdapterSettingsEqualsResolution(
const VideoCaptureSettings& settings) {
......@@ -86,6 +95,7 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
media::VideoCaptureFormat(gfx::Size(1000, 1000), 20.0f,
media::PIXEL_FORMAT_I420),
};
device.pan_tilt_zoom_supported = true;
capabilities_.device_capabilities.push_back(std::move(device));
// A low-resolution device.
......@@ -106,6 +116,7 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
media::VideoCaptureFormat(gfx::Size(800, 600), 20.0f,
media::PIXEL_FORMAT_I420),
};
device.pan_tilt_zoom_supported = true;
capabilities_.device_capabilities.push_back(std::move(device));
// A high-resolution device.
......@@ -137,6 +148,7 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
media::VideoCaptureFormat(gfx::Size(2304, 1536), 10.0f,
media::PIXEL_FORMAT_I420),
};
device.pan_tilt_zoom_supported = true;
capabilities_.device_capabilities.push_back(std::move(device));
// A depth capture device.
......@@ -160,6 +172,16 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
media::VideoCaptureFormat(gfx::Size(500, 500), 0.1f,
media::PIXEL_FORMAT_I420),
};
device.pan_tilt_zoom_supported = true;
capabilities_.device_capabilities.push_back(std::move(device));
// A camera device without PTZ.
device.device_id = kDeviceID6;
device.group_id = kGroupID6;
device.facing_mode = media::MEDIA_VIDEO_FACING_NONE;
device.formats = {media::VideoCaptureFormat(gfx::Size(640, 480), 30.0f,
media::PIXEL_FORMAT_I420)};
device.pan_tilt_zoom_supported = false;
capabilities_.device_capabilities.push_back(std::move(device));
capabilities_.noise_reduction_capabilities = {
......@@ -172,6 +194,7 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
low_res_device_ = &capabilities_.device_capabilities[1];
high_res_device_ = &capabilities_.device_capabilities[2];
invalid_frame_rate_device_ = &capabilities_.device_capabilities[4];
no_ptz_device_ = &capabilities_.device_capabilities[5];
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];
......@@ -189,6 +212,7 @@ class MediaStreamConstraintsUtilVideoDeviceTest : public testing::Test {
const VideoInputDeviceCapabilities* low_res_device_;
const VideoInputDeviceCapabilities* high_res_device_;
const VideoInputDeviceCapabilities* invalid_frame_rate_device_;
const VideoInputDeviceCapabilities* no_ptz_device_;
// Closest formats to the default settings.
const media::VideoCaptureFormat* default_closest_format_;
const media::VideoCaptureFormat* low_res_closest_format_;
......@@ -401,6 +425,43 @@ TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
OverconstrainedOnPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
(constraint_factory_.basic().*constraint).SetIdeal(1);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
(constraint_factory_.basic().*constraint).SetMin(1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
(constraint_factory_.basic().*constraint).SetMax(1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ((constraint_factory_.basic().*constraint).GetName(),
result.failed_constraint_name());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
(constraint_factory_.basic().*constraint).SetExact(1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ((constraint_factory_.basic().*constraint).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) {
......@@ -1827,6 +1888,97 @@ TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, TwoIdealResizeValues) {
EXPECT_FALSE(result.track_adapter_settings().target_size().has_value());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryExactPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetExact(3);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should prefer the first device that supports PTZ natively,
// which is the default device.
EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(3, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(3, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(3, result.zoom().value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMinPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetMin(2);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should prefer the first device that supports PTZ
// natively, which is the default device.
EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(2, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(2, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(2, result.zoom().value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryMaxPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetMax(4);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should prefer the first device that supports PTZ
// natively, which is the default device.
EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(4, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(4, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(4, result.zoom().value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, MandatoryPanTiltZoomRange) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetMin(2);
(constraint_factory_.basic().*constraint).SetMax(4);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should prefer the first device that supports PTZ
// natively, which is the default device.
EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(2, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(2, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(2, result.zoom().value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, IdealPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetIdeal(3);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// The algorithm should select the first device that supports the ideal PTZ
// constraint natively, which is the default device.
EXPECT_EQ(default_device_->device_id.Utf8(), result.device_id());
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(3, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(3, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(3, result.zoom().value());
}
}
// The "Advanced" tests check selection criteria involving advanced constraint
// sets.
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
......@@ -2393,6 +2545,44 @@ TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
AdvancedContradictoryPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
MediaTrackConstraintSetPlatform& advanced1 =
constraint_factory_.AddAdvanced();
advanced1.device_id.SetExact({low_res_device_->device_id});
MediaTrackConstraintSetPlatform& advanced2 =
constraint_factory_.AddAdvanced();
advanced2.device_id.SetExact({no_ptz_device_->device_id});
(advanced2.*constraint).SetExact(4);
MediaTrackConstraintSetPlatform& advanced3 =
constraint_factory_.AddAdvanced();
(advanced3.*constraint).SetMin(4);
(advanced3.*constraint).SetMax(2);
MediaTrackConstraintSetPlatform& advanced4 =
constraint_factory_.AddAdvanced();
(advanced4.*constraint).SetExact(3);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(low_res_device_->device_id.Utf8(), result.device_id());
// The second advanced set must be ignored because it contradicts the first
// set. The third advanced must be ignored because it is invalid. The fourth
// advanced set must be applied.
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_EQ(3, result.pan().value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_EQ(3, result.tilt().value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_EQ(3, result.zoom().value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, AdvancedResize) {
constraint_factory_.Reset();
constraint_factory_.basic().width.SetIdeal(1);
......@@ -2439,6 +2629,26 @@ TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
EXPECT_EQ(result.FrameRate(), 30.0);
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, AdvancedPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(no_ptz_device_->device_id);
MediaTrackConstraintSetPlatform& advanced =
constraint_factory_.AddAdvanced();
(advanced.*constraint).SetExact(3);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(no_ptz_device_->device_id.Utf8(), result.device_id());
// The advanced set must be ignored because the device does not support PTZ.
if (constraint == &MediaTrackConstraintSetPlatform::pan)
EXPECT_FALSE(result.pan().has_value());
else if (constraint == &MediaTrackConstraintSetPlatform::tilt)
EXPECT_FALSE(result.tilt().has_value());
else if (constraint == &MediaTrackConstraintSetPlatform::zoom)
EXPECT_FALSE(result.zoom().has_value());
}
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest, BasicContradictoryWidth) {
constraint_factory_.Reset();
constraint_factory_.basic().width.SetMin(10);
......@@ -2460,6 +2670,19 @@ TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
result.failed_constraint_name());
}
TEST_F(MediaStreamConstraintsUtilVideoDeviceTest,
BasicContradictoryPanTiltZoom) {
for (auto& constraint : kPanTiltZoomConstraints) {
constraint_factory_.Reset();
(constraint_factory_.basic().*constraint).SetMin(4);
(constraint_factory_.basic().*constraint).SetMax(2);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ((constraint_factory_.basic().*constraint).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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment