Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / media_stream_video_source.cc
index 2db0ef0..23e6e54 100644 (file)
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/media_stream_dependency_factory.h"
+#include "content/renderer/media/media_stream_constraints_util.h"
 #include "content/renderer/media/media_stream_video_track.h"
-#include "content/renderer/media/video_frame_deliverer.h"
-#include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h"
+#include "content/renderer/media/video_track_adapter.h"
+#include "media/base/bind_to_current_loop.h"
 
 namespace content {
 
@@ -45,21 +45,82 @@ const int MediaStreamVideoSource::kDefaultHeight = 480;
 const int MediaStreamVideoSource::kDefaultFrameRate = 30;
 
 namespace {
-// Constraints keys for http://dev.w3.org/2011/webrtc/editor/getusermedia.html
-const char kSourceId[] = "sourceId";
 
 // Google-specific key prefix. Constraints with this prefix are ignored if they
 // are unknown.
 const char kGooglePrefix[] = "goog";
 
-// MediaStreamVideoSource supports cropping of video frames but only up to
-// kMaxCropFactor. Ie - if a constraint is set to maxHeight 360, an original
-// input frame height of max 360 * kMaxCropFactor pixels is accepted.
-const int kMaxCropFactor = 2;
+// Returns true if |constraint| has mandatory constraints.
+bool HasMandatoryConstraints(const blink::WebMediaConstraints& constraints) {
+  blink::WebVector<blink::WebMediaConstraint> mandatory_constraints;
+  constraints.getMandatoryConstraints(mandatory_constraints);
+  return !mandatory_constraints.isEmpty();
+}
 
-// Returns true if |constraint| is fulfilled. |format| can be changed
-// changed by a constraint. Ie - the frame rate can be changed by setting
-// maxFrameRate.
+// Retrieve the desired max width and height from |constraints|. If not set,
+// the |desired_width| and |desired_height| are set to
+// std::numeric_limits<int>::max();
+// If either max width or height is set as a mandatory constraint, the optional
+// constraints are not checked.
+void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints,
+                                 int* desired_width, int* desired_height) {
+  *desired_width = std::numeric_limits<int>::max();
+  *desired_height = std::numeric_limits<int>::max();
+
+  bool mandatory = GetMandatoryConstraintValueAsInteger(
+      constraints,
+      MediaStreamVideoSource::kMaxWidth,
+      desired_width);
+  mandatory |= GetMandatoryConstraintValueAsInteger(
+      constraints,
+      MediaStreamVideoSource::kMaxHeight,
+      desired_height);
+  if (mandatory)
+    return;
+
+  GetOptionalConstraintValueAsInteger(constraints,
+                                      MediaStreamVideoSource::kMaxWidth,
+                                      desired_width);
+  GetOptionalConstraintValueAsInteger(constraints,
+                                      MediaStreamVideoSource::kMaxHeight,
+                                      desired_height);
+}
+
+// Retrieve the desired max and min aspect ratio from |constraints|. If not set,
+// the |min_aspect_ratio| is set to 0 and |max_aspect_ratio| is set to
+// std::numeric_limits<double>::max();
+// If either min or max aspect ratio is set as a mandatory constraint, the
+// optional constraints are not checked.
+void GetDesiredMinAndMaxAspectRatio(
+    const blink::WebMediaConstraints& constraints,
+    double* min_aspect_ratio,
+    double* max_aspect_ratio) {
+  *min_aspect_ratio = 0;
+  *max_aspect_ratio = std::numeric_limits<double>::max();
+
+  bool mandatory = GetMandatoryConstraintValueAsDouble(
+      constraints,
+      MediaStreamVideoSource::kMinAspectRatio,
+      min_aspect_ratio);
+  mandatory |= GetMandatoryConstraintValueAsDouble(
+      constraints,
+      MediaStreamVideoSource::kMaxAspectRatio,
+      max_aspect_ratio);
+  if (mandatory)
+    return;
+
+  GetOptionalConstraintValueAsDouble(
+      constraints,
+      MediaStreamVideoSource::kMinAspectRatio,
+      min_aspect_ratio);
+  GetOptionalConstraintValueAsDouble(
+      constraints,
+      MediaStreamVideoSource::kMaxAspectRatio,
+      max_aspect_ratio);
+}
+
+// Returns true if |constraint| is fulfilled. |format| can be changed by a
+// constraint, e.g. the frame rate can be changed by setting maxFrameRate.
 bool UpdateFormatForConstraint(
     const blink::WebMediaConstraint& constraint,
     bool mandatory,
@@ -78,7 +139,7 @@ bool UpdateFormatForConstraint(
     return true;
   }
 
-  if (constraint_name == kSourceId) {
+  if (constraint_name == MediaStreamSource::kSourceId) {
     // This is a constraint that doesn't affect the format.
     return true;
   }
@@ -90,49 +151,38 @@ bool UpdateFormatForConstraint(
 
   if (constraint_name == MediaStreamVideoSource::kMinAspectRatio ||
       constraint_name == MediaStreamVideoSource::kMaxAspectRatio) {
-    double double_value = 0;
-    base::StringToDouble(constraint_value, &double_value);
-
-    // The aspect ratio in |constraint.m_value| has been converted to a string
-    // and back to a double, so it may have a rounding error.
-    // E.g if the value 1/3 is converted to a string, the string will not have
-    // infinite length.
-    // We add a margin of 0.0005 which is high enough to detect the same aspect
-    // ratio but small enough to avoid matching wrong aspect ratios.
-    const double kRoundingTruncation = 0.0005;
-    double ratio = static_cast<double>(format->frame_size.width()) /
-        format->frame_size.height();
-    if (constraint_name == MediaStreamVideoSource::kMinAspectRatio)
-      return (double_value <= ratio + kRoundingTruncation);
-    // Subtract 0.0005 to avoid rounding problems. Same as above.
-    return (double_value >= ratio - kRoundingTruncation);
+    // These constraints are handled by cropping if the camera outputs the wrong
+    // aspect ratio.
+    double value;
+    return base::StringToDouble(constraint_value, &value);
   }
 
-  int value;
-  if (!base::StringToInt(constraint_value, &value)) {
+  double value = 0.0;
+  if (!base::StringToDouble(constraint_value, &value)) {
     DLOG(WARNING) << "Can't parse MediaStream constraint. Name:"
                   <<  constraint_name << " Value:" << constraint_value;
     return false;
   }
+
   if (constraint_name == MediaStreamVideoSource::kMinWidth) {
     return (value <= format->frame_size.width());
   } else if (constraint_name == MediaStreamVideoSource::kMaxWidth) {
-    return (value * kMaxCropFactor >= format->frame_size.width());
+    return value > 0.0;
   } else if (constraint_name == MediaStreamVideoSource::kMinHeight) {
     return (value <= format->frame_size.height());
   } else if (constraint_name == MediaStreamVideoSource::kMaxHeight) {
-     return (value * kMaxCropFactor >= format->frame_size.height());
+     return value > 0.0;
   } else if (constraint_name == MediaStreamVideoSource::kMinFrameRate) {
-    return (value <= format->frame_rate);
+    return (value > 0.0) && (value <= format->frame_rate);
   } else if (constraint_name == MediaStreamVideoSource::kMaxFrameRate) {
-    if (value == 0) {
+    if (value <= 0.0) {
       // The frame rate is set by constraint.
       // Don't allow 0 as frame rate if it is a mandatory constraint.
       // Set the frame rate to 1 if it is not mandatory.
       if (mandatory) {
         return false;
       } else {
-        value = 1;
+        value = 1.0;
       }
     }
     format->frame_rate =
@@ -170,20 +220,64 @@ void FilterFormatsByConstraint(
 // Returns the media::VideoCaptureFormats that matches |constraints|.
 media::VideoCaptureFormats FilterFormats(
     const blink::WebMediaConstraints& constraints,
-    const media::VideoCaptureFormats& supported_formats) {
+    const media::VideoCaptureFormats& supported_formats,
+    blink::WebString* unsatisfied_constraint) {
   if (constraints.isNull()) {
     return supported_formats;
   }
 
+  double max_aspect_ratio;
+  double min_aspect_ratio;
+  GetDesiredMinAndMaxAspectRatio(constraints,
+                                 &min_aspect_ratio,
+                                 &max_aspect_ratio);
+
+  if (min_aspect_ratio > max_aspect_ratio || max_aspect_ratio < 0.05f) {
+    DLOG(WARNING) << "Wrong requested aspect ratio.";
+    return media::VideoCaptureFormats();
+  }
+
+  int min_width = 0;
+  GetMandatoryConstraintValueAsInteger(constraints,
+                                       MediaStreamVideoSource::kMinWidth,
+                                       &min_width);
+  int min_height = 0;
+  GetMandatoryConstraintValueAsInteger(constraints,
+                                       MediaStreamVideoSource::kMinHeight,
+                                       &min_height);
+  int max_width;
+  int max_height;
+  GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height);
+
+  if (min_width > max_width || min_height > max_height)
+    return media::VideoCaptureFormats();
+
+  double min_frame_rate = 0.0f;
+  double max_frame_rate = 0.0f;
+  if (GetConstraintValueAsDouble(constraints,
+                                 MediaStreamVideoSource::kMaxFrameRate,
+                                 &max_frame_rate) &&
+      GetConstraintValueAsDouble(constraints,
+                                 MediaStreamVideoSource::kMinFrameRate,
+                                 &min_frame_rate)) {
+    if (min_frame_rate > max_frame_rate) {
+      DLOG(WARNING) << "Wrong requested frame rate.";
+      return media::VideoCaptureFormats();
+    }
+  }
+
   blink::WebVector<blink::WebMediaConstraint> mandatory;
   blink::WebVector<blink::WebMediaConstraint> optional;
   constraints.getMandatoryConstraints(mandatory);
   constraints.getOptionalConstraints(optional);
-
   media::VideoCaptureFormats candidates = supported_formats;
-
-  for (size_t i = 0; i < mandatory.size(); ++i)
+  for (size_t i = 0; i < mandatory.size(); ++i) {
     FilterFormatsByConstraint(mandatory[i], true, &candidates);
+    if (candidates.empty()) {
+      *unsatisfied_constraint = mandatory[i].m_name;
+      return candidates;
+    }
+  }
 
   if (candidates.empty())
     return candidates;
@@ -206,43 +300,6 @@ media::VideoCaptureFormats FilterFormats(
   return candidates;
 }
 
-bool GetConstraintValue(const blink::WebMediaConstraints& constraints,
-                        bool mandatory, const blink::WebString& name,
-                        int* value) {
-  blink::WebString value_str;
-  bool ret = mandatory ?
-      constraints.getMandatoryConstraintValue(name, value_str) :
-      constraints.getOptionalConstraintValue(name, value_str);
-  if (ret)
-    base::StringToInt(value_str.utf8(), value);
-  return ret;
-}
-
-// Returns true if |constraint| has mandatory constraints.
-bool HasMandatoryConstraints(const blink::WebMediaConstraints& constraints) {
-  blink::WebVector<blink::WebMediaConstraint> mandatory_constraints;
-  constraints.getMandatoryConstraints(mandatory_constraints);
-  return !mandatory_constraints.isEmpty();
-}
-
-// Retrieve the desired max width and height from |constraints|.
-void GetDesiredMaxWidthAndHeight(const blink::WebMediaConstraints& constraints,
-                                 int* desired_width, int* desired_height) {
-  bool mandatory = GetConstraintValue(constraints, true,
-                                      MediaStreamVideoSource::kMaxWidth,
-                                      desired_width);
-  mandatory |= GetConstraintValue(constraints, true,
-                                  MediaStreamVideoSource::kMaxHeight,
-                                  desired_height);
-  if (mandatory)
-    return;
-
-  GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxWidth,
-                     desired_width);
-  GetConstraintValue(constraints, false, MediaStreamVideoSource::kMaxHeight,
-                     desired_height);
-}
-
 const media::VideoCaptureFormat& GetBestFormatBasedOnArea(
     const media::VideoCaptureFormats& formats,
     int area) {
@@ -268,109 +325,21 @@ const media::VideoCaptureFormat& GetBestFormatBasedOnArea(
 void GetBestCaptureFormat(
     const media::VideoCaptureFormats& formats,
     const blink::WebMediaConstraints& constraints,
-    media::VideoCaptureFormat* capture_format,
-    gfx::Size* max_frame_output_size) {
+    media::VideoCaptureFormat* capture_format) {
   DCHECK(!formats.empty());
-  DCHECK(max_frame_output_size);
 
-  int max_width = std::numeric_limits<int>::max();
-  int max_height = std::numeric_limits<int>::max();;
+  int max_width;
+  int max_height;
   GetDesiredMaxWidthAndHeight(constraints, &max_width, &max_height);
 
   *capture_format = GetBestFormatBasedOnArea(
       formats,
       std::min(max_width, MediaStreamVideoSource::kDefaultWidth) *
       std::min(max_height, MediaStreamVideoSource::kDefaultHeight));
-
-  max_frame_output_size->set_width(max_width);
-  max_frame_output_size->set_height(max_height);
-}
-
-// Empty method used for keeping a reference to the original media::VideoFrame
-// in MediaStreamVideoSource::FrameDeliverer::DeliverFrameOnIO if cropping is
-// needed. The reference to |frame| is kept in the closure that calls this
-// method.
-void ReleaseOriginalFrame(
-    const scoped_refptr<media::VideoFrame>& frame) {
 }
 
 }  // anonymous namespace
 
-// Helper class used for delivering video frames to all registered tracks
-// on the IO-thread.
-class MediaStreamVideoSource::FrameDeliverer : public VideoFrameDeliverer {
- public:
-  FrameDeliverer(
-      const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
-      : VideoFrameDeliverer(io_message_loop)  {
-  }
-
-  // Register |callback| to receive video frames of max size
-  // |max_frame_output_size| on the IO thread.
-  // TODO(perkj): Currently |max_frame_output_size| must be the same for all
-  // |callbacks|.
-  void AddCallback(void* id,
-                   const VideoCaptureDeliverFrameCB& callback,
-                   const gfx::Size& max_frame_output_size) {
-    DCHECK(thread_checker().CalledOnValidThread());
-    io_message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(
-            &FrameDeliverer::AddCallbackWithResolutionOnIO,
-            this, id, callback, max_frame_output_size));
-  }
-
-  virtual void DeliverFrameOnIO(
-      const scoped_refptr<media::VideoFrame>& frame,
-      const media::VideoCaptureFormat& format) OVERRIDE {
-    DCHECK(io_message_loop()->BelongsToCurrentThread());
-    TRACE_EVENT0("video", "MediaStreamVideoSource::DeliverFrameOnIO");
-    if (max_output_size_.IsEmpty())
-      return;  // Frame received before the output has been decided.
-
-    scoped_refptr<media::VideoFrame> video_frame(frame);
-    const gfx::Size& visible_size = frame->visible_rect().size();
-    if (visible_size.width() > max_output_size_.width() ||
-        visible_size.height() > max_output_size_.height()) {
-      // If |frame| is not the size that is expected, we need to crop it by
-      // providing a new |visible_rect|. The new visible rect must be within the
-      // original |visible_rect|.
-      gfx::Rect output_rect = frame->visible_rect();
-      output_rect.ClampToCenteredSize(max_output_size_);
-      // TODO(perkj): Allow cropping of textures once http://crbug/362521 is
-      // fixed.
-      if (frame->format() != media::VideoFrame::NATIVE_TEXTURE) {
-        video_frame = media::VideoFrame::WrapVideoFrame(
-            frame,
-            output_rect,
-            output_rect.size(),
-            base::Bind(&ReleaseOriginalFrame, frame));
-      }
-    }
-    VideoFrameDeliverer::DeliverFrameOnIO(video_frame, format);
-  }
-
- protected:
-  virtual ~FrameDeliverer() {
-  }
-
-  void AddCallbackWithResolutionOnIO(
-      void* id,
-     const VideoCaptureDeliverFrameCB& callback,
-      const gfx::Size& max_frame_output_size) {
-    DCHECK(io_message_loop()->BelongsToCurrentThread());
-    // Currently we only support one frame output size.
-    DCHECK(!max_frame_output_size.IsEmpty() &&
-           (max_output_size_.IsEmpty() ||
-            max_output_size_ == max_frame_output_size));
-    max_output_size_ = max_frame_output_size;
-    VideoFrameDeliverer::AddCallbackOnIO(id, callback);
-  }
-
- private:
-  gfx::Size max_output_size_;
-};
-
 // static
 MediaStreamVideoSource* MediaStreamVideoSource::GetVideoSource(
     const blink::WebMediaStreamSource& source) {
@@ -388,14 +357,14 @@ bool MediaStreamVideoSource::IsConstraintSupported(const std::string& name) {
 
 MediaStreamVideoSource::MediaStreamVideoSource()
     : state_(NEW),
-      frame_deliverer_(
-          new MediaStreamVideoSource::FrameDeliverer(
-              ChildProcess::current()->io_message_loop_proxy())),
+      muted_state_(false),
+      track_adapter_(new VideoTrackAdapter(
+          ChildProcess::current()->io_message_loop_proxy())),
       weak_factory_(this) {
 }
 
 MediaStreamVideoSource::~MediaStreamVideoSource() {
-  DVLOG(3) << "~MediaStreamVideoSource()";
+  DCHECK(CalledOnValidThread());
 }
 
 void MediaStreamVideoSource::AddTrack(
@@ -404,6 +373,7 @@ void MediaStreamVideoSource::AddTrack(
     const blink::WebMediaConstraints& constraints,
     const ConstraintsCallback& callback) {
   DCHECK(CalledOnValidThread());
+  DCHECK(!constraints.isNull());
   DCHECK(std::find(tracks_.begin(), tracks_.end(),
                    track) == tracks_.end());
   tracks_.push_back(track);
@@ -416,15 +386,24 @@ void MediaStreamVideoSource::AddTrack(
       // Tab capture and Screen capture needs the maximum requested height
       // and width to decide on the resolution.
       int max_requested_width = 0;
-      GetConstraintValue(constraints, true, kMaxWidth, &max_requested_width);
+      GetMandatoryConstraintValueAsInteger(constraints, kMaxWidth,
+                                           &max_requested_width);
 
       int max_requested_height = 0;
-      GetConstraintValue(constraints, true, kMaxHeight, &max_requested_height);
+      GetMandatoryConstraintValueAsInteger(constraints, kMaxHeight,
+                                           &max_requested_height);
+
+      double max_requested_frame_rate;
+      if (!GetConstraintValueAsDouble(constraints, kMaxFrameRate,
+                                      &max_requested_frame_rate)) {
+        max_requested_frame_rate = kDefaultFrameRate;
+      }
 
       state_ = RETRIEVING_CAPABILITIES;
       GetCurrentSupportedFormats(
           max_requested_width,
           max_requested_height,
+          max_requested_frame_rate,
           base::Bind(&MediaStreamVideoSource::OnSupportedFormats,
                      weak_factory_.GetWeakPtr()));
 
@@ -450,9 +429,20 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
       std::find(tracks_.begin(), tracks_.end(), video_track);
   DCHECK(it != tracks_.end());
   tracks_.erase(it);
-  // Call |RemoveCallback| here even if adding the track has failed and
-  // frame_deliverer_->AddCallback has not been called.
-  frame_deliverer_->RemoveCallback(video_track);
+
+  // Check if |video_track| is waiting for applying new constraints and remove
+  // the request in that case.
+  for (std::vector<RequestedConstraints>::iterator it =
+           requested_constraints_.begin();
+       it != requested_constraints_.end(); ++it) {
+    if (it->track == video_track) {
+      requested_constraints_.erase(it);
+      break;
+    }
+  }
+  // Call |frame_adapter_->RemoveTrack| here even if adding the track has
+  // failed and |frame_adapter_->AddCallback| has not been called.
+  track_adapter_->RemoveTrack(video_track);
 
   if (tracks_.empty())
     StopSource();
@@ -460,7 +450,8 @@ void MediaStreamVideoSource::RemoveTrack(MediaStreamVideoTrack* video_track) {
 
 const scoped_refptr<base::MessageLoopProxy>&
 MediaStreamVideoSource::io_message_loop() const {
-  return frame_deliverer_->io_message_loop();
+  DCHECK(CalledOnValidThread());
+  return track_adapter_->io_message_loop();
 }
 
 void MediaStreamVideoSource::DoStopSource() {
@@ -480,9 +471,7 @@ void MediaStreamVideoSource::OnSupportedFormats(
 
   supported_formats_ = formats;
   if (!FindBestFormatWithConstraints(supported_formats_,
-                                     &current_format_,
-                                     &max_frame_output_size_,
-                                     &current_constraints_)) {
+                                     &current_format_)) {
     SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded);
     // This object can be deleted after calling FinalizeAddTrack. See comment
     // in the header file.
@@ -494,21 +483,22 @@ void MediaStreamVideoSource::OnSupportedFormats(
   DVLOG(3) << "Starting the capturer with"
            << " width = " << current_format_.frame_size.width()
            << " height = " << current_format_.frame_size.height()
-           << " frame rate = " << current_format_.frame_rate;
+           << " frame rate = " << current_format_.frame_rate
+           << " pixel format = "
+           << media::VideoCaptureFormat::PixelFormatToString(
+               current_format_.pixel_format);
 
   media::VideoCaptureParams params;
   params.requested_format = current_format_;
   StartSourceImpl(
       params,
-      base::Bind(&MediaStreamVideoSource::FrameDeliverer::DeliverFrameOnIO,
-                 frame_deliverer_));
+      base::Bind(&VideoTrackAdapter::DeliverFrameOnIO, track_adapter_));
 }
 
 bool MediaStreamVideoSource::FindBestFormatWithConstraints(
     const media::VideoCaptureFormats& formats,
-    media::VideoCaptureFormat* best_format,
-    gfx::Size* max_frame_output_size,
-    blink::WebMediaConstraints* resulting_constraints) {
+    media::VideoCaptureFormat* best_format) {
+  DCHECK(CalledOnValidThread());
   // Find the first constraints that we can fulfill.
   for (std::vector<RequestedConstraints>::iterator request_it =
            requested_constraints_.begin();
@@ -521,37 +511,31 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints(
     // we will start with whatever format is native to the source.
     if (formats.empty() && !HasMandatoryConstraints(requested_constraints)) {
       *best_format = media::VideoCaptureFormat();
-      *resulting_constraints = requested_constraints;
-      *max_frame_output_size = gfx::Size(std::numeric_limits<int>::max(),
-                                         std::numeric_limits<int>::max());
       return true;
     }
+    blink::WebString unsatisfied_constraint;
     media::VideoCaptureFormats filtered_formats =
-        FilterFormats(requested_constraints, formats);
+        FilterFormats(requested_constraints, formats, &unsatisfied_constraint);
     if (filtered_formats.size() > 0) {
       // A request with constraints that can be fulfilled.
       GetBestCaptureFormat(filtered_formats,
                            requested_constraints,
-                           best_format,
-                           max_frame_output_size);
-      *resulting_constraints= requested_constraints;
+                           best_format);
       return true;
     }
   }
   return false;
 }
 
-void MediaStreamVideoSource::OnStartDone(bool success) {
+void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) {
   DCHECK(CalledOnValidThread());
-  DVLOG(3) << "OnStartDone({success =" << success << "})";
-  if (success) {
+  DVLOG(3) << "OnStartDone({result =" << result << "})";
+  if (result == MEDIA_DEVICE_OK) {
     DCHECK_EQ(STARTING, state_);
     state_ = STARTED;
     SetReadyState(blink::WebMediaStreamSource::ReadyStateLive);
   } else {
-    state_ = ENDED;
-    SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded);
-    StopSourceImpl();
+    StopSource();
   }
 
   // This object can be deleted after calling FinalizeAddTrack. See comment in
@@ -560,6 +544,7 @@ void MediaStreamVideoSource::OnStartDone(bool success) {
 }
 
 void MediaStreamVideoSource::FinalizeAddTrack() {
+  DCHECK(CalledOnValidThread());
   media::VideoCaptureFormats formats;
   formats.push_back(current_format_);
 
@@ -567,27 +552,53 @@ void MediaStreamVideoSource::FinalizeAddTrack() {
   callbacks.swap(requested_constraints_);
   for (std::vector<RequestedConstraints>::iterator it = callbacks.begin();
        it != callbacks.end(); ++it) {
-    // The track has been added successfully if the source has started and
-    // there are either no mandatory constraints and the source doesn't expose
-    // its format capabilities, or the constraints and the format match.
-    // For example, a remote source doesn't expose its format capabilities.
-    bool success =
-        state_ == STARTED &&
-        ((!current_format_.IsValid() && !HasMandatoryConstraints(
-            it->constraints)) ||
-         !FilterFormats(it->constraints, formats).empty());
-    if (success) {
-      frame_deliverer_->AddCallback(it->track, it->frame_callback,
-                                    max_frame_output_size_);
+    MediaStreamRequestResult result = MEDIA_DEVICE_OK;
+    blink::WebString unsatisfied_constraint;
+
+    if (HasMandatoryConstraints(it->constraints) &&
+        FilterFormats(it->constraints, formats,
+                      &unsatisfied_constraint).empty())
+      result = MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+
+    if (state_ != STARTED && result == MEDIA_DEVICE_OK)
+      result = MEDIA_DEVICE_TRACK_START_FAILURE;
+
+    if (result == MEDIA_DEVICE_OK) {
+      int max_width;
+      int max_height;
+      GetDesiredMaxWidthAndHeight(it->constraints, &max_width, &max_height);
+      double max_aspect_ratio;
+      double min_aspect_ratio;
+      GetDesiredMinAndMaxAspectRatio(it->constraints,
+                                     &min_aspect_ratio,
+                                     &max_aspect_ratio);
+      double max_frame_rate = 0.0f;
+      GetConstraintValueAsDouble(it->constraints,
+                                 kMaxFrameRate, &max_frame_rate);
+
+      VideoTrackAdapter::OnMutedCallback on_mute_callback =
+          media::BindToCurrentLoop(base::Bind(
+              &MediaStreamVideoSource::SetMutedState,
+              weak_factory_.GetWeakPtr()));
+      track_adapter_->AddTrack(it->track, it->frame_callback,
+                               max_width, max_height,
+                               min_aspect_ratio, max_aspect_ratio,
+                               max_frame_rate, current_format_.frame_rate,
+                               on_mute_callback);
+    }
+
+    DVLOG(3) << "FinalizeAddTrack() result " << result;
+
+    if (!it->callback.is_null()) {
+      it->callback.Run(this, result, unsatisfied_constraint);
     }
-    DVLOG(3) << "FinalizeAddTrack() success " << success;
-    if (!it->callback.is_null())
-      it->callback.Run(this, success);
   }
 }
 
 void MediaStreamVideoSource::SetReadyState(
     blink::WebMediaStreamSource::ReadyState state) {
+  DVLOG(3) << "MediaStreamVideoSource::SetReadyState state " << state;
+  DCHECK(CalledOnValidThread());
   if (!owner().isNull()) {
     owner().setReadyState(state);
   }
@@ -597,6 +608,16 @@ void MediaStreamVideoSource::SetReadyState(
   }
 }
 
+void MediaStreamVideoSource::SetMutedState(bool muted_state) {
+  DVLOG(3) << "MediaStreamVideoSource::SetMutedState state=" << muted_state;
+  DCHECK(CalledOnValidThread());
+  // WebMediaStreamSource doesn't have a muted state, the tracks do.
+  for (std::vector<MediaStreamVideoTrack*>::iterator it = tracks_.begin();
+       it != tracks_.end(); ++it) {
+    (*it)->SetMutedState(muted_state);
+  }
+}
+
 MediaStreamVideoSource::RequestedConstraints::RequestedConstraints(
     MediaStreamVideoTrack* track,
     const VideoCaptureDeliverFrameCB& frame_callback,