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

Side by Side 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 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_video_content.h"
6
7 #include <cmath>
8 #include <utility>
9 #include <vector>
10
11 #include "content/renderer/media/media_stream_constraints_util_sets.h"
12 #include "content/renderer/media/media_stream_video_source.h"
13 #include "media/base/limits.h"
14 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
15 #include "third_party/WebKit/public/platform/WebString.h"
16
17 namespace content {
18
19 namespace {
20
21 using Point = ResolutionSet::Point;
22
23 // 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.
24 constexpr double kMaxScreenCastFrameRate = 120.0;
25 constexpr double kMinScreenCastFrameRate = 1.0 / 60.0;
26
27 constexpr double kDefaultFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
28
29 constexpr int kMinScreenCastDimension = 1;
30 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:/
31 constexpr double kMinScreenCastAspectRatio =
32 static_cast<double>(kMinScreenCastDimension) /
33 static_cast<double>(kMaxScreenCastDimension);
34 constexpr double kMaxScreenCastAspectRatio =
35 static_cast<double>(kMaxScreenCastDimension) /
36 static_cast<double>(kMinScreenCastDimension);
37
38 using StringSet = DiscreteSet<std::string>;
39 StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
40 if (!constraint.hasExact())
41 return StringSet::UniversalSet();
42
43 std::vector<std::string> elements;
44 for (const auto& entry : constraint.exact())
45 elements.push_back(entry.ascii());
46
47 return StringSet(std::move(elements));
48 }
49
50 using BoolSet = DiscreteSet<bool>;
51 BoolSet BoolSetFromConstraint(const blink::BooleanConstraint& constraint) {
52 if (!constraint.hasExact())
53 return BoolSet::UniversalSet();
54
55 return BoolSet({constraint.exact()});
56 }
57
58 using DoubleRangeSet = NumericRangeSet<double>;
59
60 class VideoContentCaptureCandidates {
61 public:
62 VideoContentCaptureCandidates()
63 : device_id_set(StringSet::UniversalSet()),
64 noise_reduction_set(BoolSet::UniversalSet()) {}
65 explicit VideoContentCaptureCandidates(
66 const blink::WebMediaTrackConstraintSet& constraint_set)
67 : resolution_set(ResolutionSet::FromConstraintSet(constraint_set)),
68 frame_rate_set(
69 DoubleRangeSet::FromConstraint(constraint_set.frameRate)),
70 device_id_set(StringSetFromConstraint(constraint_set.deviceId)),
71 noise_reduction_set(
72 BoolSetFromConstraint(constraint_set.googNoiseReduction)) {}
73
74 VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) =
75 default;
76 VideoContentCaptureCandidates& operator=(
77 VideoContentCaptureCandidates&& other) = default;
78
79 bool IsEmpty() const {
80 return resolution_set.IsEmpty() || frame_rate_set.IsEmpty() ||
81 device_id_set.IsEmpty() || noise_reduction_set.IsEmpty();
82 }
83
84 VideoContentCaptureCandidates Intersection(
85 const VideoContentCaptureCandidates& other) {
86 VideoContentCaptureCandidates intersection;
87 intersection.resolution_set =
88 resolution_set.Intersection(other.resolution_set);
89 intersection.frame_rate_set =
90 frame_rate_set.Intersection(other.frame_rate_set);
91 intersection.device_id_set =
92 device_id_set.Intersection(other.device_id_set);
93 intersection.noise_reduction_set =
94 noise_reduction_set.Intersection(other.noise_reduction_set);
95 return intersection;
96 }
97
98 ResolutionSet resolution_set;
99 DoubleRangeSet frame_rate_set;
100 StringSet device_id_set;
101 BoolSet noise_reduction_set;
102 };
103
104 media::ResolutionChangePolicy SelectResolutionPolicyFromCandidates(
105 const ResolutionSet& resolution_set,
106 const blink::WebMediaTrackConstraintSet& basic_constraint_set,
107 int min_height_capability,
108 int max_height_capability,
109 int min_width_capability,
110 int max_width_capability) {
111 double min_aspect_ratio_capability =
112 static_cast<double>(min_width_capability) /
113 static_cast<double>(max_height_capability);
114 double max_aspect_ratio_capability =
115 static_cast<double>(max_width_capability) /
116 static_cast<double>(min_height_capability);
117 bool can_adjust_height = !basic_constraint_set.height.hasIdeal() &&
118 resolution_set.min_height() <= min_height_capability;
119
120 bool can_adjust_width = !basic_constraint_set.width.hasIdeal() &&
121 resolution_set.min_width() <= min_width_capability;
122
123 bool can_adjust_aspect_ratio =
124 !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
125 resolution_set.min_aspect_ratio() <= min_aspect_ratio_capability &&
126 resolution_set.max_aspect_ratio() >= max_aspect_ratio_capability;
127 if (can_adjust_height && can_adjust_width) {
128 return can_adjust_aspect_ratio
129 ? media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
130 : media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
131 }
132
133 return media::RESOLUTION_POLICY_FIXED_RESOLUTION;
134 }
135
136 int RoundToInt(double d) {
137 return static_cast<int>(std::round(d));
138 }
139
140 gfx::Size ToGfxSize(const Point& point) {
141 return gfx::Size(RoundToInt(point.width()), RoundToInt(point.height()));
142 }
143
144 double SelectFrameRateFromCandidates(
145 const DoubleRangeSet& candidate_set,
146 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
147 double frame_rate = basic_constraint_set.frameRate.hasIdeal()
148 ? basic_constraint_set.frameRate.ideal()
149 : kDefaultFrameRate;
150 if (frame_rate > candidate_set.Max())
151 frame_rate = candidate_set.Max();
152 else if (frame_rate < candidate_set.Min())
153 frame_rate = candidate_set.Min();
154
155 return frame_rate;
156 }
157
158 media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
159 const VideoContentCaptureCandidates& candidates,
160 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
161 double requested_frame_rate = SelectFrameRateFromCandidates(
162 candidates.frame_rate_set, basic_constraint_set);
163 Point requested_resolution =
164 candidates.resolution_set.SelectClosestPointToIdeal(basic_constraint_set);
165 media::VideoCaptureParams params;
166 params.requested_format = media::VideoCaptureFormat(
167 ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
168 media::PIXEL_FORMAT_I420);
169 params.resolution_change_policy = SelectResolutionPolicyFromCandidates(
170 candidates.resolution_set, basic_constraint_set, kMinScreenCastDimension,
171 params.requested_format.frame_size.height(), kMinScreenCastDimension,
172 params.requested_format.frame_size.width());
173 DCHECK(params.IsValid());
174
175 return params;
176 }
177
178 std::string SelectDeviceIDFromCandidates(
179 const StringSet& candidates,
180 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
181 DCHECK(!candidates.IsEmpty());
182 if (basic_constraint_set.deviceId.hasIdeal()) {
183 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.
184 basic_constraint_set.deviceId.ideal().begin()->ascii();
185 if (candidates.Contains(ideal_value)) {
186 return ideal_value;
187 }
188 }
189
190 if (candidates.is_universal()) {
191 return std::string();
192 }
193
194 return candidates.FirstElement();
195 }
196
197 rtc::Optional<bool> SelectNoiseReductionFromCandidates(
198 const BoolSet& candidates,
199 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
200 DCHECK(!candidates.IsEmpty());
201 if (basic_constraint_set.googNoiseReduction.hasIdeal() &&
202 candidates.Contains(basic_constraint_set.googNoiseReduction.ideal())) {
203 return rtc::Optional<bool>(basic_constraint_set.googNoiseReduction.ideal());
204 }
205
206 if (candidates.is_universal())
207 return rtc::Optional<bool>();
208
209 return rtc::Optional<bool>(candidates.FirstElement());
210 }
211
212 VideoContentCaptureSourceSelectionResult SelectResultFromCandidates(
213 const VideoContentCaptureCandidates& candidates,
214 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
215 VideoContentCaptureSourceSelectionResult result;
216 result.failed_constraint_name = nullptr;
217 result.capture_params =
218 SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set);
219 result.device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
220 basic_constraint_set);
221 result.noise_reduction = SelectNoiseReductionFromCandidates(
222 candidates.noise_reduction_set, basic_constraint_set);
223 return result;
224 }
225
226 VideoContentCaptureSourceSelectionResult UnsatisfiedConstraintsResult(
227 const VideoContentCaptureCandidates& candidates,
228 const blink::WebMediaTrackConstraintSet& constraint_set) {
229 DCHECK(candidates.IsEmpty());
230 VideoContentCaptureSourceSelectionResult result;
231 if (candidates.resolution_set.IsHeightEmpty()) {
232 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.
233 } else if (candidates.resolution_set.IsWidthEmpty()) {
234 result.failed_constraint_name = constraint_set.width.name();
235 } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
236 result.failed_constraint_name = constraint_set.aspectRatio.name();
237 } else if (candidates.frame_rate_set.IsEmpty()) {
238 result.failed_constraint_name = constraint_set.frameRate.name();
239 } else if (candidates.noise_reduction_set.IsEmpty()) {
240 result.failed_constraint_name = constraint_set.googNoiseReduction.name();
241 } else {
242 DCHECK(candidates.device_id_set.IsEmpty());
243 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
244 }
245
246 return result;
247 }
248
249 } // namespace
250
251 VideoContentCaptureSourceSelectionResult::
252 VideoContentCaptureSourceSelectionResult()
253 : failed_constraint_name("") {}
254 VideoContentCaptureSourceSelectionResult::
255 VideoContentCaptureSourceSelectionResult(
256 const VideoContentCaptureSourceSelectionResult& other) = default;
257 VideoContentCaptureSourceSelectionResult::
258 VideoContentCaptureSourceSelectionResult(
259 VideoContentCaptureSourceSelectionResult&& other) = default;
260 VideoContentCaptureSourceSelectionResult::
261 ~VideoContentCaptureSourceSelectionResult() = default;
262 VideoContentCaptureSourceSelectionResult&
263 VideoContentCaptureSourceSelectionResult::operator=(
264 const VideoContentCaptureSourceSelectionResult& other) = default;
265 VideoContentCaptureSourceSelectionResult&
266 VideoContentCaptureSourceSelectionResult::operator=(
267 VideoContentCaptureSourceSelectionResult&& other) = default;
268
269 bool VideoContentCaptureSourceSelectionResult::HasValue() const {
270 return failed_constraint_name == nullptr;
271 }
272
273 int VideoContentCaptureSourceSelectionResult::Height() const {
274 DCHECK(HasValue());
275 return capture_params.requested_format.frame_size.height();
276 }
277
278 int VideoContentCaptureSourceSelectionResult::Width() const {
279 DCHECK(HasValue());
280 return capture_params.requested_format.frame_size.width();
281 }
282
283 float VideoContentCaptureSourceSelectionResult::FrameRate() const {
284 DCHECK(HasValue());
285 return capture_params.requested_format.frame_rate;
286 }
287
288 media::ResolutionChangePolicy
289 VideoContentCaptureSourceSelectionResult::ResolutionChangePolicy() const {
290 DCHECK(HasValue());
291 return capture_params.resolution_change_policy;
292 }
293
294 VideoContentCaptureSourceSelectionResult
295 SelectVideoContentCaptureSourceSettings(
296 const blink::WebMediaConstraints& constraints) {
297 VideoContentCaptureCandidates candidates;
298 candidates.resolution_set =
299 ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
300 kMinScreenCastDimension, kMaxScreenCastDimension,
301 kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
302 candidates.frame_rate_set =
303 DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
304 // candidates.device_id_set and candidates.noise_reduction_set are
305 // automatically initialized with the universal set.
306
307 candidates = candidates.Intersection(
308 VideoContentCaptureCandidates(constraints.basic()));
309 if (candidates.IsEmpty())
310 return UnsatisfiedConstraintsResult(candidates, constraints.basic());
311
312 for (const auto& advanced_set : constraints.advanced()) {
313 VideoContentCaptureCandidates advanced_candidates(advanced_set);
314 VideoContentCaptureCandidates intersection =
315 candidates.Intersection(advanced_candidates);
316 if (!intersection.IsEmpty())
317 candidates = std::move(intersection);
318 }
319
320 DCHECK(!candidates.IsEmpty());
321 return SelectResultFromCandidates(candidates, constraints.basic());
322 }
323
324 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698