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

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: fix android/win_clang compilation issue 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 using StringSet = DiscreteSet<std::string>;
23 using BoolSet = DiscreteSet<bool>;
24
25 // Hard upper and lower bound frame rates for tab/desktop capture.
26 constexpr double kMaxScreenCastFrameRate = 120.0;
27 constexpr double kMinScreenCastFrameRate = 1.0 / 60.0;
28
29 constexpr double kDefaultFrameRate = MediaStreamVideoSource::kDefaultFrameRate;
30
31 constexpr int kMinScreenCastDimension = 1;
32 constexpr int kMaxScreenCastDimension = media::limits::kMaxDimension - 1;
33 constexpr double kMinScreenCastAspectRatio =
34 static_cast<double>(kMinScreenCastDimension) /
35 static_cast<double>(kMaxScreenCastDimension);
36 constexpr double kMaxScreenCastAspectRatio =
37 static_cast<double>(kMaxScreenCastDimension) /
38 static_cast<double>(kMinScreenCastDimension);
39
40 StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
41 if (!constraint.hasExact())
42 return StringSet::UniversalSet();
43
44 std::vector<std::string> elements;
45 for (const auto& entry : constraint.exact())
46 elements.push_back(entry.ascii());
47
48 return StringSet(std::move(elements));
49 }
50
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 ResolutionSet ScreenCastResolutionCapabilities() {
105 return ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
106 kMinScreenCastDimension, kMaxScreenCastDimension,
107 kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
108 }
109
110 // TODO(guidou): Update this policy to better match the way
111 // WebContentsCaptureMachine::ComputeOptimalViewSize() interprets
112 // resolution-change policies. See http://crbug.com/701302.
113 media::ResolutionChangePolicy SelectResolutionPolicyFromCandidates(
114 const ResolutionSet& resolution_set) {
115 ResolutionSet capabilities = ScreenCastResolutionCapabilities();
116 bool can_adjust_resolution =
117 resolution_set.min_height() <= capabilities.min_height() &&
118 resolution_set.max_height() >= capabilities.max_height() &&
119 resolution_set.min_width() <= capabilities.min_width() &&
120 resolution_set.max_width() >= capabilities.max_width() &&
121 resolution_set.min_aspect_ratio() <= capabilities.min_aspect_ratio() &&
122 resolution_set.max_aspect_ratio() >= capabilities.max_aspect_ratio();
123
124 return can_adjust_resolution ? media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
125 : media::RESOLUTION_POLICY_FIXED_RESOLUTION;
126 }
127
128 int RoundToInt(double d) {
129 return static_cast<int>(std::round(d));
130 }
131
132 gfx::Size ToGfxSize(const Point& point) {
133 return gfx::Size(RoundToInt(point.width()), RoundToInt(point.height()));
134 }
135
136 double SelectFrameRateFromCandidates(
137 const DoubleRangeSet& candidate_set,
138 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
139 double frame_rate = basic_constraint_set.frameRate.hasIdeal()
140 ? basic_constraint_set.frameRate.ideal()
141 : kDefaultFrameRate;
142 if (frame_rate > candidate_set.Max())
143 frame_rate = candidate_set.Max();
144 else if (frame_rate < candidate_set.Min())
145 frame_rate = candidate_set.Min();
146
147 return frame_rate;
148 }
149
150 media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
151 const VideoContentCaptureCandidates& candidates,
152 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
153 double requested_frame_rate = SelectFrameRateFromCandidates(
154 candidates.frame_rate_set, basic_constraint_set);
155 Point requested_resolution =
156 candidates.resolution_set.SelectClosestPointToIdeal(basic_constraint_set);
157 media::VideoCaptureParams params;
158 params.requested_format = media::VideoCaptureFormat(
159 ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
160 media::PIXEL_FORMAT_I420);
161 params.resolution_change_policy =
162 SelectResolutionPolicyFromCandidates(candidates.resolution_set);
163 // Content capture always uses default power-line frequency.
164 DCHECK(params.IsValid());
165
166 return params;
167 }
168
169 std::string SelectDeviceIDFromCandidates(
170 const StringSet& candidates,
171 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
172 DCHECK(!candidates.IsEmpty());
173 if (basic_constraint_set.deviceId.hasIdeal()) {
174 // If there are multiple elements specified by ideal, break ties by choosing
175 // the first one that satisfies the constraints.
176 for (const auto& ideal_entry : basic_constraint_set.deviceId.ideal()) {
177 std::string ideal_value = ideal_entry.ascii();
178 if (candidates.Contains(ideal_value)) {
179 return ideal_value;
180 }
181 }
182 }
183
184 // Return the empty string if nothing is specified in the constraints.
185 // The empty string is treated as a default device ID by the browser.
186 if (candidates.is_universal()) {
187 return std::string();
188 }
189
190 // If there are multiple elements that satisfy the constraints, break ties by
191 // using the element that was specified first.
192 return candidates.FirstElement();
193 }
194
195 rtc::Optional<bool> SelectNoiseReductionFromCandidates(
196 const BoolSet& candidates,
197 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
198 DCHECK(!candidates.IsEmpty());
199 if (basic_constraint_set.googNoiseReduction.hasIdeal() &&
200 candidates.Contains(basic_constraint_set.googNoiseReduction.ideal())) {
201 return rtc::Optional<bool>(basic_constraint_set.googNoiseReduction.ideal());
202 }
203
204 if (candidates.is_universal())
205 return rtc::Optional<bool>();
206
207 // A non-universal BoolSet can have at most one element.
208 return rtc::Optional<bool>(candidates.FirstElement());
209 }
210
211 VideoContentCaptureSourceSelectionResult SelectResultFromCandidates(
212 const VideoContentCaptureCandidates& candidates,
213 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
214 std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
215 basic_constraint_set);
216 media::VideoCaptureParams capture_params =
217 SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set);
218
219 rtc::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
220 candidates.noise_reduction_set, basic_constraint_set);
221
222 return VideoContentCaptureSourceSelectionResult(
223 std::move(device_id), noise_reduction, capture_params);
224 }
225
226 VideoContentCaptureSourceSelectionResult UnsatisfiedConstraintsResult(
227 const VideoContentCaptureCandidates& candidates,
228 const blink::WebMediaTrackConstraintSet& constraint_set) {
229 DCHECK(candidates.IsEmpty());
230 if (candidates.resolution_set.IsHeightEmpty()) {
231 return VideoContentCaptureSourceSelectionResult(
232 constraint_set.height.name());
233 } else if (candidates.resolution_set.IsWidthEmpty()) {
234 return VideoContentCaptureSourceSelectionResult(
235 constraint_set.width.name());
236 } else if (candidates.resolution_set.IsAspectRatioEmpty()) {
237 return VideoContentCaptureSourceSelectionResult(
238 constraint_set.aspectRatio.name());
239 } else if (candidates.frame_rate_set.IsEmpty()) {
240 return VideoContentCaptureSourceSelectionResult(
241 constraint_set.frameRate.name());
242 } else if (candidates.noise_reduction_set.IsEmpty()) {
243 return VideoContentCaptureSourceSelectionResult(
244 constraint_set.googNoiseReduction.name());
245 } else {
246 DCHECK(candidates.device_id_set.IsEmpty());
247 return VideoContentCaptureSourceSelectionResult(
248 constraint_set.deviceId.name());
249 }
250 }
251
252 } // namespace
253
254 VideoContentCaptureSourceSelectionResult::
255 VideoContentCaptureSourceSelectionResult(const char* failed_constraint_name)
256 : failed_constraint_name_(failed_constraint_name) {}
257
258 VideoContentCaptureSourceSelectionResult::
259 VideoContentCaptureSourceSelectionResult(
260 std::string device_id,
261 const rtc::Optional<bool>& noise_reduction,
262 media::VideoCaptureParams capture_params)
263 : failed_constraint_name_(nullptr),
264 device_id_(std::move(device_id)),
265 noise_reduction_(noise_reduction),
266 capture_params_(capture_params) {}
267
268 VideoContentCaptureSourceSelectionResult::
269 VideoContentCaptureSourceSelectionResult(
270 const VideoContentCaptureSourceSelectionResult& other) = default;
271 VideoContentCaptureSourceSelectionResult::
272 VideoContentCaptureSourceSelectionResult(
273 VideoContentCaptureSourceSelectionResult&& other) = default;
274 VideoContentCaptureSourceSelectionResult::
275 ~VideoContentCaptureSourceSelectionResult() = default;
276 VideoContentCaptureSourceSelectionResult&
277 VideoContentCaptureSourceSelectionResult::operator=(
278 const VideoContentCaptureSourceSelectionResult& other) = default;
279 VideoContentCaptureSourceSelectionResult&
280 VideoContentCaptureSourceSelectionResult::operator=(
281 VideoContentCaptureSourceSelectionResult&& other) = default;
282
283 int VideoContentCaptureSourceSelectionResult::Height() const {
284 DCHECK(HasValue());
285 return capture_params_.requested_format.frame_size.height();
286 }
287
288 int VideoContentCaptureSourceSelectionResult::Width() const {
289 DCHECK(HasValue());
290 return capture_params_.requested_format.frame_size.width();
291 }
292
293 float VideoContentCaptureSourceSelectionResult::FrameRate() const {
294 DCHECK(HasValue());
295 return capture_params_.requested_format.frame_rate;
296 }
297
298 media::ResolutionChangePolicy
299 VideoContentCaptureSourceSelectionResult::ResolutionChangePolicy() const {
300 DCHECK(HasValue());
301 return capture_params_.resolution_change_policy;
302 }
303
304 VideoContentCaptureSourceSelectionResult
305 SelectVideoContentCaptureSourceSettings(
306 const blink::WebMediaConstraints& constraints) {
307 VideoContentCaptureCandidates candidates;
308 candidates.resolution_set = ScreenCastResolutionCapabilities();
309 candidates.frame_rate_set =
310 DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate);
311 // candidates.device_id_set and candidates.noise_reduction_set are
312 // automatically initialized with the universal set.
313
314 candidates = candidates.Intersection(
315 VideoContentCaptureCandidates(constraints.basic()));
316 if (candidates.IsEmpty())
317 return UnsatisfiedConstraintsResult(candidates, constraints.basic());
318
319 for (const auto& advanced_set : constraints.advanced()) {
320 VideoContentCaptureCandidates advanced_candidates(advanced_set);
321 VideoContentCaptureCandidates intersection =
322 candidates.Intersection(advanced_candidates);
323 if (!intersection.IsEmpty())
324 candidates = std::move(intersection);
325 }
326
327 DCHECK(!candidates.IsEmpty());
328 return SelectResultFromCandidates(candidates, constraints.basic());
329 }
330
331 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698