Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / media_stream_video_source.cc
1 // Copyright 2014 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_video_source.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10
11 #include "base/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "content/child/child_process.h"
15 #include "content/renderer/media/media_stream_constraints_util.h"
16 #include "content/renderer/media/media_stream_video_track.h"
17 #include "content/renderer/media/video_track_adapter.h"
18
19 namespace content {
20
21 // Constraint keys. Specified by draft-alvestrand-constraints-resolution-00b
22 const char MediaStreamVideoSource::kMinAspectRatio[] = "minAspectRatio";
23 const char MediaStreamVideoSource::kMaxAspectRatio[] = "maxAspectRatio";
24 const char MediaStreamVideoSource::kMaxWidth[] = "maxWidth";
25 const char MediaStreamVideoSource::kMinWidth[] = "minWidth";
26 const char MediaStreamVideoSource::kMaxHeight[] = "maxHeight";
27 const char MediaStreamVideoSource::kMinHeight[] = "minHeight";
28 const char MediaStreamVideoSource::kMaxFrameRate[] = "maxFrameRate";
29 const char MediaStreamVideoSource::kMinFrameRate[] = "minFrameRate";
30
31 const char* kSupportedConstraints[] = {
32   MediaStreamVideoSource::kMaxAspectRatio,
33   MediaStreamVideoSource::kMinAspectRatio,
34   MediaStreamVideoSource::kMaxWidth,
35   MediaStreamVideoSource::kMinWidth,
36   MediaStreamVideoSource::kMaxHeight,
37   MediaStreamVideoSource::kMinHeight,
38   MediaStreamVideoSource::kMaxFrameRate,
39   MediaStreamVideoSource::kMinFrameRate,
40 };
41
42 const int MediaStreamVideoSource::kDefaultWidth = 640;
43 const int MediaStreamVideoSource::kDefaultHeight = 480;
44 const int MediaStreamVideoSource::kDefaultFrameRate = 30;
45 const int MediaStreamVideoSource::kUnknownFrameRate = 0;
46
47 namespace {
48
49 // Google-specific key prefix. Constraints with this prefix are ignored if they
50 // are unknown.
51 const char kGooglePrefix[] = "goog";
52
53 // Returns true if |constraint| has mandatory constraints.
54 bool HasMandatoryConstraints(const blink::WebMediaConstraints& constraints) {
55   blink::WebVector<blink::WebMediaConstraint> mandatory_constraints;
56   constraints.getMandatoryConstraints(mandatory_constraints);
57   return !mandatory_constraints.isEmpty();
58 }
59
60 // Retrieve the desired max width and height from |constraints|. If not set,
61 // the |desired_width| and |desired_height| are set to
62 // std::numeric_limits<int>::max();
63 // If either max width or height is set as a mandatory constraint, the optional
64 // constraints are not checked.
65 void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints,
66                                  int* desired_width, int* desired_height) {
67   *desired_width = std::numeric_limits<int>::max();
68   *desired_height = std::numeric_limits<int>::max();
69
70   bool mandatory = GetMandatoryConstraintValueAsInteger(
71       constraints,
72       MediaStreamVideoSource::kMaxWidth,
73       desired_width);
74   mandatory |= GetMandatoryConstraintValueAsInteger(
75       constraints,
76       MediaStreamVideoSource::kMaxHeight,
77       desired_height);
78   if (mandatory)
79     return;
80
81   GetOptionalConstraintValueAsInteger(constraints,
82                                       MediaStreamVideoSource::kMaxWidth,
83                                       desired_width);
84   GetOptionalConstraintValueAsInteger(constraints,
85                                       MediaStreamVideoSource::kMaxHeight,
86                                       desired_height);
87 }
88
89 // Retrieve the desired max and min aspect ratio from |constraints|. If not set,
90 // the |min_aspect_ratio| is set to 0 and |max_aspect_ratio| is set to
91 // std::numeric_limits<double>::max();
92 // If either min or max aspect ratio is set as a mandatory constraint, the
93 // optional constraints are not checked.
94 void GetDesiredMinAndMaxAspectRatio(
95     const blink::WebMediaConstraints& constraints,
96     double* min_aspect_ratio,
97     double* max_aspect_ratio) {
98   *min_aspect_ratio = 0;
99   *max_aspect_ratio = std::numeric_limits<double>::max();
100
101   bool mandatory = GetMandatoryConstraintValueAsDouble(
102       constraints,
103       MediaStreamVideoSource::kMinAspectRatio,
104       min_aspect_ratio);
105   mandatory |= GetMandatoryConstraintValueAsDouble(
106       constraints,
107       MediaStreamVideoSource::kMaxAspectRatio,
108       max_aspect_ratio);
109   if (mandatory)
110     return;
111
112   GetOptionalConstraintValueAsDouble(
113       constraints,
114       MediaStreamVideoSource::kMinAspectRatio,
115       min_aspect_ratio);
116   GetOptionalConstraintValueAsDouble(
117       constraints,
118       MediaStreamVideoSource::kMaxAspectRatio,
119       max_aspect_ratio);
120 }
121
122 // Returns true if |constraint| is fulfilled. |format| can be changed by a
123 // constraint, e.g. the frame rate can be changed by setting maxFrameRate.
124 bool UpdateFormatForConstraint(
125     const blink::WebMediaConstraint& constraint,
126     bool mandatory,
127     media::VideoCaptureFormat* format) {
128   DCHECK(format != NULL);
129
130   if (!format->IsValid())
131     return false;
132
133   std::string constraint_name = constraint.m_name.utf8();
134   std::string constraint_value = constraint.m_value.utf8();
135
136   if (constraint_name.find(kGooglePrefix) == 0) {
137     // These are actually options, not constraints, so they can be satisfied
138     // regardless of the format.
139     return true;
140   }
141
142   if (constraint_name == MediaStreamSource::kSourceId) {
143     // This is a constraint that doesn't affect the format.
144     return true;
145   }
146
147   // Ignore Chrome specific Tab capture constraints.
148   if (constraint_name == kMediaStreamSource ||
149       constraint_name == kMediaStreamSourceId)
150     return true;
151
152   if (constraint_name == MediaStreamVideoSource::kMinAspectRatio ||
153       constraint_name == MediaStreamVideoSource::kMaxAspectRatio) {
154     // These constraints are handled by cropping if the camera outputs the wrong
155     // aspect ratio.
156     double value;
157     return base::StringToDouble(constraint_value, &value);
158   }
159
160   double value = 0.0;
161   if (!base::StringToDouble(constraint_value, &value)) {
162     DLOG(WARNING) << "Can't parse MediaStream constraint. Name:"
163                   <<  constraint_name << " Value:" << constraint_value;
164     return false;
165   }
166
167   if (constraint_name == MediaStreamVideoSource::kMinWidth) {
168     return (value <= format->frame_size.width());
169   } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) {
170     return value > 0.0;
171   } else if (constraint_name == MediaStreamVideoSource::kMinHeight) {
172     return (value <= format->frame_size.height());
173   } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) {
174      return value > 0.0;
175   } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) {
176     return (value > 0.0) && (value <= format->frame_rate);
177   } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) {
178     if (value <= 0.0) {
179       // The frame rate is set by constraint.
180       // Don't allow 0 as frame rate if it is a mandatory constraint.
181       // Set the frame rate to 1 if it is not mandatory.
182       if (mandatory) {
183         return false;
184       } else {
185         value = 1.0;
186       }
187     }
188     format->frame_rate =
189         (format->frame_rate > value) ? value : format->frame_rate;
190     return true;
191   } else {
192     LOG(WARNING) << "Found unknown MediaStream constraint. Name:"
193                  <<  constraint_name << " Value:" << constraint_value;
194     return false;
195   }
196 }
197
198 // Removes media::VideoCaptureFormats from |formats| that don't meet
199 // |constraint|.
200 void FilterFormatsByConstraint(
201     const blink::WebMediaConstraint& constraint,
202     bool mandatory,
203     media::VideoCaptureFormats* formats) {
204   DVLOG(3) << "FilterFormatsByConstraint("
205            << "{ constraint.m_name = " << constraint.m_name.utf8()
206            << "  constraint.m_value = " << constraint.m_value.utf8()
207            << "  mandatory =  " << mandatory << "})";
208   media::VideoCaptureFormats::iterator format_it = formats->begin();
209   while (format_it != formats->end()) {
210     // Modify the format_it to fulfill the constraint if possible.
211     // Delete it otherwise.
212     if (!UpdateFormatForConstraint(constraint, mandatory, &(*format_it))) {
213       format_it = formats->erase(format_it);
214     } else {
215       ++format_it;
216     }
217   }
218 }
219
220 // Returns the media::VideoCaptureFormats that matches |constraints|.
221 media::VideoCaptureFormats FilterFormats(
222     const blink::WebMediaConstraints& constraints,
223     const media::VideoCaptureFormats& supported_formats,
224     blink::WebString* unsatisfied_constraint) {
225   if (constraints.isNull()) {
226     return supported_formats;
227   }
228
229   double max_aspect_ratio;
230   double min_aspect_ratio;
231   GetDesiredMinAndMaxAspectRatio(constraints,
232                                  &min_aspect_ratio,
233                                  &max_aspect_ratio);
234
235   if (min_aspect_ratio > max_aspect_ratio || max_aspect_ratio < 0.05f) {
236     DLOG(WARNING) << "Wrong requested aspect ratio.";
237     return media::VideoCaptureFormats();
238   }
239
240   int min_width = 0;
241   GetMandatoryConstraintValueAsInteger(constraints,
242                                        MediaStreamVideoSource::kMinWidth,
243                                        &min_width);
244   int min_height = 0;
245   GetMandatoryConstraintValueAsInteger(constraints,
246                                        MediaStreamVideoSource::kMinHeight,
247                                        &min_height);
248   int max_width;
249   int max_height;
250   GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height);
251
252   if (min_width > max_width || min_height > max_height)
253     return media::VideoCaptureFormats();
254
255   double min_frame_rate = 0.0f;
256   double max_frame_rate = 0.0f;
257   if (GetConstraintValueAsDouble(constraints,
258                                  MediaStreamVideoSource::kMaxFrameRate,
259                                  &max_frame_rate) &&
260       GetConstraintValueAsDouble(constraints,
261                                  MediaStreamVideoSource::kMinFrameRate,
262                                  &min_frame_rate)) {
263     if (min_frame_rate > max_frame_rate) {
264       DLOG(WARNING) << "Wrong requested frame rate.";
265       return media::VideoCaptureFormats();
266     }
267   }
268
269   blink::WebVector<blink::WebMediaConstraint> mandatory;
270   blink::WebVector<blink::WebMediaConstraint> optional;
271   constraints.getMandatoryConstraints(mandatory);
272   constraints.getOptionalConstraints(optional);
273   media::VideoCaptureFormats candidates = supported_formats;
274   for (size_t i = 0; i < mandatory.size(); ++i) {
275     FilterFormatsByConstraint(mandatory[i], true, &candidates);
276     if (candidates.empty()) {
277       *unsatisfied_constraint = mandatory[i].m_name;
278       return candidates;
279     }
280   }
281
282   if (candidates.empty())
283     return candidates;
284
285   // Ok - all mandatory checked and we still have candidates.
286   // Let's try filtering using the optional constraints. The optional
287   // constraints must be filtered in the order they occur in |optional|.
288   // But if a constraint produce zero candidates, the constraint is ignored and
289   // the next constraint is tested.
290   // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-Constraints
291   for (size_t i = 0; i < optional.size(); ++i) {
292     media::VideoCaptureFormats current_candidates = candidates;
293     FilterFormatsByConstraint(optional[i], false, &current_candidates);
294     if (!current_candidates.empty()) {
295       candidates = current_candidates;
296     }
297   }
298
299   // We have done as good as we can to filter the supported resolutions.
300   return candidates;
301 }
302
303 const media::VideoCaptureFormat& GetBestFormatBasedOnArea(
304     const media::VideoCaptureFormats& formats,
305     int area) {
306   media::VideoCaptureFormats::const_iterator it = formats.begin();
307   media::VideoCaptureFormats::const_iterator best_it = formats.begin();
308   int best_diff = std::numeric_limits<int>::max();
309   for (; it != formats.end(); ++it) {
310     int diff = abs(area - it->frame_size.width() * it->frame_size.height());
311     if (diff < best_diff) {
312       best_diff = diff;
313       best_it = it;
314     }
315   }
316   return *best_it;
317 }
318
319 // Find the format that best matches the default video size.
320 // This algorithm is chosen since a resolution must be picked even if no
321 // constraints are provided. We don't just select the maximum supported
322 // resolution since higher resolutions cost more in terms of complexity and
323 // many cameras have lower frame rate and have more noise in the image at
324 // their maximum supported resolution.
325 void GetBestCaptureFormat(
326     const media::VideoCaptureFormats& formats,
327     const blink::WebMediaConstraints& constraints,
328     media::VideoCaptureFormat* capture_format) {
329   DCHECK(!formats.empty());
330
331   int max_width;
332   int max_height;
333   GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height);
334
335   *capture_format = GetBestFormatBasedOnArea(
336       formats,
337       std::min(max_width, MediaStreamVideoSource::kDefaultWidth) *
338       std::min(max_height, MediaStreamVideoSource::kDefaultHeight));
339 }
340
341 }  // anonymous namespace
342
343 // static
344 MediaStreamVideoSource* MediaStreamVideoSource::GetVideoSource(
345     const blink::WebMediaStreamSource& source) {
346   return static_cast<MediaStreamVideoSource*>(source.extraData());
347 }
348
349 // static
350 bool MediaStreamVideoSource::IsConstraintSupported(const std::string& name) {
351   for (size_t i = 0; i < arraysize(kSupportedConstraints); ++i) {
352     if (kSupportedConstraints[i] == name)
353       return true;
354   }
355   return false;
356 }
357
358 MediaStreamVideoSource::MediaStreamVideoSource()
359     : state_(NEW),
360       track_adapter_(new VideoTrackAdapter(
361           ChildProcess::current()->io_message_loop_proxy())),
362       weak_factory_(this) {
363 }
364
365 MediaStreamVideoSource::~MediaStreamVideoSource() {
366   DCHECK(CalledOnValidThread());
367 }
368
369 void MediaStreamVideoSource::AddTrack(
370     MediaStreamVideoTrack* track,
371     const VideoCaptureDeliverFrameCB& frame_callback,
372     const blink::WebMediaConstraints& constraints,
373     const ConstraintsCallback& callback) {
374   DCHECK(CalledOnValidThread());
375   DCHECK(!constraints.isNull());
376   DCHECK(std::find(tracks_.begin(), tracks_.end(),
377                    track) == tracks_.end());
378   tracks_.push_back(track);
379
380   requested_constraints_.push_back(
381       RequestedConstraints(track, frame_callback, constraints, callback));
382
383   switch (state_) {
384     case NEW: {
385       // Tab capture and Screen capture needs the maximum requested height
386       // and width to decide on the resolution.
387       int max_requested_width = 0;
388       GetMandatoryConstraintValueAsInteger(constraints, kMaxWidth,
389                                            &max_requested_width);
390
391       int max_requested_height = 0;
392       GetMandatoryConstraintValueAsInteger(constraints, kMaxHeight,
393                                            &max_requested_height);
394
395       double max_requested_frame_rate;
396       if (!GetConstraintValueAsDouble(constraints, kMaxFrameRate,
397                                       &max_requested_frame_rate)) {
398         max_requested_frame_rate = kDefaultFrameRate;
399       }
400
401       state_ = RETRIEVING_CAPABILITIES;
402       GetCurrentSupportedFormats(
403           max_requested_width,
404           max_requested_height,
405           max_requested_frame_rate,
406           base::Bind(&MediaStreamVideoSource::OnSupportedFormats,
407                      weak_factory_.GetWeakPtr()));
408
409       break;
410     }
411     case STARTING:
412     case RETRIEVING_CAPABILITIES: {
413       // The |callback| will be triggered once the source has started or
414       // the capabilities have been retrieved.
415       break;
416     }
417     case ENDED:
418     case STARTED: {
419       // Currently, reconfiguring the source is not supported.
420       FinalizeAddTrack();
421     }
422   }
423 }
424
425 void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
426   DCHECK(CalledOnValidThread());
427   std::vector<MediaStreamVideoTrack*>::iterator it =
428       std::find(tracks_.begin(), tracks_.end(), video_track);
429   DCHECK(it != tracks_.end());
430   tracks_.erase(it);
431
432   // Check if |video_track| is waiting for applying new constraints and remove
433   // the request in that case.
434   for (std::vector<RequestedConstraints>::iterator it =
435            requested_constraints_.begin();
436        it != requested_constraints_.end(); ++it) {
437     if (it->track == video_track) {
438       requested_constraints_.erase(it);
439       break;
440     }
441   }
442   // Call |frame_adapter_->RemoveTrack| here even if adding the track has
443   // failed and |frame_adapter_->AddCallback| has not been called.
444   track_adapter_->RemoveTrack(video_track);
445
446   if (tracks_.empty())
447     StopSource();
448 }
449
450 const scoped_refptr<base::MessageLoopProxy>&
451 MediaStreamVideoSource::io_message_loop() const {
452   DCHECK(CalledOnValidThread());
453   return track_adapter_->io_message_loop();
454 }
455
456 void MediaStreamVideoSource::DoStopSource() {
457   DCHECK(CalledOnValidThread());
458   DVLOG(3) << "DoStopSource()";
459   if (state_ == ENDED)
460     return;
461   track_adapter_->StopFrameMonitoring();
462   StopSourceImpl();
463   state_ = ENDED;
464   SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded);
465 }
466
467 void MediaStreamVideoSource::OnSupportedFormats(
468     const media::VideoCaptureFormats& formats) {
469   DCHECK(CalledOnValidThread());
470   DCHECK_EQ(RETRIEVING_CAPABILITIES, state_);
471
472   supported_formats_ = formats;
473   if (!FindBestFormatWithConstraints(supported_formats_,
474                                      &current_format_)) {
475     SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded);
476     // This object can be deleted after calling FinalizeAddTrack. See comment
477     // in the header file.
478     FinalizeAddTrack();
479     return;
480   }
481
482   state_ = STARTING;
483   DVLOG(3) << "Starting the capturer with " << current_format_.ToString();
484
485   StartSourceImpl(
486       current_format_,
487       base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_));
488 }
489
490 bool MediaStreamVideoSource::FindBestFormatWithConstraints(
491     const media::VideoCaptureFormats& formats,
492     media::VideoCaptureFormat* best_format) {
493   DCHECK(CalledOnValidThread());
494   // Find the first constraints that we can fulfill.
495   for (std::vector<RequestedConstraints>::iterator request_it =
496            requested_constraints_.begin();
497        request_it != requested_constraints_.end(); ++request_it) {
498     const blink::WebMediaConstraints& requested_constraints =
499         request_it->constraints;
500
501     // If the source doesn't support capability enumeration it is still ok if
502     // no mandatory constraints have been specified. That just means that
503     // we will start with whatever format is native to the source.
504     if (formats.empty() && !HasMandatoryConstraints(requested_constraints)) {
505       *best_format = media::VideoCaptureFormat();
506       return true;
507     }
508     blink::WebString unsatisfied_constraint;
509     media::VideoCaptureFormats filtered_formats =
510         FilterFormats(requested_constraints, formats, &unsatisfied_constraint);
511     if (filtered_formats.size() > 0) {
512       // A request with constraints that can be fulfilled.
513       GetBestCaptureFormat(filtered_formats,
514                            requested_constraints,
515                            best_format);
516       return true;
517     }
518   }
519   return false;
520 }
521
522 void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) {
523   DCHECK(CalledOnValidThread());
524   DVLOG(3) << "OnStartDone({result =" << result << "})";
525   if (result == MEDIA_DEVICE_OK) {
526     DCHECK_EQ(STARTING, state_);
527     state_ = STARTED;
528     SetReadyState(blink::WebMediaStreamSource::ReadyStateLive);
529
530     track_adapter_->StartFrameMonitoring(
531         current_format_.frame_rate,
532         base::Bind(&MediaStreamVideoSource::SetMutedState,
533                    weak_factory_.GetWeakPtr()));
534
535   } else {
536     StopSource();
537   }
538
539   // This object can be deleted after calling FinalizeAddTrack. See comment in
540   // the header file.
541   FinalizeAddTrack();
542 }
543
544 void MediaStreamVideoSource::FinalizeAddTrack() {
545   DCHECK(CalledOnValidThread());
546   media::VideoCaptureFormats formats;
547   formats.push_back(current_format_);
548
549   std::vector<RequestedConstraints> callbacks;
550   callbacks.swap(requested_constraints_);
551   for (std::vector<RequestedConstraints>::iterator it = callbacks.begin();
552        it != callbacks.end(); ++it) {
553     MediaStreamRequestResult result = MEDIA_DEVICE_OK;
554     blink::WebString unsatisfied_constraint;
555
556     if (HasMandatoryConstraints(it->constraints) &&
557         FilterFormats(it->constraints, formats,
558                       &unsatisfied_constraint).empty())
559       result = MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
560
561     if (state_ != STARTED && result == MEDIA_DEVICE_OK)
562       result = MEDIA_DEVICE_TRACK_START_FAILURE;
563
564     if (result == MEDIA_DEVICE_OK) {
565       int max_width;
566       int max_height;
567       GetDesiredMaxWidthAndHeight(it->constraints, &max_width, &max_height);
568       double max_aspect_ratio;
569       double min_aspect_ratio;
570       GetDesiredMinAndMaxAspectRatio(it->constraints,
571                                      &min_aspect_ratio,
572                                      &max_aspect_ratio);
573       double max_frame_rate = 0.0f;
574       GetConstraintValueAsDouble(it->constraints,
575                                  kMaxFrameRate, &max_frame_rate);
576
577       track_adapter_->AddTrack(it->track, it->frame_callback,
578                                max_width, max_height,
579                                min_aspect_ratio, max_aspect_ratio,
580                                max_frame_rate);
581     }
582
583     DVLOG(3) << "FinalizeAddTrack() result " << result;
584
585     if (!it->callback.is_null()) {
586       it->callback.Run(this, result, unsatisfied_constraint);
587     }
588   }
589 }
590
591 void MediaStreamVideoSource::SetReadyState(
592     blink::WebMediaStreamSource::ReadyState state) {
593   DVLOG(3) << "MediaStreamVideoSource::SetReadyState state " << state;
594   DCHECK(CalledOnValidThread());
595   if (!owner().isNull())
596     owner().setReadyState(state);
597   for (std::vector<MediaStreamVideoTrack*>::iterator it = tracks_.begin();
598        it != tracks_.end(); ++it) {
599     (*it)->OnReadyStateChanged(state);
600   }
601 }
602
603 void MediaStreamVideoSource::SetMutedState(bool muted_state) {
604   DVLOG(3) << "MediaStreamVideoSource::SetMutedState state=" << muted_state;
605   DCHECK(CalledOnValidThread());
606   if (!owner().isNull()) {
607     owner().setReadyState(muted_state
608         ? blink::WebMediaStreamSource::ReadyStateMuted
609         : blink::WebMediaStreamSource::ReadyStateLive);
610   }
611 }
612
613 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints(
614     MediaStreamVideoTrack* track,
615     const VideoCaptureDeliverFrameCB& frame_callback,
616     const blink::WebMediaConstraints& constraints,
617     const ConstraintsCallback& callback)
618     : track(track),
619       frame_callback(frame_callback),
620       constraints(constraints),
621       callback(callback) {
622 }
623
624 MediaStreamVideoSource::RequestedConstraints::~RequestedConstraints() {
625 }
626
627 }  // namespace content