Bug 1388219 - add a nsTArray mTargetCapability to record each track target capability. r?jib draft
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Wed, 13 Sep 2017 00:19:02 +0800
changeset 663120 025ecccedd3a24892a6199f943136aa2e3970a2d
parent 663119 a66b75a159ec5fd203778dc031f407653061741d
child 663121 5218145fbdab941c4e33a3c9b428878532598bd1
child 663464 ab3a20e8d0808521fb23a4b3ccad7f34bbbcae6b
push id79331
push userbmo:mchiang@mozilla.com
push dateTue, 12 Sep 2017 17:23:50 +0000
reviewersjib
bugs1388219
milestone57.0a1
Bug 1388219 - add a nsTArray mTargetCapability to record each track target capability. r?jib MozReview-Commit-ID: KJLhzSDw4wz
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineCameraVideoSource.cpp
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
dom/media/webrtc/MediaEngineRemoteVideoSource.h
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
dom/media/webrtc/MediaTrackConstraints.cpp
dom/media/webrtc/MediaTrackConstraints.h
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -217,31 +217,34 @@ public:
   virtual bool GetScary() const { return false; };
 
   class AllocationHandle
   {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocationHandle);
   protected:
     ~AllocationHandle() {}
+    static uint32_t sId;
   public:
     AllocationHandle(const dom::MediaTrackConstraints& aConstraints,
                      const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                      const MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId)
 
     : mConstraints(aConstraints),
       mPrincipalInfo(aPrincipalInfo),
       mPrefs(aPrefs),
-      mDeviceId(aDeviceId) {}
+      mDeviceId(aDeviceId),
+      mId(sId++) {}
   public:
     NormalizedConstraints mConstraints;
     mozilla::ipc::PrincipalInfo mPrincipalInfo;
     MediaEnginePrefs mPrefs;
     nsString mDeviceId;
