OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_SETS_H_ |
| 6 #define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_SETS_H_ |
| 7 |
| 8 #include <algorithm> |
| 9 #include <limits> |
| 10 #include <utility> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/gtest_prod_util.h" |
| 14 #include "base/logging.h" |
| 15 #include "content/common/content_export.h" |
| 16 #include "content/renderer/media/media_stream_constraints_util.h" |
| 17 |
| 18 namespace blink { |
| 19 struct WebMediaTrackConstraintSet; |
| 20 } |
| 21 |
| 22 namespace content { |
| 23 |
| 24 // This class template represents a set of candidates suitable for a numeric |
| 25 // range-based constraint. |
| 26 template <typename T> |
| 27 class NumericRangeSet { |
| 28 public: |
| 29 NumericRangeSet() : min_(0), max_(DefaultMax()) {} |
| 30 NumericRangeSet(T min, T max) : min_(min), max_(max) {} |
| 31 NumericRangeSet(const NumericRangeSet& other) = default; |
| 32 NumericRangeSet& operator=(const NumericRangeSet& other) = default; |
| 33 ~NumericRangeSet() = default; |
| 34 |
| 35 T Min() const { return min_; } |
| 36 T Max() const { return max_; } |
| 37 bool IsEmpty() const { return max_ < min_; } |
| 38 |
| 39 NumericRangeSet Intersection(const NumericRangeSet& other) const { |
| 40 return NumericRangeSet(std::max(min_, other.min_), |
| 41 std::min(max_, other.max_)); |
| 42 } |
| 43 |
| 44 // Creates a NumericRangeSet based on the minimum and maximum values of |
| 45 // |constraint|. |
| 46 template <typename ConstraintType> |
| 47 static auto FromConstraint(ConstraintType constraint) |
| 48 -> NumericRangeSet<decltype(constraint.min())> { |
| 49 return NumericRangeSet<decltype(constraint.min())>( |
| 50 ConstraintHasMin(constraint) ? ConstraintMin(constraint) : 0, |
| 51 ConstraintHasMax(constraint) ? ConstraintMax(constraint) |
| 52 : DefaultMax()); |
| 53 } |
| 54 |
| 55 private: |
| 56 static inline T DefaultMax() { |
| 57 return std::numeric_limits<T>::has_infinity |
| 58 ? std::numeric_limits<T>::infinity() |
| 59 : std::numeric_limits<T>::max(); |
| 60 } |
| 61 T min_; |
| 62 T max_; |
| 63 }; |
| 64 |
| 65 // This class defines a set of discrete elements suitable for resolving |
| 66 // constraints with a countable number of choices not suitable to be constrained |
| 67 // by range. Examples are strings, booleans and certain constraints of type |
| 68 // long. A DiscreteSet can be empty, have their elements explicitly stated, or |
| 69 // be the universal set. The universal set is a set that contains all possible |
| 70 // elements. The specific definition of what elements are in the universal set |
| 71 // is application defined (e.g., it could be all possible boolean values, all |
| 72 // possible strings of length N, or anything that suits a particular |
| 73 // application). |
| 74 template <typename T> |
| 75 class DiscreteSet { |
| 76 public: |
| 77 // Creates a set containing the elements in |elements|. |
| 78 // It is the responsibility of the caller to ensure that |elements| is not |
| 79 // equivalent to the universal set and that |elements| has no repeated |
| 80 // values. Takes ownership of |elements|. |
| 81 explicit DiscreteSet(std::vector<T> elements) |
| 82 : is_universal_(false), elements_(std::move(elements)) {} |
| 83 // Creates an empty set; |
| 84 static DiscreteSet EmptySet() { return DiscreteSet(std::vector<T>()); } |
| 85 static DiscreteSet UniversalSet() { return DiscreteSet(); } |
| 86 |
| 87 DiscreteSet(const DiscreteSet& other) = default; |
| 88 DiscreteSet& operator=(const DiscreteSet& other) = default; |
| 89 DiscreteSet(DiscreteSet&& other) = default; |
| 90 DiscreteSet& operator=(DiscreteSet&& other) = default; |
| 91 ~DiscreteSet() = default; |
| 92 |
| 93 bool Contains(const T& value) const { |
| 94 return is_universal_ || std::find(elements_.begin(), elements_.end(), |
| 95 value) != elements_.end(); |
| 96 } |
| 97 |
| 98 bool IsEmpty() const { return !is_universal_ && elements_.empty(); } |
| 99 |
| 100 bool HasExplicitElements() const { return !elements_.empty(); } |
| 101 |
| 102 DiscreteSet Intersection(const DiscreteSet& other) const { |
| 103 if (is_universal_) |
| 104 return other; |
| 105 if (other.is_universal_) |
| 106 return *this; |
| 107 if (IsEmpty() || other.IsEmpty()) |
| 108 return EmptySet(); |
| 109 |
| 110 // Both sets have explicit elements. |
| 111 std::vector<T> intersection; |
| 112 for (const auto& entry : elements_) { |
| 113 auto it = |
| 114 std::find(other.elements_.begin(), other.elements_.end(), entry); |
| 115 if (it != other.elements_.end()) { |
| 116 intersection.push_back(entry); |
| 117 } |
| 118 } |
| 119 return DiscreteSet(std::move(intersection)); |
| 120 } |
| 121 |
| 122 // Returns a copy of the first element in the set. This is useful as a simple |
| 123 // tie-breaker rule. This applies only to constrained nonempty sets. |
| 124 // Behavior is undefined if the set is empty or universal. |
| 125 T FirstElement() const { |
| 126 DCHECK(HasExplicitElements()); |
| 127 return elements_[0]; |
| 128 } |
| 129 |
| 130 bool is_universal() const { return is_universal_; } |
| 131 |
| 132 private: |
| 133 // Creates a universal set. |
| 134 DiscreteSet() : is_universal_(true) {} |
| 135 |
| 136 bool is_universal_; |
| 137 std::vector<T> elements_; |
| 138 }; |
| 139 |
| 140 // This class represents a set of (height, width) screen resolution candidates |
| 141 // determined by width, height and aspect-ratio constraints. |
| 142 // This class supports widths and heights from 0 to kMaxDimension, both |
| 143 // inclusive and aspect ratios from 0.0 to positive infinity, both inclusive. |
| 144 class CONTENT_EXPORT ResolutionSet { |
| 145 public: |
| 146 static constexpr int kMaxDimension = std::numeric_limits<int>::max(); |
| 147 |
| 148 // Helper class that represents (height, width) points on a plane. |
| 149 // TODO(guidou): Use a generic point/vector class that uses double once it |
| 150 // becomes available (e.g., a gfx::Vector2dD). |
| 151 class CONTENT_EXPORT Point { |
| 152 public: |
| 153 // Creates a (|height|, |width|) point. |height| and |width| must be finite. |
| 154 Point(double height, double width); |
| 155 Point(const Point& other); |
| 156 Point& operator=(const Point& other); |
| 157 ~Point(); |
| 158 |
| 159 // Accessors. |
| 160 double height() const { return height_; } |
| 161 double width() const { return width_; } |
| 162 double AspectRatio() const { return width_ / height_; } |
| 163 |
| 164 // Exact equality/inequality operators. |
| 165 bool operator==(const Point& other) const; |
| 166 bool operator!=(const Point& other) const; |
| 167 |
| 168 // Returns true if the coordinates of this point and |other| are |
| 169 // approximately equal. |
| 170 bool IsApproximatelyEqualTo(const Point& other) const; |
| 171 |
| 172 // Vector-style addition and subtraction operators. |
| 173 Point operator+(const Point& other) const; |
| 174 Point operator-(const Point& other) const; |
| 175 |
| 176 // Returns the dot product between |p1| and |p2|. |
| 177 static double Dot(const Point& p1, const Point& p2); |
| 178 |
| 179 // Returns the square Euclidean distance between |p1| and |p2|. |
| 180 static double SquareEuclideanDistance(const Point& p1, const Point& p2); |
| 181 |
| 182 // Returns the point in the line segment determined by |s1| and |s2| that |
| 183 // is closest to |p|. |
| 184 static Point ClosestPointInSegment(const Point& p, |
| 185 const Point& s1, |
| 186 const Point& s2); |
| 187 |
| 188 private: |
| 189 double height_; |
| 190 double width_; |
| 191 }; |
| 192 |
| 193 // Creates a set with the maximum supported ranges for width, height and |
| 194 // aspect ratio. |
| 195 ResolutionSet(); |
| 196 ResolutionSet(int min_height, |
| 197 int max_height, |
| 198 int min_width, |
| 199 int max_width, |
| 200 double min_aspect_ratio, |
| 201 double max_aspect_ratio); |
| 202 ResolutionSet(const ResolutionSet& other); |
| 203 ResolutionSet& operator=(const ResolutionSet& other); |
| 204 ~ResolutionSet(); |
| 205 |
| 206 // Getters. |
| 207 int min_height() const { return min_height_; } |
| 208 int max_height() const { return max_height_; } |
| 209 int min_width() const { return min_width_; } |
| 210 int max_width() const { return max_width_; } |
| 211 double min_aspect_ratio() const { return min_aspect_ratio_; } |
| 212 double max_aspect_ratio() const { return max_aspect_ratio_; } |
| 213 |
| 214 // Returns true if this set is empty. |
| 215 bool IsEmpty() const; |
| 216 |
| 217 // These functions return true if a particular variable causes the set to be |
| 218 // empty. |
| 219 bool IsHeightEmpty() const; |
| 220 bool IsWidthEmpty() const; |
| 221 bool IsAspectRatioEmpty() const; |
| 222 |
| 223 // These functions return true if the given point is included in this set. |
| 224 bool ContainsPoint(const Point& point) const; |
| 225 bool ContainsPoint(int height, int width) const; |
| 226 |
| 227 // Returns a new set with the intersection of |*this| and |other|. |
| 228 ResolutionSet Intersection(const ResolutionSet& other) const; |
| 229 |
| 230 // Returns a point in this (nonempty) set closest to the ideal values for the |
| 231 // height, width and aspectRatio constraints in |constraint_set|. |
| 232 // Note that this function ignores all the other data in |constraint_set|. |
| 233 // Only the ideal height, width and aspect ratio are used, and from now on |
| 234 // referred to as |ideal_height|, |ideal_width| and |ideal_aspect_ratio| |
| 235 // respectively. |
| 236 // |
| 237 // * If all three ideal values are given, |ideal_aspect_ratio| is ignored and |
| 238 // the point closest to (|ideal_height|, |ideal_width|) is returned. |
| 239 // * If two ideal values are given, they are used to determine a single ideal |
| 240 // point, which can be one of: |
| 241 // - (|ideal_height|, |ideal_width|), |
| 242 // - (|ideal_height|, |ideal_height|*|ideal_aspect_ratio|), or |
| 243 // - (|ideal_width| / |ideal_aspect_ratio|, |ideal_width|). |
| 244 // The point in the set closest to the ideal point is returned. |
| 245 // * If a single ideal value is given, a point in the set closest to the line |
| 246 // defined by the ideal value is returned. If there is more than one point |
| 247 // closest to the ideal line, the following tie-breaker rules are used: |
| 248 // - If |ideal_height| is provided, the point closest to |
| 249 // (|ideal_height|, |ideal_height| * kDefaultAspectRatio). |
| 250 // - If |ideal_width| is provided, the point closest to |
| 251 // (|ideal_width| / kDefaultAspectRatio, |ideal_width|). |
| 252 // - If |ideal_aspect_ratio| is provided, the point with largest area among |
| 253 // the points closest to |
| 254 // (kDefaultHeight, kDefaultHeight * aspect_ratio) and |
| 255 // (kDefaultWidth / aspect_ratio, kDefaultWidth), |
| 256 // where aspect_ratio is |ideal_aspect_ratio| if the ideal line intersects |
| 257 // the set, and the closest aspect ratio to |ideal_aspect_ratio| among the |
| 258 // points in the set if no point in the set has an aspect ratio equal to |
| 259 // |ideal_aspect_ratio|. |
| 260 // * If no ideal value is given, proceed as if only |ideal_aspect_ratio| was |
| 261 // provided with a value of kDefaultAspectRatio. |
| 262 // |
| 263 // This is intended to support the implementation of the spec algorithm for |
| 264 // selection of track settings, as defined in |
| 265 // https://w3c.github.io/mediacapture-main/#dfn-selectsettings. |
| 266 // |
| 267 // The main difference between this algorithm and the spec is that when ideal |
| 268 // values are provided, the spec mandates finding a point that minimizes the |
| 269 // sum of custom relative distances for each provided ideal value, while this |
| 270 // algorithm minimizes either the Euclidean distance (sum of square distances) |
| 271 // on a height-width plane for the cases where two or three ideal values are |
| 272 // provided, or the absolute distance for the case with one ideal value. |
| 273 // Also, in the case with three ideal values, this algorithm ignores the |
| 274 // distance to the ideal aspect ratio. |
| 275 // In most cases the difference in the final result should be negligible. |
| 276 // The reason to follow this approach is that optimization in the worst case |
| 277 // is reduced to projection of a point on line segment, which is a simple |
| 278 // operation that provides exact results. Using the distance function of the |
| 279 // spec, which is not continuous, would require complex optimization methods |
| 280 // that do not necessarily guarantee finding the real optimal value. |
| 281 // |
| 282 // This function has undefined behavior if this set is empty. |
| 283 Point SelectClosestPointToIdeal( |
| 284 const blink::WebMediaTrackConstraintSet& constraint_set) const; |
| 285 |
| 286 // Utilities that return ResolutionSets constrained on a specific variable. |
| 287 static ResolutionSet FromHeight(int min, int max); |
| 288 static ResolutionSet FromExactHeight(int value); |
| 289 static ResolutionSet FromWidth(int min, int max); |
| 290 static ResolutionSet FromExactWidth(int value); |
| 291 static ResolutionSet FromAspectRatio(double min, double max); |
| 292 static ResolutionSet FromExactAspectRatio(double value); |
| 293 |
| 294 // Returns a ResolutionCandidateSet initialized with |constraint_set|'s |
| 295 // width, height and aspectRatio constraints. |
| 296 static ResolutionSet FromConstraintSet( |
| 297 const blink::WebMediaTrackConstraintSet& constraint_set); |
| 298 |
| 299 private: |
| 300 FRIEND_TEST_ALL_PREFIXES(MediaStreamConstraintsUtilSetsTest, |
| 301 ResolutionVertices); |
| 302 FRIEND_TEST_ALL_PREFIXES(MediaStreamConstraintsUtilSetsTest, |
| 303 ResolutionPointSetClosestPoint); |
| 304 FRIEND_TEST_ALL_PREFIXES(MediaStreamConstraintsUtilSetsTest, |
| 305 ResolutionLineSetClosestPoint); |
| 306 FRIEND_TEST_ALL_PREFIXES(MediaStreamConstraintsUtilSetsTest, |
| 307 ResolutionGeneralSetClosestPoint); |
| 308 FRIEND_TEST_ALL_PREFIXES(MediaStreamConstraintsUtilSetsTest, |
| 309 ResolutionIdealOutsideSinglePoint); |
| 310 |
| 311 // Implements SelectClosestPointToIdeal() for the case when only the ideal |
| 312 // aspect ratio is provided. |
| 313 Point SelectClosestPointToIdealAspectRatio(double ideal_aspect_ratio) const; |
| 314 |
| 315 // Returns the closest point in this set to |point|. If |point| is included in |
| 316 // this set, Point is returned. If this set is empty, behavior is undefined. |
| 317 Point ClosestPointTo(const Point& point) const; |
| 318 |
| 319 // Returns the vertices of the set that have the property accessed |
| 320 // by |accessor| closest to |value|. The returned vector always has one or two |
| 321 // elements. Behavior is undefined if the set is empty. |
| 322 std::vector<Point> GetClosestVertices(double (Point::*accessor)() const, |
| 323 double value) const; |
| 324 |
| 325 // Returns a list of the vertices defined by the constraints on a height-width |
| 326 // Cartesian plane. |
| 327 // If the list is empty, the set is empty. |
| 328 // If the list contains a single point, the set contains a single point. |
| 329 // If the list contains two points, the set is composed of points on a line |
| 330 // segment. |
| 331 // If the list contains three to six points, they are the vertices of a |
| 332 // convex polygon containing all valid points in the set. Each pair of |
| 333 // consecutive vertices (modulo the size of the list) corresponds to a side of |
| 334 // the polygon, with the vertices given in counterclockwise order. |
| 335 // The list cannot contain more than six points. |
| 336 std::vector<Point> ComputeVertices() const; |
| 337 |
| 338 // Adds |point| to |vertices| if |point| is included in this candidate set. |
| 339 void TryAddVertex(std::vector<ResolutionSet::Point>* vertices, |
| 340 const ResolutionSet::Point& point) const; |
| 341 |
| 342 int min_height_; |
| 343 int max_height_; |
| 344 int min_width_; |
| 345 int max_width_; |
| 346 double min_aspect_ratio_; |
| 347 double max_aspect_ratio_; |
| 348 }; |
| 349 |
| 350 // Scalar multiplication for Points. |
| 351 ResolutionSet::Point CONTENT_EXPORT operator*(double d, |
| 352 const ResolutionSet::Point& p); |
| 353 |
| 354 } // namespace content |
| 355 |
| 356 #endif // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_CONSTRAINTS_UTIL_SETS_H_ |
OLD | NEW |