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

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

Issue 2814063002: Update constraints algorithm for screen capture. (Closed)
Patch Set: reduce max dimension Created 3 years, 8 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
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/media_stream_constraints_util_video_content.h" 5 #include "content/renderer/media/media_stream_constraints_util_video_content.h"
6 6
7 #include <algorithm>
7 #include <cmath> 8 #include <cmath>
8 #include <utility> 9 #include <utility>
9 #include <vector> 10 #include <vector>
10 11
11 #include "content/renderer/media/media_stream_constraints_util_sets.h" 12 #include "content/renderer/media/media_stream_constraints_util_sets.h"
12 #include "content/renderer/media/media_stream_video_source.h" 13 #include "content/renderer/media/media_stream_video_source.h"
13 #include "media/base/limits.h" 14 #include "media/base/limits.h"
14 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" 15 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
15 #include "third_party/WebKit/public/platform/WebString.h" 16 #include "third_party/WebKit/public/platform/WebString.h"
16 17
17 namespace content { 18 namespace content {
18 19
19 const int kDefaultScreenCastWidth = 2880; 20 const int kDefaultScreenCastWidth = 2880;
20 const int kDefaultScreenCastHeight = 1800; 21 const int kDefaultScreenCastHeight = 1800;
21 const double kDefaultScreenCastAspectRatio = 22 const double kDefaultScreenCastAspectRatio =
22 static_cast<double>(kDefaultScreenCastWidth) / kDefaultScreenCastHeight; 23 static_cast<double>(kDefaultScreenCastWidth) / kDefaultScreenCastHeight;
23 const double kDefaultScreenCastFrameRate = 24 const double kDefaultScreenCastFrameRate =
24 MediaStreamVideoSource::kDefaultFrameRate; 25 MediaStreamVideoSource::kDefaultFrameRate;
25 const int kMinScreenCastDimension = 1; 26 const int kMinScreenCastDimension = 1;
26 const int kMaxScreenCastDimension = media::limits::kMaxDimension - 1; 27 // Use kMaxDimension/2 as maximum to ensure selected resolutions have area less
28 // than media::limits::kMaxCanvas.
Max Morin 2017/04/12 15:29:37 Should there be a static_assert that it's less the
Guido Urdaneta 2017/04/12 15:53:28 Done.
29 const int kMaxScreenCastDimension = media::limits::kMaxDimension / 2;
30 const double kMinScreenCastFrameRate = 1.0 / 60.0;
31 const double kMaxScreenCastFrameRate = 120.0;
27 32
28 namespace { 33 namespace {
29 34
30 using Point = ResolutionSet::Point; 35 using Point = ResolutionSet::Point;
31 using StringSet = DiscreteSet<std::string>; 36 using StringSet = DiscreteSet<std::string>;
32 using BoolSet = DiscreteSet<bool>; 37 using BoolSet = DiscreteSet<bool>;
33 38
34 // Hard upper and lower bound frame rates for tab/desktop capture.
35 const double kMaxScreenCastFrameRate = 120.0;
36 const double kMinScreenCastFrameRate = 1.0 / 60.0;
37 39
38 constexpr double kMinScreenCastAspectRatio = 40 constexpr double kMinScreenCastAspectRatio =
39 static_cast<double>(kMinScreenCastDimension) / 41 static_cast<double>(kMinScreenCastDimension) /
40 static_cast<double>(kMaxScreenCastDimension); 42 static_cast<double>(kMaxScreenCastDimension);
41 constexpr double kMaxScreenCastAspectRatio = 43 constexpr double kMaxScreenCastAspectRatio =
42 static_cast<double>(kMaxScreenCastDimension) / 44 static_cast<double>(kMaxScreenCastDimension) /
43 static_cast<double>(kMinScreenCastDimension); 45 static_cast<double>(kMinScreenCastDimension);
44 46
45 StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) { 47 StringSet StringSetFromConstraint(const blink::StringConstraint& constraint) {
46 if (!constraint.HasExact()) 48 if (!constraint.HasExact())
(...skipping 11 matching lines...) Expand all
58 return BoolSet::UniversalSet(); 60 return BoolSet::UniversalSet();
59 61
60 return BoolSet({constraint.Exact()}); 62 return BoolSet({constraint.Exact()});
61 } 63 }
62 64
63 using DoubleRangeSet = NumericRangeSet<double>; 65 using DoubleRangeSet = NumericRangeSet<double>;
64 66
65 class VideoContentCaptureCandidates { 67 class VideoContentCaptureCandidates {
66 public: 68 public:
67 VideoContentCaptureCandidates() 69 VideoContentCaptureCandidates()
68 : device_id_set(StringSet::UniversalSet()), 70 : has_explicit_max_height_(false),
69 noise_reduction_set(BoolSet::UniversalSet()) {} 71 has_explicit_max_width_(false),
72 has_explicit_max_frame_rate_(false),
73 device_id_set_(StringSet::UniversalSet()),
74 noise_reduction_set_(BoolSet::UniversalSet()) {}
70 explicit VideoContentCaptureCandidates( 75 explicit VideoContentCaptureCandidates(
71 const blink::WebMediaTrackConstraintSet& constraint_set) 76 const blink::WebMediaTrackConstraintSet& constraint_set)
72 : resolution_set(ResolutionSet::FromConstraintSet(constraint_set)), 77 : resolution_set_(ResolutionSet::FromConstraintSet(constraint_set)),
73 frame_rate_set( 78 has_explicit_max_height_(ConstraintHasMax(constraint_set.height) &&
79 ConstraintMax(constraint_set.height) <=
80 kMaxScreenCastDimension),
81 has_explicit_max_width_(ConstraintHasMax(constraint_set.width) &&
82 ConstraintMax(constraint_set.width) <=
83 kMaxScreenCastDimension),
84 frame_rate_set_(
74 DoubleRangeSet::FromConstraint(constraint_set.frame_rate)), 85 DoubleRangeSet::FromConstraint(constraint_set.frame_rate)),
75 device_id_set(StringSetFromConstraint(constraint_set.device_id)), 86 has_explicit_max_frame_rate_(
76 noise_reduction_set( 87 ConstraintHasMax(constraint_set.frame_rate) &&
88 ConstraintMax(constraint_set.frame_rate) <=
89 kMaxScreenCastFrameRate),
90 device_id_set_(StringSetFromConstraint(constraint_set.device_id)),
91 noise_reduction_set_(
77 BoolSetFromConstraint(constraint_set.goog_noise_reduction)) {} 92 BoolSetFromConstraint(constraint_set.goog_noise_reduction)) {}
78 93
79 VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) = 94 VideoContentCaptureCandidates(VideoContentCaptureCandidates&& other) =
80 default; 95 default;
81 VideoContentCaptureCandidates& operator=( 96 VideoContentCaptureCandidates& operator=(
82 VideoContentCaptureCandidates&& other) = default; 97 VideoContentCaptureCandidates&& other) = default;
83 98
84 bool IsEmpty() const { 99 bool IsEmpty() const {
85 return resolution_set.IsEmpty() || frame_rate_set.IsEmpty() || 100 return resolution_set_.IsEmpty() || frame_rate_set_.IsEmpty() ||
86 device_id_set.IsEmpty() || noise_reduction_set.IsEmpty(); 101 device_id_set_.IsEmpty() || noise_reduction_set_.IsEmpty();
87 } 102 }
88 103
89 VideoContentCaptureCandidates Intersection( 104 VideoContentCaptureCandidates Intersection(
90 const VideoContentCaptureCandidates& other) { 105 const VideoContentCaptureCandidates& other) {
91 VideoContentCaptureCandidates intersection; 106 VideoContentCaptureCandidates intersection;
92 intersection.resolution_set = 107 intersection.resolution_set_ =
93 resolution_set.Intersection(other.resolution_set); 108 resolution_set_.Intersection(other.resolution_set_);
94 intersection.frame_rate_set = 109 intersection.has_explicit_max_height_ =
95 frame_rate_set.Intersection(other.frame_rate_set); 110 has_explicit_max_height_ || other.has_explicit_max_height_;
96 intersection.device_id_set = 111 intersection.has_explicit_max_width_ =
97 device_id_set.Intersection(other.device_id_set); 112 has_explicit_max_width_ || other.has_explicit_max_width_;
98 intersection.noise_reduction_set = 113 intersection.frame_rate_set_ =
99 noise_reduction_set.Intersection(other.noise_reduction_set); 114 frame_rate_set_.Intersection(other.frame_rate_set_);
115 intersection.has_explicit_max_frame_rate_ =
116 has_explicit_max_frame_rate_ || other.has_explicit_max_frame_rate_;
117 intersection.device_id_set_ =
118 device_id_set_.Intersection(other.device_id_set_);
119 intersection.noise_reduction_set_ =
120 noise_reduction_set_.Intersection(other.noise_reduction_set_);
100 return intersection; 121 return intersection;
101 } 122 }
102 123
103 ResolutionSet resolution_set; 124 const ResolutionSet& resolution_set() const { return resolution_set_; }
104 DoubleRangeSet frame_rate_set; 125 bool has_explicit_max_height() const { return has_explicit_max_height_; }
105 StringSet device_id_set; 126 bool has_explicit_max_width() const { return has_explicit_max_width_; }
106 BoolSet noise_reduction_set; 127 const DoubleRangeSet& frame_rate_set() const { return frame_rate_set_; }
128 bool has_explicit_max_frame_rate() const {
129 return has_explicit_max_frame_rate_;
130 }
131 const StringSet& device_id_set() const { return device_id_set_; }
132 const BoolSet& noise_reduction_set() const { return noise_reduction_set_; }
133 void set_resolution_set(const ResolutionSet& set) { resolution_set_ = set; }
134 void set_frame_rate_set(const DoubleRangeSet& set) { frame_rate_set_ = set; }
135
136 private:
137 ResolutionSet resolution_set_;
138 bool has_explicit_max_height_;
139 bool has_explicit_max_width_;
140 DoubleRangeSet frame_rate_set_;
141 bool has_explicit_max_frame_rate_;
142 StringSet device_id_set_;
143 BoolSet noise_reduction_set_;
107 }; 144 };
108 145
109 ResolutionSet ScreenCastResolutionCapabilities() { 146 ResolutionSet ScreenCastResolutionCapabilities() {
110 return ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension, 147 return ResolutionSet(kMinScreenCastDimension, kMaxScreenCastDimension,
111 kMinScreenCastDimension, kMaxScreenCastDimension, 148 kMinScreenCastDimension, kMaxScreenCastDimension,
112 kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio); 149 kMinScreenCastAspectRatio, kMaxScreenCastAspectRatio);
113 } 150 }
114 151
115 // TODO(guidou): Update this policy to better match the way 152 // TODO(guidou): Update this policy to better match the way
116 // WebContentsCaptureMachine::ComputeOptimalViewSize() interprets 153 // WebContentsCaptureMachine::ComputeOptimalViewSize() interprets
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 return frame_rate; 203 return frame_rate;
167 } 204 }
168 205
169 media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates( 206 media::VideoCaptureParams SelectVideoCaptureParamsFromCandidates(
170 const VideoContentCaptureCandidates& candidates, 207 const VideoContentCaptureCandidates& candidates,
171 const blink::WebMediaTrackConstraintSet& basic_constraint_set, 208 const blink::WebMediaTrackConstraintSet& basic_constraint_set,
172 int default_height, 209 int default_height,
173 int default_width, 210 int default_width,
174 double default_frame_rate) { 211 double default_frame_rate) {
175 double requested_frame_rate = SelectFrameRateFromCandidates( 212 double requested_frame_rate = SelectFrameRateFromCandidates(
176 candidates.frame_rate_set, basic_constraint_set, default_frame_rate); 213 candidates.frame_rate_set(), basic_constraint_set, default_frame_rate);
177 Point requested_resolution = 214 Point requested_resolution =
178 candidates.resolution_set.SelectClosestPointToIdeal( 215 candidates.resolution_set().SelectClosestPointToIdeal(
179 basic_constraint_set, default_height, default_width); 216 basic_constraint_set, default_height, default_width);
180 media::VideoCaptureParams params; 217 media::VideoCaptureParams params;
181 params.requested_format = media::VideoCaptureFormat( 218 params.requested_format = media::VideoCaptureFormat(
182 ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate), 219 ToGfxSize(requested_resolution), static_cast<float>(requested_frame_rate),
183 media::PIXEL_FORMAT_I420); 220 media::PIXEL_FORMAT_I420);
184 params.resolution_change_policy = 221 params.resolution_change_policy =
185 SelectResolutionPolicyFromCandidates(candidates.resolution_set); 222 SelectResolutionPolicyFromCandidates(candidates.resolution_set());
186 // Content capture always uses default power-line frequency. 223 // Content capture always uses default power-line frequency.
187 DCHECK(params.IsValid()); 224 DCHECK(params.IsValid());
188 225
189 return params; 226 return params;
190 } 227 }
191 228
192 std::string SelectDeviceIDFromCandidates( 229 std::string SelectDeviceIDFromCandidates(
193 const StringSet& candidates, 230 const StringSet& candidates,
194 const blink::WebMediaTrackConstraintSet& basic_constraint_set) { 231 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
195 DCHECK(!candidates.IsEmpty()); 232 DCHECK(!candidates.IsEmpty());
(...skipping 29 matching lines...) Expand all
225 basic_constraint_set.goog_noise_reduction.Ideal()); 262 basic_constraint_set.goog_noise_reduction.Ideal());
226 } 263 }
227 264
228 if (candidates.is_universal()) 265 if (candidates.is_universal())
229 return base::Optional<bool>(); 266 return base::Optional<bool>();
230 267
231 // A non-universal BoolSet can have at most one element. 268 // A non-universal BoolSet can have at most one element.
232 return base::Optional<bool>(candidates.FirstElement()); 269 return base::Optional<bool>(candidates.FirstElement());
233 } 270 }
234 271
272 int ClampToValidDimension(int value) {
273 if (value > kMaxScreenCastDimension)
274 return kMaxScreenCastDimension;
275 else if (value < kMinScreenCastDimension)
276 return kMinScreenCastDimension;
277 return value;
278 }
279
235 VideoCaptureSettings SelectResultFromCandidates( 280 VideoCaptureSettings SelectResultFromCandidates(
236 const VideoContentCaptureCandidates& candidates, 281 const VideoContentCaptureCandidates& candidates,
237 const blink::WebMediaTrackConstraintSet& basic_constraint_set) { 282 const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
238 std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set, 283 std::string device_id = SelectDeviceIDFromCandidates(
239 basic_constraint_set); 284 candidates.device_id_set(), basic_constraint_set);
240 // If a maximum width or height is explicitly given, use them as default. 285 // If a maximum width or height is explicitly given, use them as default.
241 // If only one of them is given, use the default aspect ratio to determine the 286 // If only one of them is given, use the default aspect ratio to determine the
242 // other default value. 287 // other default value.
243 // TODO(guidou): Use native screen-capture resolution as default. 288 // TODO(guidou): Use native screen-capture resolution as default.
244 // http://crbug.com/257097 289 // http://crbug.com/257097
245 int default_height = kDefaultScreenCastHeight; 290 int default_height = kDefaultScreenCastHeight;
246 int default_width = kDefaultScreenCastWidth; 291 int default_width = kDefaultScreenCastWidth;
247 bool has_explicit_max_height = 292 if (candidates.has_explicit_max_height() &&
248 candidates.resolution_set.max_height() < kMaxScreenCastDimension; 293 candidates.has_explicit_max_width()) {
249 bool has_explicit_max_width = 294 default_height = candidates.resolution_set().max_height();
250 candidates.resolution_set.max_width() < kMaxScreenCastDimension; 295 default_width = candidates.resolution_set().max_width();
251 if (has_explicit_max_height && has_explicit_max_width) { 296 } else if (candidates.has_explicit_max_height()) {
252 default_height = candidates.resolution_set.max_height(); 297 default_height = candidates.resolution_set().max_height();
253 default_width = candidates.resolution_set.max_width();
254 } else if (has_explicit_max_height) {
255 default_height = candidates.resolution_set.max_height();
256 default_width = static_cast<int>( 298 default_width = static_cast<int>(
257 std::round(default_height * kDefaultScreenCastAspectRatio)); 299 std::round(default_height * kDefaultScreenCastAspectRatio));
258 } else if (has_explicit_max_width) { 300 } else if (candidates.has_explicit_max_width()) {
259 default_width = candidates.resolution_set.max_width(); 301 default_width = candidates.resolution_set().max_width();
260 default_height = static_cast<int>( 302 default_height = static_cast<int>(
261 std::round(default_width / kDefaultScreenCastAspectRatio)); 303 std::round(default_width / kDefaultScreenCastAspectRatio));
262 } 304 }
305 // When the given maximum values are large, the computed values using default
306 // aspect ratio may fall out of range. Ensure the defaults are in the valid
307 // range.
308 default_height = ClampToValidDimension(default_height);
309 default_width = ClampToValidDimension(default_width);
310
311 // If a maximum frame rate is explicitly given, use it as default for
312 // better compatibility with the old constraints algorithm.
313 // TODO(guidou): Use the actual default when applications migrate to the new
314 // constraint syntax. http://crbug.com/710800
315 double default_frame_rate = candidates.has_explicit_max_frame_rate()
316 ? candidates.frame_rate_set().Max()
317 : kDefaultScreenCastFrameRate;
263 media::VideoCaptureParams capture_params = 318 media::VideoCaptureParams capture_params =
264 SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set, 319 SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set,
265 default_height, default_width, 320 default_height, default_width,
266 kDefaultScreenCastFrameRate); 321 default_frame_rate);
267 322
268 base::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates( 323 base::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
269 candidates.noise_reduction_set, basic_constraint_set); 324 candidates.noise_reduction_set(), basic_constraint_set);
270 325
271 auto track_adapter_settings = SelectVideoTrackAdapterSettings( 326 auto track_adapter_settings = SelectVideoTrackAdapterSettings(
272 basic_constraint_set, candidates.resolution_set, 327 basic_constraint_set, candidates.resolution_set(),
273 candidates.frame_rate_set, capture_params.requested_format); 328 candidates.frame_rate_set(), capture_params.requested_format);
274 329
275 return VideoCaptureSettings(std::move(device_id), capture_params, 330 return VideoCaptureSettings(std::move(device_id), capture_params,
276 noise_reduction, track_adapter_settings, 331 noise_reduction, track_adapter_settings,
277 candidates.frame_rate_set.Min()); 332 candidates.frame_rate_set().Min());
278 } 333 }
279 334
280 VideoCaptureSettings UnsatisfiedConstraintsResult( 335 VideoCaptureSettings UnsatisfiedConstraintsResult(
281 const VideoContentCaptureCandidates& candidates, 336 const VideoContentCaptureCandidates& candidates,
282 const blink::WebMediaTrackConstraintSet& constraint_set) { 337 const blink::WebMediaTrackConstraintSet& constraint_set) {
283 DCHECK(candidates.IsEmpty()); 338 DCHECK(candidates.IsEmpty());
284 if (candidates.resolution_set.IsHeightEmpty()) { 339 if (candidates.resolution_set().IsHeightEmpty()) {
285 return VideoCaptureSettings(constraint_set.height.GetName()); 340 return VideoCaptureSettings(constraint_set.height.GetName());
286 } else if (candidates.resolution_set.IsWidthEmpty()) { 341 } else if (candidates.resolution_set().IsWidthEmpty()) {
287 return VideoCaptureSettings(constraint_set.width.GetName()); 342 return VideoCaptureSettings(constraint_set.width.GetName());
288 } else if (candidates.resolution_set.IsAspectRatioEmpty()) { 343 } else if (candidates.resolution_set().IsAspectRatioEmpty()) {
289 return VideoCaptureSettings(constraint_set.aspect_ratio.GetName()); 344 return VideoCaptureSettings(constraint_set.aspect_ratio.GetName());
290 } else if (candidates.frame_rate_set.IsEmpty()) { 345 } else if (candidates.frame_rate_set().IsEmpty()) {
291 return VideoCaptureSettings(constraint_set.frame_rate.GetName()); 346 return VideoCaptureSettings(constraint_set.frame_rate.GetName());
292 } else if (candidates.noise_reduction_set.IsEmpty()) { 347 } else if (candidates.noise_reduction_set().IsEmpty()) {
293 return VideoCaptureSettings(constraint_set.goog_noise_reduction.GetName()); 348 return VideoCaptureSettings(constraint_set.goog_noise_reduction.GetName());
294 } else { 349 } else {
295 DCHECK(candidates.device_id_set.IsEmpty()); 350 DCHECK(candidates.device_id_set().IsEmpty());
296 return VideoCaptureSettings(constraint_set.device_id.GetName()); 351 return VideoCaptureSettings(constraint_set.device_id.GetName());
297 } 352 }
298 } 353 }
299 354
300 } // namespace 355 } // namespace
301 356
302 VideoCaptureSettings SelectSettingsVideoContentCapture( 357 VideoCaptureSettings SelectSettingsVideoContentCapture(
303 const blink::WebMediaConstraints& constraints) { 358 const blink::WebMediaConstraints& constraints) {
304 VideoContentCaptureCandidates candidates; 359 VideoContentCaptureCandidates candidates;
305 candidates.resolution_set = ScreenCastResolutionCapabilities(); 360 candidates.set_resolution_set(ScreenCastResolutionCapabilities());
306 candidates.frame_rate_set = 361 candidates.set_frame_rate_set(
307 DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate); 362 DoubleRangeSet(kMinScreenCastFrameRate, kMaxScreenCastFrameRate));
308 // candidates.device_id_set and candidates.noise_reduction_set are 363 // candidates.device_id_set and candidates.noise_reduction_set are
309 // automatically initialized with the universal set. 364 // automatically initialized with the universal set.
310 365
311 candidates = candidates.Intersection( 366 candidates = candidates.Intersection(
312 VideoContentCaptureCandidates(constraints.Basic())); 367 VideoContentCaptureCandidates(constraints.Basic()));
313 if (candidates.IsEmpty()) 368 if (candidates.IsEmpty())
314 return UnsatisfiedConstraintsResult(candidates, constraints.Basic()); 369 return UnsatisfiedConstraintsResult(candidates, constraints.Basic());
315 370
316 for (const auto& advanced_set : constraints.Advanced()) { 371 for (const auto& advanced_set : constraints.Advanced()) {
317 VideoContentCaptureCandidates advanced_candidates(advanced_set); 372 VideoContentCaptureCandidates advanced_candidates(advanced_set);
318 VideoContentCaptureCandidates intersection = 373 VideoContentCaptureCandidates intersection =
319 candidates.Intersection(advanced_candidates); 374 candidates.Intersection(advanced_candidates);
320 if (!intersection.IsEmpty()) 375 if (!intersection.IsEmpty())
321 candidates = std::move(intersection); 376 candidates = std::move(intersection);
322 } 377 }
323 378
324 DCHECK(!candidates.IsEmpty()); 379 DCHECK(!candidates.IsEmpty());
325 return SelectResultFromCandidates(candidates, constraints.Basic()); 380 return SelectResultFromCandidates(candidates, constraints.Basic());
326 } 381 }
327 382
328 } // namespace content 383 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698