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

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

Powered by Google App Engine
This is Rietveld 408576698