+    uint32_t mId;
   };
 
   /* Release the device back to the system. */
   virtual nsresult Deallocate(AllocationHandle* aHandle)
   {
     MOZ_ASSERT(aHandle);
     RefPtr<AllocationHandle> handle = aHandle;
 
@@ -369,16 +372,17 @@ protected:
    * aPrefs            - As passed in (in case of changes in about:config).
    * aDeviceId         - As passed in (origin dependent).
    * aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
    */
 
   virtual nsresult
   UpdateSingleSource(const AllocationHandle* aHandle,
                      const NormalizedConstraints& aNetConstraints,
+                     const NormalizedConstraints& aNewConstraint,
                      const MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId,
                      const char** aOutBadConstraint) {
     return NS_ERROR_NOT_IMPLEMENTED;
   };
 
   /* ReevaluateAllocation - Call to change constraints for an allocation of
    * a single device. Manages allocation handles, calculates net constraints
@@ -397,36 +401,42 @@ protected:
                        NormalizedConstraints* aConstraintsUpdate,
                        const MediaEnginePrefs& aPrefs,
                        const nsString& aDeviceId,
                        const char** aOutBadConstraint)
   {
     // aHandle and/or aConstraintsUpdate may be nullptr (see below)
 
     AutoTArray<const NormalizedConstraints*, 10> allConstraints;
+    AutoTArray<const NormalizedConstraints*, 1> updatedConstraint;
     for (auto& registered : mRegisteredHandles) {
       if (aConstraintsUpdate && registered.get() == aHandle) {
         continue; // Don't count old constraints
       }
       allConstraints.AppendElement(&registered->mConstraints);
     }
     if (aConstraintsUpdate) {
       allConstraints.AppendElement(aConstraintsUpdate);
+      updatedConstraint.AppendElement(aConstraintsUpdate);
     } else if (aHandle) {
       // In the case of AddShareOfSingleSource, the handle isn't registered yet.
       allConstraints.AppendElement(&aHandle->mConstraints);
+      updatedConstraint.AppendElement(&aHandle->mConstraints);
+    } else {
+      updatedConstraint.AppendElements(allConstraints);
     }
 
     NormalizedConstraints netConstraints(allConstraints);
     if (netConstraints.mBadConstraint) {
       *aOutBadConstraint = netConstraints.mBadConstraint;
       return NS_ERROR_FAILURE;
     }
 
-    nsresult rv = UpdateSingleSource(aHandle, netConstraints, aPrefs, aDeviceId,
+    NormalizedConstraints newConstraint(updatedConstraint);
+    nsresult rv = UpdateSingleSource(aHandle, netConstraints, newConstraint, aPrefs, aDeviceId,
                                      aOutBadConstraint);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (aHandle && aConstraintsUpdate) {
       aHandle->mConstraints = *aConstraintsUpdate;
     }
     return NS_OK;
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -50,16 +50,29 @@ void
 MediaEngineCameraVideoSource::GetCapability(size_t aIndex,
                                             webrtc::CaptureCapability& aOut) const
 {
   MOZ_ASSERT(aIndex < mHardcodedCapabilities.Length());
   aOut = mHardcodedCapabilities.SafeElementAt(aIndex, webrtc::CaptureCapability());
 }
 
 uint32_t
+MediaEngineCameraVideoSource::GetDistance(
+    const webrtc::CaptureCapability& aCandidate,
+    const NormalizedConstraintSet &aConstraints,
+    const nsString& aDeviceId,
+    const DistanceCalculation aCalculate) const
+{
+  if (aCalculate == kFeasibility)
+    return GetFeasibilityDistance(aCandidate, aConstraints, aDeviceId);
+  else
+    return GetFitnessDistance(aCandidate, aConstraints, aDeviceId);
+}
+
+uint32_t
 MediaEngineCameraVideoSource::GetFitnessDistance(
     const webrtc::CaptureCapability& aCandidate,
     const NormalizedConstraintSet &aConstraints,
     const nsString& aDeviceId) const
 {
   // Treat width|height|frameRate == 0 on capability as "can do any".
   // This allows for orthogonal capabilities that are not in discrete steps.
 
@@ -70,16 +83,37 @@ MediaEngineCameraVideoSource::GetFitness
                                                aConstraints.mWidth) : 0) +
     uint64_t(aCandidate.height? FitnessDistance(int32_t(aCandidate.height),
                                                 aConstraints.mHeight) : 0) +
     uint64_t(aCandidate.maxFPS? FitnessDistance(double(aCandidate.maxFPS),
                                                 aConstraints.mFrameRate) : 0);
   return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
 }
 
+uint32_t
+MediaEngineCameraVideoSource::GetFeasibilityDistance(
+    const webrtc::CaptureCapability& aCandidate,
+    const NormalizedConstraintSet &aConstraints,
+    const nsString& aDeviceId) const
+{
+  // Treat width|height|frameRate == 0 on capability as "can do any".
+  // This allows for orthogonal capabilities that are not in discrete steps.
+
+  uint64_t distance =
+    uint64_t(FitnessDistance(aDeviceId, aConstraints.mDeviceId)) +
+    uint64_t(FitnessDistance(mFacingMode, aConstraints.mFacingMode)) +
+    uint64_t(aCandidate.width? FeasibilityDistance(int32_t(aCandidate.width),
+                                               aConstraints.mWidth) : 0) +
+    uint64_t(aCandidate.height? FeasibilityDistance(int32_t(aCandidate.height),
+                                                aConstraints.mHeight) : 0) +
+    uint64_t(aCandidate.maxFPS? FeasibilityDistance(double(aCandidate.maxFPS),
+                                                aConstraints.mFrameRate) : 0);
+  return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
+}
+
 // Find best capability by removing inferiors. May leave >1 of equal distance
 
 /* static */ void
 MediaEngineCameraVideoSource::TrimLessFitCandidates(CapabilitySet& set) {
   uint32_t best = UINT32_MAX;
   for (auto& candidate : set) {
     if (best > candidate.mDistance) {
       best = candidate.mDistance;
@@ -213,17 +247,19 @@ MediaEngineCameraVideoSource::LogCapabil
                       uint32_t(sizeof(codec) / sizeof(*codec) - 1))],
        aDistance));
 }
 
 bool
 MediaEngineCameraVideoSource::ChooseCapability(
     const NormalizedConstraints &aConstraints,
     const MediaEnginePrefs &aPrefs,
-    const nsString& aDeviceId)
+    const nsString& aDeviceId,
+    webrtc::CaptureCapability& aCapability,
+    const DistanceCalculation aCalculate)
 {
   if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
          aPrefs.GetWidth(), aPrefs.GetHeight(),
          aPrefs.mFPS, aPrefs.mMinFPS));
     LogConstraints(aConstraints);
     if (!aConstraints.mAdvanced.empty()) {
       LOG(("Advanced array[%zu]:", aConstraints.mAdvanced.size()));
@@ -241,17 +277,17 @@ MediaEngineCameraVideoSource::ChooseCapa
   }
 
   // First, filter capabilities by required constraints (min, max, exact).
 
   for (size_t i = 0; i < candidateSet.Length();) {
     auto& candidate = candidateSet[i];
     webrtc::CaptureCapability cap;
     GetCapability(candidate.mIndex, cap);
-    candidate.mDistance = GetFitnessDistance(cap, aConstraints, aDeviceId);
+    candidate.mDistance = GetDistance(cap, aConstraints, aDeviceId, aCalculate);
     LogCapability("Capability", cap, candidate.mDistance);
     if (candidate.mDistance == UINT32_MAX) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
@@ -263,17 +299,17 @@ MediaEngineCameraVideoSource::ChooseCapa
   // Filter further with all advanced constraints (that don't overconstrain).
 
   for (const auto &cs : aConstraints.mAdvanced) {
     CapabilitySet rejects;
     for (size_t i = 0; i < candidateSet.Length();) {
       auto& candidate = candidateSet[i];
       webrtc::CaptureCapability cap;
       GetCapability(candidate.mIndex, cap);
-      if (GetFitnessDistance(cap, cs, aDeviceId) == UINT32_MAX) {
+      if (GetDistance(cap, cs, aDeviceId, aCalculate) == UINT32_MAX) {
         rejects.AppendElement(candidate);
         candidateSet.RemoveElementAt(i);
       } else {
         ++i;
       }
     }
     if (!candidateSet.Length()) {
       candidateSet.AppendElements(Move(rejects));
@@ -294,39 +330,39 @@ MediaEngineCameraVideoSource::ChooseCapa
     prefs.mWidth.SetAsLong() = aPrefs.GetWidth();
     prefs.mHeight.SetAsLong() = aPrefs.GetHeight();
     prefs.mFrameRate.SetAsDouble() = aPrefs.mFPS;
     NormalizedConstraintSet normPrefs(prefs, false);
 
     for (auto& candidate : candidateSet) {
       webrtc::CaptureCapability cap;
       GetCapability(candidate.mIndex, cap);
-      candidate.mDistance = GetFitnessDistance(cap, normPrefs, aDeviceId);
+      candidate.mDistance = GetDistance(cap, normPrefs, aDeviceId, aCalculate);
     }
     TrimLessFitCandidates(candidateSet);
   }
 
   // Any remaining multiples all have the same distance, but may vary on
   // format. Some formats are more desirable for certain use like WebRTC.
   // E.g. I420 over RGB24 can remove a needless format conversion.
 
   bool found = false;
   for (auto& candidate : candidateSet) {
     webrtc::CaptureCapability cap;
     GetCapability(candidate.mIndex, cap);
     if (cap.rawType == webrtc::RawVideoType::kVideoI420 ||
         cap.rawType == webrtc::RawVideoType::kVideoYUY2 ||
         cap.rawType == webrtc::RawVideoType::kVideoYV12) {
-      mCapability = cap;
+      aCapability = cap;
       found = true;
       break;
     }
   }
   if (!found) {
-    GetCapability(candidateSet[0].mIndex, mCapability);
+    GetCapability(candidateSet[0].mIndex, aCapability);
   }
 
   LogCapability("Chosen capability", mCapability, sameDistance);
   return true;
 }
 
 void
 MediaEngineCameraVideoSource::SetName(nsString aName)
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -19,16 +19,21 @@
 #include "webrtc/modules/video_capture/video_capture_defines.h"
 
 namespace webrtc {
   using CaptureCapability = VideoCaptureCapability;
 }
 
 namespace mozilla {
 
+enum DistanceCalculation {
+  kFitness,
+  kFeasibility
+};
+
 class MediaEngineCameraVideoSource : public MediaEngineVideoSource
 {
 public:
   // Some subclasses use an index to track multiple instances.
   explicit MediaEngineCameraVideoSource(int aIndex,
                                         const char* aMonitorName = "Camera.Monitor")
     : MediaEngineVideoSource(kReleased)
     , mMonitor(aMonitorName)
@@ -83,29 +88,40 @@ protected:
   ~MediaEngineCameraVideoSource() {}
 
   // guts for appending data to the MSG track
   virtual bool AppendToTrack(SourceMediaStream* aSource,
                              layers::Image* aImage,
                              TrackID aID,
                              StreamTime delta,
                              const PrincipalHandle& aPrincipalHandle);
+  uint32_t GetDistance(const webrtc::CaptureCapability& aCandidate,
+                              const NormalizedConstraintSet &aConstraints,
+                              const nsString& aDeviceId,
+                              const DistanceCalculation aCalculate) const;
   uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
                               const NormalizedConstraintSet &aConstraints,
                               const nsString& aDeviceId) const;
+  uint32_t GetFeasibilityDistance(const webrtc::CaptureCapability& aCandidate,
+                              const NormalizedConstraintSet &aConstraints,
+                              const nsString& aDeviceId) const;
   static void TrimLessFitCandidates(CapabilitySet& set);
   static void LogConstraints(const NormalizedConstraintSet& aConstraints);
   static void LogCapability(const char* aHeader,
                             const webrtc::CaptureCapability &aCapability,
                             uint32_t aDistance);
   virtual size_t NumCapabilities() const;
   virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) const;
-  virtual bool ChooseCapability(const NormalizedConstraints &aConstraints,
-                                const MediaEnginePrefs &aPrefs,
-                                const nsString& aDeviceId);
+  virtual bool ChooseCapability(
+    const NormalizedConstraints &aConstraints,
+    const MediaEnginePrefs &aPrefs,
+    const nsString& aDeviceId,
+    webrtc::CaptureCapability& aCapability,
+    const DistanceCalculation aCalculate
+  );
   void SetName(nsString aName);
   void SetUUID(const char* aUUID);
   const nsCString& GetUUID() const; // protected access
 
   // Engine variables.
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
@@ -113,26 +129,30 @@ protected:
   // mMonitor also protects mSources[] and mPrincipalHandles[] access/changes.
   // mSources[] and mPrincipalHandles[] are accessed from webrtc threads.
 
   // All the mMonitor accesses are from the child classes.
   Monitor mMonitor; // Monitor for processing Camera frames.
   nsTArray<RefPtr<SourceMediaStream>> mSources; // When this goes empty, we shut down HW
   nsTArray<PrincipalHandle> mPrincipalHandles; // Directly mapped to mSources.
   RefPtr<layers::Image> mImage;
+  nsTArray<webrtc::CaptureCapability> mTargetCapabilities;
+  nsTArray<uint32_t> mHandleIds;
   RefPtr<layers::ImageContainer> mImageContainer;
   // end of data protected by mMonitor
 
   int mWidth, mHeight;
   bool mInitDone;
   bool mHasDirectListeners;
   int mCaptureIndex;
   TrackID mTrackID;
 
   webrtc::CaptureCapability mCapability;
+  webrtc::CaptureCapability mTargetCapability;
+  uint32_t mHandleId;
 
   mutable nsTArray<webrtc::CaptureCapability> mHardcodedCapabilities;
 private:
   nsString mDeviceName;
   nsCString mUniqueId;
   nsString mFacingMode;
 };
 
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -12,16 +12,18 @@
 #include "CamerasChild.h"
 
 extern mozilla::LogModule* GetMediaManagerLog();
 #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 #define LOGFRAME(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
 
 namespace mozilla {
 
+uint32_t MediaEngineCameraVideoSource::AllocationHandle::sId = 0;
+
 // These need a definition somewhere because template
 // code is allowed to take their address, and they aren't
 // guaranteed to have one without this.
 const unsigned int MediaEngineSource::kMaxDeviceNameLength;
 const unsigned int MediaEngineSource::kMaxUniqueIdLength;;
 
 using dom::ConstrainLongRange;
 
@@ -75,16 +77,18 @@ MediaEngineRemoteVideoSource::Shutdown()
     bool empty;
 
     while (1) {
       {
         MonitorAutoLock lock(mMonitor);
         empty = mSources.IsEmpty();
         if (empty) {
           MOZ_ASSERT(mPrincipalHandles.IsEmpty());
+          MOZ_ASSERT(mTargetCapabilities.IsEmpty());
+          MOZ_ASSERT(mHandleIds.IsEmpty());
           break;
         }
         source = mSources[0];
       }
       Stop(source, kVideoTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
   }
@@ -121,16 +125,18 @@ MediaEngineRemoteVideoSource::Allocate(
   if (NS_FAILED(rv)) {
     return rv;
   }
   if (mState == kStarted &&
       MOZ_LOG_TEST(GetMediaManagerLog(), mozilla::LogLevel::Debug)) {
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
       MOZ_ASSERT(mPrincipalHandles.IsEmpty());
+      MOZ_ASSERT(mTargetCapabilities.IsEmpty());
+      MOZ_ASSERT(mHandleIds.IsEmpty());
       LOG(("Video device %d reallocated", mCaptureIndex));
     } else {
       LOG(("Video device %d allocated shared", mCaptureIndex));
     }
   }
   return NS_OK;
 }
 
@@ -167,17 +173,22 @@ MediaEngineRemoteVideoSource::Start(Sour
     LOG(("No stream or init not done"));
     return NS_ERROR_FAILURE;
   }
 
   {
     MonitorAutoLock lock(mMonitor);
     mSources.AppendElement(aStream);
     mPrincipalHandles.AppendElement(aPrincipalHandle);
+    mTargetCapabilities.AppendElement(mTargetCapability);
+    mHandleIds.AppendElement(mHandleId);
+
     MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length());
+    MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length());
+    MOZ_ASSERT(mSources.Length() == mHandleIds.Length());
   }
 
   aStream->AddTrack(aID, 0, new VideoSegment(), SourceMediaStream::ADDTRACK_QUEUED);
 
   if (mState == kStarted) {
     return NS_OK;
   }
   mImageContainer =
