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

Side by Side Diff: content/renderer/media/media_stream_constraints_util_sets.cc

Issue 2728633002: Add utility set classes to support getUserMedia constraint proccessing. (Closed)
Patch Set: Add missing#include and make ResolutionSet::ClosestPointTo private 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 unified diff | Download patch
OLDNEW
(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 #include "content/renderer/media/media_stream_constraints_util_sets.h"
6
7 #include <cmath>
8
9 #include "content/renderer/media/media_stream_constraints_util.h"
10 #include "content/renderer/media/media_stream_video_source.h"
11 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
12
13 namespace content {
14
15 using Point = ResolutionSet::Point;
16
17 namespace {
18
19 constexpr double kTolerance = 1e-5;
20
21 constexpr int kDefaultHeight = MediaStreamVideoSource::kDefaultHeight;
22 constexpr int kDefaultWidth = MediaStreamVideoSource::kDefaultWidth;
23 constexpr double kDefaultAspectRatio =
24 MediaStreamVideoSource::kDefaultAspectRatio;
25
26 // Not perfect, but good enough for this application.
27 bool AreApproximatelyEqual(double d1, double d2) {
28 if (std::fabs((d1 - d2)) <= kTolerance)
29 return true;
30
31 return d1 == d2 ||
32 (std::fabs((d1 - d2) / d1) <= kTolerance &&
33 std::fabs((d1 - d2) / d2) <= kTolerance);
34 }
35
36 bool IsLess(double d1, double d2) {
37 return d1 < d2 && !AreApproximatelyEqual(d1, d2);
38 }
39
40 bool IsLessOrEqual(double d1, double d2) {
41 return d1 < d2 || AreApproximatelyEqual(d1, d2);
42 }
43
44 bool IsGreater(double d1, double d2) {
45 return d1 > d2 && !AreApproximatelyEqual(d1, d2);
46 }
47
48 bool IsGreaterOrEqual(double d1, double d2) {
49 return d1 > d2 || AreApproximatelyEqual(d1, d2);
50 }
51
52 int ToValidDimension(long dimension) {
53 if (dimension > ResolutionSet::kMaxDimension)
54 return ResolutionSet::kMaxDimension;
55 if (dimension < 0)
56 return 0;
57
58 return static_cast<int>(dimension);
59 }
60
61 int MinDimensionFromConstraint(const blink::LongConstraint& constraint) {
62 if (!ConstraintHasMin(constraint))
63 return 0;
64
65 return ToValidDimension(ConstraintMin(constraint));
66 }
67
68 int MaxDimensionFromConstraint(const blink::LongConstraint& constraint) {
69 if (!ConstraintHasMax(constraint))
70 return ResolutionSet::kMaxDimension;
71
72 return ToValidDimension(ConstraintMax(constraint));
73 }
74
75 double ToValidAspectRatio(double aspect_ratio) {
76 return aspect_ratio < 0.0 ? 0.0 : aspect_ratio;
77 }
78
79 double MinAspectRatioFromConstraint(const blink::DoubleConstraint& constraint) {
80 if (!ConstraintHasMin(constraint))
81 return 0.0;
82
83 return ToValidAspectRatio(ConstraintMin(constraint));
84 }
85
86 double MaxAspectRatioFromConstraint(const blink::DoubleConstraint& constraint) {
87 if (!ConstraintHasMax(constraint))
88 return HUGE_VAL;
89
90 return ToValidAspectRatio(ConstraintMax(constraint));
91 }
92
93 bool IsPositiveFiniteAspectRatio(double aspect_ratio) {
94 return std::isfinite(aspect_ratio) && aspect_ratio > 0.0;
95 }
96
97 // If |vertices| has a single element, return |vertices[0]|.
98 // If |vertices| has two elements, returns the point in the segment defined by
99 // |vertices| that is closest to |point|.
100 // |vertices| must have 1 or 2 elements. Otherwise, behavior is undefined.
101 // This function is called when |point| has already been determined to be
102 // outside a polygon and |vertices| is the vertex or side closest to |point|.
103 Point GetClosestPointToVertexOrSide(const std::vector<Point> vertices,
104 const Point& point) {
105 DCHECK(!vertices.empty());
106 // If only a single vertex closest to |point|, return that vertex.
107 if (vertices.size() == 1U)
108 return vertices[0];
109
110 DCHECK_EQ(vertices.size(), 2U);
111 // If a polygon side is closest to the ideal height, return the
112 // point with aspect ratio closest to the default.
113 return Point::ClosestPointInSegment(point, vertices[0], vertices[1]);
114 }
115
116 Point SelectPointWithLargestArea(const Point& p1, const Point& p2) {
117 return p1.width() * p1.height() > p2.width() * p2.height() ? p1 : p2;
118 }
119
120 } // namespace
121
122 Point::Point(double height, double width) : height_(height), width_(width) {
123 DCHECK(!std::isnan(height_));
124 DCHECK(!std::isnan(width_));
125 }
126 Point::Point(const Point& other) = default;
127 Point& Point::operator=(const Point& other) = default;
128 Point::~Point() = default;
129
130 bool Point::operator==(const Point& other) const {
131 return height_ == other.height_ && width_ == other.width_;
132 }
133
134 bool Point::operator!=(const Point& other) const {
135 return !(*this == other);
136 }
137
138 bool Point::IsApproximatelyEqualTo(const Point& other) const {
139 return AreApproximatelyEqual(height_, other.height_) &&
140 AreApproximatelyEqual(width_, other.width_);
141 }
142
143 Point Point::operator+(const Point& other) const {
144 return Point(height_ + other.height_, width_ + other.width_);
145 }
146
147 Point Point::operator-(const Point& other) const {
148 return Point(height_ - other.height_, width_ - other.width_);
149 }
150
151 Point operator*(double d, const Point& p) {
152 return Point(d * p.height(), d * p.width());
153 }
154
155 // Returns the dot product between |p1| and |p2|.
156 // static
157 double Point::Dot(const Point& p1, const Point& p2) {
158 return p1.height_ * p2.height_ + p1.width_ * p2.width_;
159 }
160
161 // static
162 double Point::SquareEuclideanDistance(const Point& p1, const Point& p2) {
163 Point diff = p1 - p2;
164 return Dot(diff, diff);
165 }
166
167 // static
168 Point Point::ClosestPointInSegment(const Point& p,
169 const Point& s1,
170 const Point& s2) {
171 // If |s1| and |s2| are the same, it is not really a segment. The closest
172 // point to |p| is |s1|=|s2|.
173 if (s1 == s2)
174 return s1;
175
176 // Translate coordinates to a system where the origin is |s1|.
177 Point p_trans = p - s1;
178 Point s2_trans = s2 - s1;
179
180 // On this system, we are interested in the projection of |p_trans| on
181 // |s2_trans|. The projection is m * |s2_trans|, where
182 // m = Dot(|s2_trans|, |p_trans|) / Dot(|s2_trans|, |s2_trans|).
183 // If 0 <= m <= 1, the projection falls within the segment, and the closest
184 // point is the projection itself.
185 // If m < 0, the closest point is S1.
186 // If m > 1, the closest point is S2.
187 double m = Dot(s2_trans, p_trans) / Dot(s2_trans, s2_trans);
188 if (m < 0)
189 return s1;
190 else if (m > 1)
191 return s2;
192
193 // Return the projection in the original coordinate system.
194 return s1 + m * s2_trans;
195 }
196
197 ResolutionSet::ResolutionSet(int min_height,
198 int max_height,
199 int min_width,
200 int max_width,
201 double min_aspect_ratio,
202 double max_aspect_ratio)
203 : min_height_(min_height),
204 max_height_(max_height),
205 min_width_(min_width),
206 max_width_(max_width),
207 min_aspect_ratio_(min_aspect_ratio),
208 max_aspect_ratio_(max_aspect_ratio) {
209 DCHECK_GE(min_height_, 0);
210 DCHECK_GE(max_height_, 0);
211 DCHECK_LE(max_height_, kMaxDimension);
212 DCHECK_GE(min_width_, 0);
213 DCHECK_GE(max_width_, 0);
214 DCHECK_LE(max_width_, kMaxDimension);
215 DCHECK_GE(min_aspect_ratio_, 0.0);
216 DCHECK_GE(max_aspect_ratio_, 0.0);
217 DCHECK(!std::isnan(min_aspect_ratio_));
218 DCHECK(!std::isnan(max_aspect_ratio_));
219 }
220
221 ResolutionSet::ResolutionSet()
222 : ResolutionSet(0, kMaxDimension, 0, kMaxDimension, 0.0, HUGE_VAL) {}
223
224 ResolutionSet::ResolutionSet(const ResolutionSet& other) = default;
225 ResolutionSet::~ResolutionSet() = default;
226 ResolutionSet& ResolutionSet::operator=(const ResolutionSet& other) = default;
227
228 bool ResolutionSet::IsHeightEmpty() const {
229 return min_height_ > max_height_ || min_height_ >= kMaxDimension ||
230 max_height_ <= 0;
231 }
232
233 bool ResolutionSet::IsWidthEmpty() const {
234 return min_width_ > max_width_ || min_width_ >= kMaxDimension ||
235 max_width_ <= 0;
236 }
237
238 bool ResolutionSet::IsAspectRatioEmpty() const {
239 double max_resolution_aspect_ratio =
240 static_cast<double>(max_width_) / static_cast<double>(min_height_);
241 double min_resolution_aspect_ratio =
242 static_cast<double>(min_width_) / static_cast<double>(max_height_);
243
244 return IsGreater(min_aspect_ratio_, max_aspect_ratio_) ||
245 IsLess(max_resolution_aspect_ratio, min_aspect_ratio_) ||
246 IsGreater(min_resolution_aspect_ratio, max_aspect_ratio_) ||
247 !std::isfinite(min_aspect_ratio_) || max_aspect_ratio_ <= 0.0;
248 }
249
250 bool ResolutionSet::IsEmpty() const {
251 return IsHeightEmpty() || IsWidthEmpty() || IsAspectRatioEmpty();
252 }
253
254 bool ResolutionSet::ContainsPoint(const Point& point) const {
255 double ratio = point.AspectRatio();
256 return point.height() >= min_height_ && point.height() <= max_height_ &&
257 point.width() >= min_width_ && point.width() <= max_width_ &&
258 ((IsGreaterOrEqual(ratio, min_aspect_ratio_) &&
259 IsLessOrEqual(ratio, max_aspect_ratio_)) ||
260 // (0.0, 0.0) is always included in the aspect-ratio range.
261 (point.width() == 0.0 && point.height() == 0.0));
262 }
263
264 bool ResolutionSet::ContainsPoint(int height, int width) const {
265 return ContainsPoint(Point(height, width));
266 }
267
268 ResolutionSet ResolutionSet::Intersection(const ResolutionSet& other) const {
269 return ResolutionSet(std::max(min_height_, other.min_height_),
270 std::min(max_height_, other.max_height_),
271 std::max(min_width_, other.min_width_),
272 std::min(max_width_, other.max_width_),
273 std::max(min_aspect_ratio_, other.min_aspect_ratio_),
274 std::min(max_aspect_ratio_, other.max_aspect_ratio_));
275 }
276
277 Point ResolutionSet::SelectClosestPointToIdeal(
278 const blink::WebMediaTrackConstraintSet& constraint_set) const {
279 DCHECK(!IsEmpty());
280 int num_ideals = 0;
281 if (constraint_set.height.hasIdeal())
282 ++num_ideals;
283 if (constraint_set.width.hasIdeal())
284 ++num_ideals;
285 if (constraint_set.aspectRatio.hasIdeal())
286 ++num_ideals;
287
288 switch (num_ideals) {
289 case 0:
290 return SelectClosestPointToIdealAspectRatio(kDefaultAspectRatio);
291
292 case 1:
293 // This case requires a point closest to a line.
294 // In all variants, if the ideal line intersects the polygon, select the
295 // point in the intersection that is closest to preserving the default
296 // aspect ratio or a default dimension.
297 // If the ideal line is outside the polygon, there is either a single
298 // vertex or a polygon side closest to the ideal line. If a single vertex,
299 // select that vertex. If a polygon side, select the point on that side
300 // that is closest to preserving the default aspect ratio or a default
301 // dimension.
302 if (constraint_set.height.hasIdeal()) {
303 int ideal_height = ToValidDimension(constraint_set.height.ideal());
304 ResolutionSet ideal_line = ResolutionSet::FromExactHeight(ideal_height);
305 ResolutionSet intersection = Intersection(ideal_line);
306 if (!intersection.IsEmpty()) {
307 return intersection.ClosestPointTo(
308 Point(ideal_height, ideal_height * kDefaultAspectRatio));
309 }
310 std::vector<Point> closest_vertices =
311 GetClosestVertices(&Point::height, ideal_height);
312 Point ideal_point(closest_vertices[0].height(),
313 closest_vertices[0].height() * kDefaultAspectRatio);
314 return GetClosestPointToVertexOrSide(closest_vertices, ideal_point);
315 } else if (constraint_set.width.hasIdeal()) {
316 int ideal_width = ToValidDimension(constraint_set.width.ideal());
317 ResolutionSet ideal_line = ResolutionSet::FromExactWidth(ideal_width);
318 ResolutionSet intersection = Intersection(ideal_line);
319 if (!intersection.IsEmpty()) {
320 return intersection.ClosestPointTo(
321 Point(ideal_width / kDefaultAspectRatio, ideal_width));
322 }
323 std::vector<Point> closest_vertices =
324 GetClosestVertices(&Point::width, ideal_width);
325 Point ideal_point(closest_vertices[0].width() / kDefaultAspectRatio,
326 closest_vertices[0].width());
327 return GetClosestPointToVertexOrSide(closest_vertices, ideal_point);
328 } else {
329 DCHECK(constraint_set.aspectRatio.hasIdeal());
330 double ideal_aspect_ratio =
331 ToValidAspectRatio(constraint_set.aspectRatio.ideal());
332 return SelectClosestPointToIdealAspectRatio(ideal_aspect_ratio);
333 }
334 NOTREACHED();
335
336 case 2:
337 case 3:
338 double ideal_height;
339 double ideal_width;
340 if (constraint_set.height.hasIdeal()) {
341 ideal_height = ToValidDimension(constraint_set.height.ideal());
342 ideal_width =
343 constraint_set.width.hasIdeal()
344 ? ToValidDimension(constraint_set.width.ideal())
345 : ideal_height *
346 ToValidAspectRatio(constraint_set.aspectRatio.ideal());
347 } else {
348 DCHECK(constraint_set.width.hasIdeal());
349 DCHECK(constraint_set.aspectRatio.hasIdeal());
350 ideal_width = ToValidDimension(constraint_set.width.ideal());
351 ideal_height = ideal_width /
352 ToValidAspectRatio(constraint_set.aspectRatio.ideal());
353 }
354 return ClosestPointTo(Point(ideal_height, ideal_width));
355
356 default:
357 NOTREACHED();
358 }
359 NOTREACHED();
360 return Point(-1, -1);
361 }
362
363 Point ResolutionSet::SelectClosestPointToIdealAspectRatio(
364 double ideal_aspect_ratio) const {
365 ResolutionSet intersection =
366 Intersection(ResolutionSet::FromExactAspectRatio(ideal_aspect_ratio));
367 if (!intersection.IsEmpty()) {
368 Point default_height_point(kDefaultHeight,
369 kDefaultHeight * ideal_aspect_ratio);
370 Point default_width_point(kDefaultWidth / ideal_aspect_ratio,
371 kDefaultWidth);
372 return SelectPointWithLargestArea(
373 intersection.ClosestPointTo(default_height_point),
374 intersection.ClosestPointTo(default_width_point));
375 }
376 std::vector<Point> closest_vertices =
377 GetClosestVertices(&Point::AspectRatio, ideal_aspect_ratio);
378 double actual_aspect_ratio = closest_vertices[0].AspectRatio();
379 Point default_height_point(kDefaultHeight,
380 kDefaultHeight * actual_aspect_ratio);
381 Point default_width_point(kDefaultWidth / actual_aspect_ratio, kDefaultWidth);
382 return SelectPointWithLargestArea(
383 GetClosestPointToVertexOrSide(closest_vertices, default_height_point),
384 GetClosestPointToVertexOrSide(closest_vertices, default_width_point));
385 }
386
387 Point ResolutionSet::ClosestPointTo(const Point& point) const {
388 DCHECK(std::numeric_limits<double>::has_infinity);
389
390 if (ContainsPoint(point))
391 return point;
392
393 auto vertices = ComputeVertices();
394 DCHECK_GE(vertices.size(), 1U);
395 Point best_candidate(0, 0);
396 double best_distance = HUGE_VAL;
397 for (size_t i = 0; i < vertices.size(); ++i) {
398 Point candidate = Point::ClosestPointInSegment(
399 point, vertices[i], vertices[(i + 1) % vertices.size()]);
400 double distance = Point::SquareEuclideanDistance(point, candidate);
401 if (distance < best_distance) {
402 best_candidate = candidate;
403 best_distance = distance;
404 }
405 }
406
407 DCHECK(std::isfinite(best_distance));
408 return best_candidate;
409 }
410
411 std::vector<Point> ResolutionSet::GetClosestVertices(double (Point::*accessor)()
412 const,
413 double value) const {
414 DCHECK(!IsEmpty());
415 std::vector<Point> vertices = ComputeVertices();
416 std::vector<Point> closest_vertices;
417 double best_diff = HUGE_VAL;
418 for (const auto& vertex : vertices) {
419 double diff;
420 if (std::isfinite(value))
421 diff = std::fabs((vertex.*accessor)() - value);
422 else
423 diff = (vertex.*accessor)() == value ? 0.0 : HUGE_VAL;
424 if (diff <= best_diff) {
425 if (diff < best_diff) {
426 best_diff = diff;
427 closest_vertices.clear();
428 }
429 closest_vertices.push_back(vertex);
430 }
431 }
432 DCHECK(!closest_vertices.empty());
433 DCHECK_LE(closest_vertices.size(), 2U);
434 return closest_vertices;
435 }
436
437 // static
438 ResolutionSet ResolutionSet::FromHeight(int min, int max) {
439 return ResolutionSet(min, max, 0, kMaxDimension, 0.0, HUGE_VAL);
440 }
441
442 // static
443 ResolutionSet ResolutionSet::FromExactHeight(int value) {
444 return ResolutionSet(value, value, 0, kMaxDimension, 0.0, HUGE_VAL);
445 }
446
447 // static
448 ResolutionSet ResolutionSet::FromWidth(int min, int max) {
449 return ResolutionSet(0, kMaxDimension, min, max, 0.0, HUGE_VAL);
450 }
451
452 // static
453 ResolutionSet ResolutionSet::FromExactWidth(int value) {
454 return ResolutionSet(0, kMaxDimension, value, value, 0.0, HUGE_VAL);
455 }
456
457 // static
458 ResolutionSet ResolutionSet::FromAspectRatio(double min, double max) {
459 return ResolutionSet(0, kMaxDimension, 0, kMaxDimension, min, max);
460 }
461
462 // static
463 ResolutionSet ResolutionSet::FromExactAspectRatio(double value) {
464 return ResolutionSet(0, kMaxDimension, 0, kMaxDimension, value, value);
465 }
466
467 std::vector<Point> ResolutionSet::ComputeVertices() const {
468 std::vector<Point> vertices;
469 // Add vertices in counterclockwise order
470 // Start with min_height, min_width and continue along min_width.
hbos_chromium 2017/03/08 21:03:00 nit: Parenthesis around points, "Start with (min_h
Guido Urdaneta 2017/03/09 17:55:43 Done.
471 TryAddVertex(&vertices, Point(min_height_, min_width_));
472 if (IsPositiveFiniteAspectRatio(max_aspect_ratio_))
473 TryAddVertex(&vertices, Point(min_width_ / max_aspect_ratio_, min_width_));
474 if (IsPositiveFiniteAspectRatio(min_aspect_ratio_))
475 TryAddVertex(&vertices, Point(min_width_ / min_aspect_ratio_, min_width_));
476 TryAddVertex(&vertices, Point(max_height_, min_width_));
477 // Continue along max_height.
478 if (IsPositiveFiniteAspectRatio(min_aspect_ratio_)) {
479 TryAddVertex(&vertices,
480 Point(max_height_, max_height_ * min_aspect_ratio_));
481 }
482 if (IsPositiveFiniteAspectRatio(max_aspect_ratio_))
483 TryAddVertex(&vertices,
484 Point(max_height_, max_height_ * max_aspect_ratio_));
485 TryAddVertex(&vertices, Point(max_height_, max_width_));
486 // Continue along max_width.
487 if (IsPositiveFiniteAspectRatio(min_aspect_ratio_))
488 TryAddVertex(&vertices, Point(max_width_ / min_aspect_ratio_, max_width_));
489 if (IsPositiveFiniteAspectRatio(max_aspect_ratio_))
490 TryAddVertex(&vertices, Point(max_width_ / max_aspect_ratio_, max_width_));
491 TryAddVertex(&vertices, Point(min_height_, max_width_));
492 // Finish along min_height.
493 if (IsPositiveFiniteAspectRatio(max_aspect_ratio_)) {
494 TryAddVertex(&vertices,
495 Point(min_height_, min_height_ * max_aspect_ratio_));
496 }
497 if (IsPositiveFiniteAspectRatio(min_aspect_ratio_)) {
498 TryAddVertex(&vertices,
499 Point(min_height_, min_height_ * min_aspect_ratio_));
500 }
501
502 DCHECK_LE(vertices.size(), 6U);
503 return vertices;
504 }
505
506 void ResolutionSet::TryAddVertex(std::vector<Point>* vertices,
507 const Point& point) const {
508 if (!ContainsPoint(point))
509 return;
510
511 // Add the point to the |vertices| if not already added.
512 // This is to prevent duplicates in case an aspect ratio intersects a width
513 // or height right on a vertex.
514 if (vertices->empty() ||
515 (*(vertices->end() - 1) != point && *vertices->begin() != point)) {
516 vertices->push_back(point);
517 }
518 }
519
520 ResolutionSet ResolutionSet::FromConstraintSet(
521 const blink::WebMediaTrackConstraintSet& constraint_set) {
522 return ResolutionSet(
523 MinDimensionFromConstraint(constraint_set.height),
524 MaxDimensionFromConstraint(constraint_set.height),
525 MinDimensionFromConstraint(constraint_set.width),
526 MaxDimensionFromConstraint(constraint_set.width),
527 MinAspectRatioFromConstraint(constraint_set.aspectRatio),
528 MaxAspectRatioFromConstraint(constraint_set.aspectRatio));
529 }
530
531 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698