Home | History | Annotate | Download | only in media
      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
    628