@@ -213,18 +224,22 @@ MediaEngineRemoteVideoSource::Stop(mozil
 
     size_t i = mSources.IndexOf(aSource);
     if (i == mSources.NoIndex) {
       // Already stopped - this is allowed
       return NS_OK;
     }
 
     MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length());
+    MOZ_ASSERT(mSources.Length() == mTargetCapabilities.Length());
+    MOZ_ASSERT(mSources.Length() == mHandleIds.Length());
     mSources.RemoveElementAt(i);
     mPrincipalHandles.RemoveElementAt(i);
+    mTargetCapabilities.RemoveElementAt(i);
+    mHandleIds.RemoveElementAt(i);
 
     aSource->EndTrack(aID);
 
     if (!mSources.IsEmpty()) {
       return NS_OK;
     }
     if (mState != kStarted) {
       return NS_ERROR_FAILURE;
@@ -257,50 +272,77 @@ MediaEngineRemoteVideoSource::Restart(Al
   return ReevaluateAllocation(aHandle, &constraints, aPrefs, aDeviceId,
                               aOutBadConstraint);
 }
 
 nsresult
 MediaEngineRemoteVideoSource::UpdateSingleSource(
     const AllocationHandle* aHandle,
     const NormalizedConstraints& aNetConstraints,
+    const NormalizedConstraints& aNewConstraint,
     const MediaEnginePrefs& aPrefs,
     const nsString& aDeviceId,
     const char** aOutBadConstraint)
 {
-  if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId)) {
-    *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId);
-    return NS_ERROR_FAILURE;
-  }
-
   switch (mState) {
     case kReleased:
       MOZ_ASSERT(aHandle);
+      mHandleId = aHandle->mId;
+      if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId, mCapability, kFitness)) {
+        *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId);
+        return NS_ERROR_FAILURE;
+      }
+      mTargetCapability = mCapability;
+
       if (camera::GetChildAndCall(&camera::CamerasChild::AllocateCaptureDevice,
                                   mCapEngine, GetUUID().get(),
                                   kMaxUniqueIdLength, mCaptureIndex,
                                   aHandle->mPrincipalInfo)) {
         return NS_ERROR_FAILURE;
       }
       mState = kAllocated;
       SetLastCapability(mCapability);
       LOG(("Video device %d allocated", mCaptureIndex));
       break;
 
     case kStarted:
