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

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: address hta's comments 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.
24 constexpr double kMaxScreenCastFrameRate = 120.0;
25 constexpr double kMinScreenCastFrameRate = 1.0 / 60.0;
hbos_chromium 2017/03/09 16:51:10 This is what I call moving pictures! :)
Guido Urdaneta 2017/03/14 12:29:05 Acknowledged.
26
27 constexpr double kDefaultFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
28
29 constexpr int kMinScreenCastDimension = 1;
30 constexpr int kMaxScreenCastDimension = media::limits::kMaxDimension - 1;
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>;
hbos_chromium 2017/03/09 16:51:09 Move all using statements to before functions are
Guido Urdaneta 2017/03/14 12:29:06 Done.
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(
hbos_chromium 2017/03/09 16:51:09 Can you add a comment? I'm not sure I understand t
Guido Urdaneta 2017/03/14 12:29:06 Changed the policy to be simpler and added a TODO
105 const ResolutionSet& resolution_set,
106 int chosen_height,
107 int chosen_width) {
108 // TODO(guidou): Most of this code is copied from
109 // WebContentsCaptureMachine::ComputeOptimalViewSize(). Find a way to more
110 // easily share this code.
111 const auto HasIntendedAspectRatio = [chosen_height, chosen_width](
112 int width_units, int height_units) {
113 const int a = height_units * chosen_width;
114 const int b = width_units * chosen_height;
115 const int percentage_diff = 100 * std::abs((a - b)) / b;
116 return percentage_diff <= 1; // Effectively, anything strictly <2%.
hbos_chromium 2017/03/09 16:51:09 Why <2%? Why not exactly (within numerical toleran
Guido Urdaneta 2017/03/14 12:29:06 See previous comment.
117 };
118 const auto RoundToExactAspectRatio = [chosen_height, chosen_width](
119 int width_step, int height_step) {
120 const int adjusted_height =
121 std::max(chosen_height - (chosen_height % height_step), height_step);
122 DCHECK_EQ((adjusted_height * width_step) % height_step, 0);
123 return gfx::Size(adjusted_height * width_step / height_step,
124 adjusted_height);
125 };
126
127 gfx::Size adjusted_size;
128 if (HasIntendedAspectRatio(16, 9)) {
129 adjusted_size = RoundToExactAspectRatio(160, 90);
130 } else if (HasIntendedAspectRatio(4, 3)) {
131 adjusted_size = RoundToExactAspectRatio(64, 48);
hbos_chromium 2017/03/09 16:51:09 Why these step sizes and not the smallest possible
Guido Urdaneta 2017/03/14 12:29:06 See previous comment.
132 } else {
133 // There will be no adjustment, so any policy value will do.
134 return media::RESOLUTION_POLICY_FIXED_RESOLUTION;
135 }
136
137 double adjusted_aspect_ratio = static_cast<double>(adjusted_size.width()) /
138 static_cast<double>(adjusted_size.height());
139 if (adjusted_aspect_ratio >= resolution_set.min_aspect_ratio() &&
140 adjusted_aspect_ratio <= resolution_set.max_aspect_ratio() &&
141 adjusted_size.width() >= resolution_set.min_width() &&
142 adjusted_size.width() <= resolution_set.max_width() &&
143 adjusted_size.height() >= resolution_set.min_height() &&
144 adjusted_size.height() <= resolution_set.max_height()) {
145 return media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
146 }
147 return media::RESOLUTION_POLICY_FIXED_RESOLUTION;
148 }
149
150 int RoundToInt(double d) {
151 return static_cast<int>(std::round(d));
152 }
153
154 gfx::Size ToGfxSize(const Point& point) {
155 return gfx::Size(RoundToInt(point.width()), RoundToInt(point.height()));
156 }
157
158 double SelectFrameRateFromCandidates(
159 const DoubleRangeSet& candidate_set,
160 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
161 double frame_rate = basic_constraint_set.frameRate.hasIdeal()
162 ? basic_constraint_set.frameRate.ideal()
163 : kDefaultFrameRate;
164 if (frame_rate > candidate_set.Max())
165 frame_rate = candidate_set.Max();
166 else if (frame_rate < candidate_set.Min())
167 frame_rate = candidate_set.Min();
168
169 return frame_rate;
170 }
171
172 media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
173 const VideoContentCaptureCandidates& candidates,
174 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
175 double requested_frame_rate = SelectFrameRateFromCandidates(
176 candidates.frame_rate_set, basic_constraint_set);
177 Point requested_resolution =
178 candidates.resolution_set.SelectClosestPointToIdeal(basic_constraint_set);
179 media::VideoCaptureParams params;
180 params.requested_format = media::VideoCaptureFormat(
181 ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
182 media::PIXEL_FORMAT_I420);
183 params.resolution_change_policy = SelectResolutionPolicyFromCandidates(
184 candidates.resolution_set, params.requested_format.frame_size.height(),
185 params.requested_format.frame_size.width());
186 DCHECK(params.IsValid());
hbos_chromium 2017/03/09 16:51:10 Note: Will always use default PowerLineFrequency.
Guido Urdaneta 2017/03/14 12:29:05 Yes. PowerLineFrequency is ignored for content cap
187
188 return params;
189 }
190
191 std::string SelectDeviceIDFromCandidates(
192 const StringSet& candidates,
193 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
194 DCHECK(!candidates.IsEmpty());
195 if (basic_constraint_set.deviceId.hasIdeal()) {
196 for (const auto& ideal_entry : basic_constraint_set.deviceId.ideal()) {
197 std::string ideal_value = ideal_entry.ascii();
198 if (candidates.Contains(ideal_value)) {
199 return ideal_value;
200 }
201 }
202 }
203
204 if (candidates.is_universal()) {
205 return std::string();
hbos_chromium 2017/03/09 16:51:10 Should this return Optional<std::string> instead?
Guido Urdaneta 2017/03/14 12:29:05 No. The default value for device_id is the empty s
206 }
207
208 return candidates.FirstElement();
hbos_chromium 2017/03/09 16:51:09 Is the preference for the first device id intentio
Guido Urdaneta 2017/03/14 12:29:06 Added comment here and in the other call to FirstE
209 }
210
211 rtc::Optional<bool> SelectNoiseReductionFromCandidates(
212 const BoolSet& candidates,
213 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
214 DCHECK(!candidates.IsEmpty());
215 if (basic_constraint_set.googNoiseReduction.hasIdeal() &&
216 candidates.Contains(basic_constraint_set.googNoiseReduction.ideal())) {
217 return rtc::Optional<bool>(basic_constraint_set.googNoiseReduction.ideal());
218 }
219
220 if (candidates.is_universal())
221 return rtc::Optional<bool>();
222
223 return rtc::Optional<bool>(candidates.FirstElement());
224 }
225
226 VideoContentCaptureSourceSelectionResult SelectResultFromCandidates(
227 const VideoContentCaptureCandidates& candidates,
228 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
229 std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
230 basic_constraint_set);
231 media::VideoCaptureParams capture_params =
232 SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set);
233
234 rtc::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
235 candidates.noise_reduction_set, basic_constraint_set);
236
237 return VideoContentCaptureSourceSelectionResult(
238 std::move(device_id), noise_reduction, capture_params);
hbos_chromium 2017/03/09 16:51:09 What if device_id is the empty string? Shouldn't t
Guido Urdaneta 2017/03/14 12:29:05 Empty string is treated like any other ID, just li
239 }
240
241 VideoContentCaptureSourceSelectionResult UnsatisfiedConstraintsResult(
242 const VideoContentCaptureCandidates& candidates,
243 const blink::WebMediaTrackConstraintSet& constraint_set) {
244 DCHECK(candidates.IsEmpty());
245 if (candidates.resolution_set.IsHeightEmpty()) {
246 return VideoContentCaptureSourceSelectionResult(
247 constraint_set.height.name());
248 } else if (candidates.resolution_set.IsWidthEmpty()) {
249 return VideoContentCaptureSourceSelectionResult(
250 constraint_set.width.name());
251 } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
252 return VideoContentCaptureSourceSelectionResult(
253 constraint_set.aspectRatio.name());
254 } else if (candidates.frame_rate_set.IsEmpty()) {
255 return VideoContentCaptureSourceSelectionResult(
256 constraint_set.frameRate.name());
257 } else if (candidates.noise_reduction_set.IsEmpty()) {
258 return VideoContentCaptureSourceSelectionResult(
259 constraint_set.googNoiseReduction.name());
260 } else {
261 DCHECK(candidates.device_id_set.IsEmpty());
262 return VideoContentCaptureSourceSelectionResult(
263 constraint_set.deviceId.name());
264 }
265 }
266
267 } // namespace
268
269 VideoContentCaptureSourceSelectionResult::
270 VideoContentCaptureSourceSelectionResult(const char* failed_constraint_name)
271 : failed_constraint_name_(failed_constraint_name) {}
272
273 VideoContentCaptureSourceSelectionResult::
274 VideoContentCaptureSourceSelectionResult(
275 std::string device_id,
276 const rtc::Optional<bool>& noise_reduction,
277 media::VideoCaptureParams capture_params)
278 : failed_constraint_name_(nullptr),
279 device_id_(std::move(device_id)),
280 noise_reduction_(noise_reduction),
281 capture_params_(capture_params) {}
282
283 VideoContentCaptureSourceSelectionResult::
284 VideoContentCaptureSourceSelectionResult(
285 const VideoContentCaptureSourceSelectionResult& other) = default;
286 VideoContentCaptureSourceSelectionResult::
287 VideoContentCaptureSourceSelectionResult(
288 VideoContentCaptureSourceSelectionResult&& other) = default;
289 VideoContentCaptureSourceSelectionResult::
290 ~VideoContentCaptureSourceSelectionResult() = default;
291 VideoContentCaptureSourceSelectionResult&
292 VideoContentCaptureSourceSelectionResult::operator=(
293 const VideoContentCaptureSourceSelectionResult& other) = default;
294 VideoContentCaptureSourceSelectionResult&
295 VideoContentCaptureSourceSelectionResult::operator=(
296 VideoContentCaptureSourceSelectionResult&& other) = default;
297
298 int VideoContentCaptureSourceSelectionResult::Height() const {
299 DCHECK(HasValue());
300 return capture_params_.requested_format.frame_size.height();
301 }
302
303 int VideoContentCaptureSourceSelectionResult::Width() const {
304 DCHECK(HasValue());
305 return capture_params_.requested_format.frame_size.width();
306 }
307
308 float VideoContentCaptureSourceSelectionResult::FrameRate() const {
309 DCHECK(HasValue());
310 return capture_params_.requested_format.frame_rate;
311 }
312
313 media::ResolutionChangePolicy
314 VideoContentCaptureSourceSelectionResult::ResolutionChangePolicy() const {
315 DCHECK(HasValue());
316 return capture_params_.resolution_change_policy;
317 }
318
319 VideoContentCaptureSourceSelectionResult
320 SelectVideoContentCaptureSourceSettings(
321 const blink::WebMediaConstraints& constraints) {
322 VideoContentCaptureCandidates candidates;
323 candidates.resolution_set =
324 ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
325 kMinScreenCastDimension, kMaxScreenCastDimension,
326 kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
327 candidates.frame_rate_set =
328 DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
329 // candidates.device_id_set and candidates.noise_reduction_set are
330 // automatically initialized with the universal set.
331
332 candidates = candidates.Intersection(
333 VideoContentCaptureCandidates(constraints.basic()));
334 if (candidates.IsEmpty())
335 return UnsatisfiedConstraintsResult(candidates, constraints.basic());
336
337 for (const auto& advanced_set : constraints.advanced()) {
338 VideoContentCaptureCandidates advanced_candidates(advanced_set);
339 VideoContentCaptureCandidates intersection =
340 candidates.Intersection(advanced_candidates);
341 if (!intersection.IsEmpty())
342 candidates = std::move(intersection);
343 }
344
345 DCHECK(!candidates.IsEmpty());
346 return SelectResultFromCandidates(candidates, constraints.basic());
347 }
348
349 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698