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

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

Issue 2735793002: Add algorithm for processing constraints for video content capture. (Closed)
Patch Set: Add contradiction tests Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/media_stream_constraints_util_video_content.cc
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.cc b/content/renderer/media/media_stream_constraints_util_video_content.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4449346aa389c5fc0818b65ec8e66d10401bf6aa
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_video_content.cc
@@ -0,0 +1,324 @@
+// 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_content.h"
+
+#include <cmath>
+#include <utility>
+#include <vector>
+
+#include "content/renderer/media/media_stream_constraints_util_sets.h"
+#include "content/renderer/media/media_stream_video_source.h"
+#include "media/base/limits.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace content {
+
+namespace {
+
+using Point = ResolutionSet::Point;
+
+// Hard upper- and lower-bound frame rates for tab/desktop capture.
hta - Chromium 2017/03/08 13:34:08 Nit: Dashes shouldn't be here. "Hard upper and low
Guido Urdaneta 2017/03/09 14:38:38 Done.
+constexpr double kMaxScreenCastFrameRate = 120.0;
+constexpr double kMinScreenCastFrameRate = 1.0 / 60.0;
+
+constexpr double kDefaultFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
+
+constexpr int kMinScreenCastDimension = 1;
+constexpr int kMaxScreenCastDimension = media::limits::kMaxDimension - 1;
hta - Chromium 2017/03/08 13:34:08 Still wonder why you do -1 on this number so that
Guido Urdaneta 2017/03/09 14:38:38 The actual limit is kMaxDimension-1. See https:/
+constexpr double kMinScreenCastAspectRatio =
+ static_cast<double>(kMinScreenCastDimension) /
+ static_cast<double>(kMaxScreenCastDimension);
+constexpr double kMaxScreenCastAspectRatio =
+ static_cast<double>(kMaxScreenCastDimension) /
+ static_cast<double>(kMinScreenCastDimension);
+
+using StringSet = DiscreteSet<std::string>;
+StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
+ if (!constraint.hasExact())
+ return StringSet::UniversalSet();
+
+ std::vector<std::string> elements;
+ for (const auto& entry : constraint.exact())
+ elements.push_back(entry.ascii());
+
+ return StringSet(std::move(elements));
+}
+
+using BoolSet = DiscreteSet<bool>;
+BoolSet BoolSetFromConstraint(const blink::BooleanConstraint& constraint) {
+ if (!constraint.hasExact())
+ return BoolSet::UniversalSet();
+
+ return BoolSet({constraint.exact()});
+}
+
+using DoubleRangeSet = NumericRangeSet<double>;
+
+class VideoContentCaptureCandidates {
+ public:
+ VideoContentCaptureCandidates()
+ : device_id_set(StringSet::UniversalSet()),
+ noise_reduction_set(BoolSet::UniversalSet()) {}
+ explicit VideoContentCaptureCandidates(
+ const blink::WebMediaTrackConstraintSet& constraint_set)
+ : resolution_set(ResolutionSet::FromConstraintSet(constraint_set)),
+ frame_rate_set(
+ DoubleRangeSet::FromConstraint(constraint_set.frameRate)),
+ device_id_set(StringSetFromConstraint(constraint_set.deviceId)),
+ noise_reduction_set(
+ BoolSetFromConstraint(constraint_set.googNoiseReduction)) {}
+
+ VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) =
+ default;
+ VideoContentCaptureCandidates& operator=(
+ VideoContentCaptureCandidates&& other) = default;
+
+ bool IsEmpty() const {
+ return resolution_set.IsEmpty() || frame_rate_set.IsEmpty() ||
+ device_id_set.IsEmpty() || noise_reduction_set.IsEmpty();
+ }
+
+ VideoContentCaptureCandidates Intersection(
+ const VideoContentCaptureCandidates& other) {
+ VideoContentCaptureCandidates intersection;
+ intersection.resolution_set =
+ resolution_set.Intersection(other.resolution_set);
+ intersection.frame_rate_set =
+ frame_rate_set.Intersection(other.frame_rate_set);
+ intersection.device_id_set =
+ device_id_set.Intersection(other.device_id_set);
+ intersection.noise_reduction_set =
+ noise_reduction_set.Intersection(other.noise_reduction_set);
+ return intersection;
+ }
+
+ ResolutionSet resolution_set;
+ DoubleRangeSet frame_rate_set;
+ StringSet device_id_set;
+ BoolSet noise_reduction_set;
+};
+
+media::ResolutionChangePolicy SelectResolutionPolicyFromCandidates(
+ const ResolutionSet& resolution_set,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set,
+ int min_height_capability,
+ int max_height_capability,
+ int min_width_capability,
+ int max_width_capability) {
+ double min_aspect_ratio_capability =
+ static_cast<double>(min_width_capability) /
+ static_cast<double>(max_height_capability);
+ double max_aspect_ratio_capability =
+ static_cast<double>(max_width_capability) /
+ static_cast<double>(min_height_capability);
+ bool can_adjust_height = !basic_constraint_set.height.hasIdeal() &&
+ resolution_set.min_height() <= min_height_capability;
+
+ bool can_adjust_width = !basic_constraint_set.width.hasIdeal() &&
+ resolution_set.min_width() <= min_width_capability;
+
+ bool can_adjust_aspect_ratio =
+ !basic_constraint_set.aspectRatio.hasIdeal() &&
hta - Chromium 2017/03/08 13:34:08 Here (and above) you're saying that if ideal is sp
Guido Urdaneta 2017/03/09 14:38:38 After looking at what the policies actually do, us
+ resolution_set.min_aspect_ratio() <= min_aspect_ratio_capability &&
+ resolution_set.max_aspect_ratio() >= max_aspect_ratio_capability;
+ if (can_adjust_height && can_adjust_width) {
+ return can_adjust_aspect_ratio
+ ? media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
+ : media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
+ }
+
+ return media::RESOLUTION_POLICY_FIXED_RESOLUTION;
+}
+
+int RoundToInt(double d) {
+ return static_cast<int>(std::round(d));
+}
+
+gfx::Size ToGfxSize(const Point& point) {
+ return gfx::Size(RoundToInt(point.width()), RoundToInt(point.height()));
+}
+
+double SelectFrameRateFromCandidates(
+ const DoubleRangeSet& candidate_set,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+ double frame_rate = basic_constraint_set.frameRate.hasIdeal()
+ ? basic_constraint_set.frameRate.ideal()
+ : kDefaultFrameRate;
+ if (frame_rate > candidate_set.Max())
+ frame_rate = candidate_set.Max();
+ else if (frame_rate < candidate_set.Min())
+ frame_rate = candidate_set.Min();
+
+ return frame_rate;
+}
+
+media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
+ const VideoContentCaptureCandidates& candidates,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+ double requested_frame_rate = SelectFrameRateFromCandidates(
+ candidates.frame_rate_set, basic_constraint_set);
+ Point requested_resolution =
+ candidates.resolution_set.SelectClosestPointToIdeal(basic_constraint_set);
+ media::VideoCaptureParams params;
+ params.requested_format = media::VideoCaptureFormat(
+ ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
+ media::PIXEL_FORMAT_I420);
+ params.resolution_change_policy = SelectResolutionPolicyFromCandidates(
+ candidates.resolution_set, basic_constraint_set, kMinScreenCastDimension,
+ params.requested_format.frame_size.height(), kMinScreenCastDimension,
+ params.requested_format.frame_size.width());
+ DCHECK(params.IsValid());
+
+ return params;
+}
+
+std::string SelectDeviceIDFromCandidates(
+ const StringSet& candidates,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+ DCHECK(!candidates.IsEmpty());
+ if (basic_constraint_set.deviceId.hasIdeal()) {
+ std::string ideal_value =
hta - Chromium 2017/03/08 13:34:08 Should you loop over deviceId.ideal() here? for (
Guido Urdaneta 2017/03/09 14:38:38 Done. Also added test for this.
+ basic_constraint_set.deviceId.ideal().begin()->ascii();
+ if (candidates.Contains(ideal_value)) {
+ return ideal_value;
+ }
+ }
+
+ if (candidates.is_universal()) {
+ return std::string();
+ }
+
+ return candidates.FirstElement();
+}
+
+rtc::Optional<bool> SelectNoiseReductionFromCandidates(
+ const BoolSet& candidates,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+ DCHECK(!candidates.IsEmpty());
+ if (basic_constraint_set.googNoiseReduction.hasIdeal() &&
+ candidates.Contains(basic_constraint_set.googNoiseReduction.ideal())) {
+ return rtc::Optional<bool>(basic_constraint_set.googNoiseReduction.ideal());
+ }
+
+ if (candidates.is_universal())
+ return rtc::Optional<bool>();
+
+ return rtc::Optional<bool>(candidates.FirstElement());
+}
+
+VideoContentCaptureSourceSelectionResult SelectResultFromCandidates(
+ const VideoContentCaptureCandidates& candidates,
+ const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
+ VideoContentCaptureSourceSelectionResult result;
+ result.failed_constraint_name = nullptr;
+ result.capture_params =
+ SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set);
+ result.device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
+ basic_constraint_set);
+ result.noise_reduction = SelectNoiseReductionFromCandidates(
+ candidates.noise_reduction_set, basic_constraint_set);
+ return result;
+}
+
+VideoContentCaptureSourceSelectionResult UnsatisfiedConstraintsResult(
+ const VideoContentCaptureCandidates& candidates,
+ const blink::WebMediaTrackConstraintSet& constraint_set) {
+ DCHECK(candidates.IsEmpty());
+ VideoContentCaptureSourceSelectionResult result;
+ if (candidates.resolution_set.IsHeightEmpty()) {
+ result.failed_constraint_name = constraint_set.height.name();
hta - Chromium 2017/03/08 13:34:08 Would it be as clear to have a constructor for res
Guido Urdaneta 2017/03/09 14:38:38 Done.
+ } else if (candidates.resolution_set.IsWidthEmpty()) {
+ result.failed_constraint_name = constraint_set.width.name();
+ } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
+ result.failed_constraint_name = constraint_set.aspectRatio.name();
+ } else if (candidates.frame_rate_set.IsEmpty()) {
+ result.failed_constraint_name = constraint_set.frameRate.name();
+ } else if (candidates.noise_reduction_set.IsEmpty()) {
+ result.failed_constraint_name = constraint_set.googNoiseReduction.name();
+ } else {
+ DCHECK(candidates.device_id_set.IsEmpty());
+ result.failed_constraint_name = constraint_set.frameRate.name();
hta - Chromium 2017/03/08 13:34:08 This choice seems odd - checking on device_id_iset
Guido Urdaneta 2017/03/09 14:38:38 Fixed. In practice this can't be reached because a
+ }
+
+ return result;
+}
+
+} // namespace
+
+VideoContentCaptureSourceSelectionResult::
+ VideoContentCaptureSourceSelectionResult()
+ : failed_constraint_name("") {}
+VideoContentCaptureSourceSelectionResult::
+ VideoContentCaptureSourceSelectionResult(
+ const VideoContentCaptureSourceSelectionResult& other) = default;
+VideoContentCaptureSourceSelectionResult::
+ VideoContentCaptureSourceSelectionResult(
+ VideoContentCaptureSourceSelectionResult&& other) = default;
+VideoContentCaptureSourceSelectionResult::
+ ~VideoContentCaptureSourceSelectionResult() = default;
+VideoContentCaptureSourceSelectionResult&
+VideoContentCaptureSourceSelectionResult::operator=(
+ const VideoContentCaptureSourceSelectionResult& other) = default;
+VideoContentCaptureSourceSelectionResult&
+VideoContentCaptureSourceSelectionResult::operator=(
+ VideoContentCaptureSourceSelectionResult&& other) = default;
+
+bool VideoContentCaptureSourceSelectionResult::HasValue() const {
+ return failed_constraint_name == nullptr;
+}
+
+int VideoContentCaptureSourceSelectionResult::Height() const {
+ DCHECK(HasValue());
+ return capture_params.requested_format.frame_size.height();
+}
+
+int VideoContentCaptureSourceSelectionResult::Width() const {
+ DCHECK(HasValue());
+ return capture_params.requested_format.frame_size.width();
+}
+
+float VideoContentCaptureSourceSelectionResult::FrameRate() const {
+ DCHECK(HasValue());
+ return capture_params.requested_format.frame_rate;
+}
+
+media::ResolutionChangePolicy
+VideoContentCaptureSourceSelectionResult::ResolutionChangePolicy() const {
+ DCHECK(HasValue());
+ return capture_params.resolution_change_policy;
+}
+
+VideoContentCaptureSourceSelectionResult
+SelectVideoContentCaptureSourceSettings(
+ const blink::WebMediaConstraints& constraints) {
+ VideoContentCaptureCandidates candidates;
+ candidates.resolution_set =
+ ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
+ kMinScreenCastDimension, kMaxScreenCastDimension,
+ kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
+ candidates.frame_rate_set =
+ DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
+ // candidates.device_id_set and candidates.noise_reduction_set are
+ // automatically initialized with the universal set.
+
+ candidates = candidates.Intersection(
+ VideoContentCaptureCandidates(constraints.basic()));
+ if (candidates.IsEmpty())
+ return UnsatisfiedConstraintsResult(candidates, constraints.basic());
+
+ for (const auto& advanced_set : constraints.advanced()) {
+ VideoContentCaptureCandidates advanced_candidates(advanced_set);
+ VideoContentCaptureCandidates intersection =
+ candidates.Intersection(advanced_candidates);
+ if (!intersection.IsEmpty())
+ candidates = std::move(intersection);
+ }
+
+ DCHECK(!candidates.IsEmpty());
+ return SelectResultFromCandidates(candidates, constraints.basic());
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698