-      if (mCapability != mLastCapability) {
-        camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
-                                mCapEngine, mCaptureIndex);
-        if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture,
-                                    mCapEngine, mCaptureIndex, mCapability,
-                                    this)) {
-          LOG(("StartCapture failed"));
+      {
+        size_t index = mHandleIds.NoIndex;
+        if (aHandle) {
+          mHandleId = aHandle->mId;
+          index = mHandleIds.IndexOf(mHandleId);
+        }
+
+        if (!ChooseCapability(aNewConstraint, aPrefs, aDeviceId, mTargetCapability,
+                              kFitness)) {
+          *aOutBadConstraint = FindBadConstraint(aNewConstraint, *this, aDeviceId);
           return NS_ERROR_FAILURE;
         }
-        SetLastCapability(mCapability);
+
+        if (index != mHandleIds.NoIndex) {
+          mTargetCapabilities[index] = mTargetCapability;
+        }
+
+        if (!ChooseCapability(aNetConstraints, aPrefs, aDeviceId, mCapability,
+                              kFeasibility)) {
+          *aOutBadConstraint = FindBadConstraint(aNetConstraints, *this, aDeviceId);
+          return NS_ERROR_FAILURE;
+        }
+
+        if (mCapability != mLastCapability) {
+          camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
+                                  mCapEngine, mCaptureIndex);
+          if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture,
+                                      mCapEngine, mCaptureIndex, mCapability,
+                                      this)) {
+            LOG(("StartCapture failed"));
+            return NS_ERROR_FAILURE;
+          }
+          SetLastCapability(mCapability);
+        }
       }
       break;
 
     default:
       LOG(("Video device %d in ignored state %d", mCaptureIndex, mState));
       break;
   }
   return NS_OK;
@@ -459,38 +501,41 @@ MediaEngineRemoteVideoSource::NumCapabil
   }
   return num;
 }
 
 bool
 MediaEngineRemoteVideoSource::ChooseCapability(
     const NormalizedConstraints &aConstraints,
     const MediaEnginePrefs &aPrefs,
-    const nsString& aDeviceId)
+    const nsString& aDeviceId,
+    webrtc::CaptureCapability& aCapability,
+    const DistanceCalculation aCalculate)
 {
   AssertIsOnOwningThread();
 
   switch(mMediaSource) {
     case dom::MediaSourceEnum::Screen:
     case dom::MediaSourceEnum::Window:
     case dom::MediaSourceEnum::Application: {
       FlattenedConstraints c(aConstraints);
       // The actual resolution to constrain around is not easy to find ahead of
       // time (and may in fact change over time), so as a hack, we push ideal
       // and max constraints down to desktop_capture_impl.cc and finish the
       // algorithm there.
-      mCapability.width = (c.mWidth.mIdeal.valueOr(0) & 0xffff) << 16 |
-                          (c.mWidth.mMax & 0xffff);
-      mCapability.height = (c.mHeight.mIdeal.valueOr(0) & 0xffff) << 16 |
-                           (c.mHeight.mMax & 0xffff);
-      mCapability.maxFPS = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS));
+      aCapability.width =
+        (c.mWidth.mIdeal.valueOr(0) & 0xffff) << 16 | (c.mWidth.mMax & 0xffff);
+      aCapability.height =
+        (c.mHeight.mIdeal.valueOr(0) & 0xffff) << 16 | (c.mHeight.mMax & 0xffff);
+      aCapability.maxFPS =
+        c.mFrameRate.Clamp(c.mFrameRate.mIdeal.valueOr(aPrefs.mFPS));
       return true;
     }
     default:
-      return MediaEngineCameraVideoSource::ChooseCapability(aConstraints, aPrefs, aDeviceId);
+      return MediaEngineCameraVideoSource::ChooseCapability(aConstraints, aPrefs, aDeviceId, aCapability, aCalculate);
   }
 
 }
 
 void
 MediaEngineRemoteVideoSource::GetCapability(size_t aIndex,
                                             webrtc::CaptureCapability& aOut) const
 {
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.h
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.h
@@ -79,19 +79,22 @@ public:
                   SourceMediaStream* aSource,
                   TrackID aId,
                   StreamTime aDesiredTime,
                   const PrincipalHandle& aPrincipalHandle) override;
   dom::MediaSourceEnum GetMediaSource() const override {
     return mMediaSource;
   }
 
-  bool ChooseCapability(const NormalizedConstraints &aConstraints,
-                        const MediaEnginePrefs &aPrefs,
-                        const nsString& aDeviceId) override;
+  bool ChooseCapability(
+    const NormalizedConstraints &aConstraints,
+    const MediaEnginePrefs &aPrefs,
+    const nsString& aDeviceId,
+    webrtc::CaptureCapability& aCapability,
+    const DistanceCalculation aCalculate) override;
 
   void Refresh(int aIndex);
 
   void Shutdown() override;
 
   bool GetScary() const override { return mScary; }
 
 protected:
@@ -102,16 +105,17 @@ private:
   void Init();
   size_t NumCapabilities() const override;
   void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) const override;
   void SetLastCapability(const webrtc::CaptureCapability& aCapability);
 
   nsresult
   UpdateSingleSource(const AllocationHandle* aHandle,
                      const NormalizedConstraints& aNetConstraints,
+                     const NormalizedConstraints& aNewConstraint,
                      const MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId,
                      const char** aOutBadConstraint) override;
 
   dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
   mozilla::camera::CaptureEngine mCapEngine;
 
   // To only restart camera when needed, we keep track previous settings.
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -562,16 +562,17 @@ public:
 
 protected:
   ~MediaEngineWebRTCMicrophoneSource() {}
 
 private:
   nsresult
   UpdateSingleSource(const AllocationHandle* aHandle,
                      const NormalizedConstraints& aNetConstraints,
+                     const NormalizedConstraints& aNewConstraint,
                      const MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId,
                      const char** aOutBadConstraint) override;
 
   void SetLastPrefs(const MediaEnginePrefs& aPrefs);
 
   // These allocate/configure and release the channel
   bool AllocChannel();
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -271,16 +271,17 @@ bool operator == (const MediaEnginePrefs
 {
   return !memcmp(&a, &b, sizeof(MediaEnginePrefs));
 };
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
     const AllocationHandle* aHandle,
     const NormalizedConstraints& aNetConstraints,
+    const NormalizedConstraints& aNewConstraint, /* Ignored */
     const MediaEnginePrefs& aPrefs,
     const nsString& aDeviceId,
     const char** aOutBadConstraint)
 {
   FlattenedConstraints c(aNetConstraints);
 
   MediaEnginePrefs prefs = aPrefs;
   prefs.mAecOn = c.mEchoCancellation.Get(prefs.mAecOn);
--- a/dom/media/webrtc/MediaTrackConstraints.cpp
+++ b/dom/media/webrtc/MediaTrackConstraints.cpp
@@ -412,16 +412,36 @@ MediaConstraintsHelper::FitnessDistance(
   }
   if (aN == aRange.mIdeal.valueOr(aN)) {
     return 0;
   }
   return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
                             std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
 }
 
+template<class ValueType, class NormalizedRange>
+/* static */ uint32_t
+MediaConstraintsHelper::FeasibilityDistance(ValueType aN,
+                                            const NormalizedRange& aRange)
+{
+  if (aRange.mMin > aN) {
+    return UINT32_MAX;
+  }
+  // We prefer larger resolution because now we support downscaling
+  if (aN == aRange.mIdeal.valueOr(aN)) {
+    return 0;
+  } else if (aN > aRange.mIdeal.value()) {
+    return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
+      std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
+  } else {
+    return (UINT32_MAX / 2) + uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
+      std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
+  }
+}
+
 // Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
 
 /* static */ uint32_t
 MediaConstraintsHelper::FitnessDistance(
     nsString aN,
     const NormalizedConstraintSet::StringRange& aParams)
 {
   if (!aParams.mExact.empty() && aParams.mExact.find(aN) == aParams.mExact.end()) {
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -292,16 +292,18 @@ struct FlattenedConstraints : public Nor
 
 // A helper class for MediaEngines
 
 class MediaConstraintsHelper
 {
 protected:
   template<class ValueType, class NormalizedRange>
   static uint32_t FitnessDistance(ValueType aN, const NormalizedRange& aRange);
+  template<class ValueType, class NormalizedRange>
+  static uint32_t FeasibilityDistance(ValueType aN, const NormalizedRange& aRange);
   static uint32_t FitnessDistance(nsString aN,
       const NormalizedConstraintSet::StringRange& aConstraint);
 
   static uint32_t
   GetMinimumFitnessDistance(const NormalizedConstraintSet &aConstraints,
                             const nsString& aDeviceId);
 
   template<class DeviceType>