Bug 1003274 - Part 2 - clue camera selection in to width/height code. r=jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Wed, 24 Sep 2014 19:00:02 -0400
changeset 236548 c2233448b59c0d2ff25486c33b9c019ac3a600c3
parent 236547 d48c270e62c97747c1093a620f8e4f14a028be20
child 236549 dd1811e791add863f1e80f16566c084b3c269c96
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1003274
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003274 - Part 2 - clue camera selection in to width/height code. r=jesup
content/media/webrtc/MediaEngine.h
content/media/webrtc/MediaEngineDefault.h
content/media/webrtc/MediaEngineGonkVideoSource.h
content/media/webrtc/MediaEngineTabVideoSource.h
content/media/webrtc/MediaEngineWebRTC.h
content/media/webrtc/MediaEngineWebRTCVideo.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -225,16 +225,20 @@ private:
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
+
+  virtual bool SatisfiesConstraintSets(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) = 0;
+
 protected:
   explicit MediaEngineVideoSource(MediaEngineState aState)
     : MediaEngineSource(aState) {}
   MediaEngineVideoSource()
     : MediaEngineSource(kReleased) {}
 };
 
 /**
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -50,16 +50,21 @@ public:
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
+  virtual bool SatisfiesConstraintSets(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
+  {
+    return true;
+  }
 
   virtual bool IsFake() {
     return true;
   }
 
   virtual const MediaSourceType GetMediaSource() {
     return MediaSourceType::Camera;
   }
--- a/content/media/webrtc/MediaEngineGonkVideoSource.h
+++ b/content/media/webrtc/MediaEngineGonkVideoSource.h
@@ -59,16 +59,21 @@ public:
   virtual nsresult Deallocate() MOZ_OVERRIDE;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) MOZ_OVERRIDE;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) MOZ_OVERRIDE;
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream* aSource,
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks& aLastEndTime) MOZ_OVERRIDE;
+  virtual bool SatisfiesConstraintSets(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
+  {
+    return true;
+  }
 
   void OnHardwareStateChange(HardwareState aState);
   void GetRotation();
   bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
   void OnUserError(UserContext aContext, nsresult aError);
   void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
 
   void AllocImpl();
--- a/content/media/webrtc/MediaEngineTabVideoSource.h
+++ b/content/media/webrtc/MediaEngineTabVideoSource.h
@@ -27,16 +27,21 @@ class MediaEngineTabVideoSource : public
     virtual void SetDirectListeners(bool aHasDirectListeners) {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::TrackTicks&);
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
     virtual bool IsFake();
     virtual const MediaSourceType GetMediaSource() {
       return MediaSourceType::Browser;
     }
+    virtual bool SatisfiesConstraintSets(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
+    {
+      return true;
+    }
 
     virtual nsresult TakePhoto(PhotoCallback* aCallback)
     {
       return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     void Draw();
 
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -105,16 +105,19 @@ public:
   }
   virtual nsresult TakePhoto(PhotoCallback* aCallback)
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   void Refresh(int aIndex);
 
+  bool SatisfiesConstraintSets(
+      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
+
 protected:
   ~MediaEngineWebRTCVideoSource() { Shutdown(); }
 
 private:
   // Initialize the needed Video engine interfaces.
   void Init();
   void Shutdown();
 
@@ -123,18 +126,18 @@ private:
   webrtc::ViEBase* mViEBase;
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   int mMinFps; // Min rate we want to accept
   MediaSourceType mMediaSource; // source of media (camera | application | screen)
 
-  static bool SatisfyConstraintSet(const dom::MediaTrackConstraintSet& aConstraints,
-                                   const webrtc::CaptureCapability& aCandidate);
+  static bool SatisfiesConstraintSet(const dom::MediaTrackConstraintSet& aConstraints,
+                                     const webrtc::CaptureCapability& aCandidate);
   void ChooseCapability(const VideoTrackConstraintsN& aConstraints,
                         const MediaEnginePrefs& aPrefs);
 };
 
 class MediaEngineWebRTCAudioSource : public MediaEngineAudioSource,
                                      public webrtc::VoEMediaProcess
 {
 public:
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -143,28 +143,64 @@ MediaEngineWebRTCVideoSource::NotifyPull
     // we've removed or finished the track.
     if (aSource->AppendToTrack(aID, &(segment))) {
       aLastEndTime = target;
     }
   }
 }
 
 /*static*/
-bool MediaEngineWebRTCVideoSource::SatisfyConstraintSet(const MediaTrackConstraintSet &aConstraints,
-                                                        const webrtc::CaptureCapability& aCandidate) {
+bool
+MediaEngineWebRTCVideoSource::SatisfiesConstraintSet(const MediaTrackConstraintSet &aConstraints,
+                                                     const webrtc::CaptureCapability& aCandidate) {
   if (!MediaEngineCameraVideoSource::IsWithin(aCandidate.width, aConstraints.mWidth) ||
       !MediaEngineCameraVideoSource::IsWithin(aCandidate.height, aConstraints.mHeight)) {
     return false;
   }
   if (!MediaEngineCameraVideoSource::IsWithin(aCandidate.maxFPS, aConstraints.mFrameRate)) {
     return false;
   }
   return true;
 }
 
+typedef nsTArray<uint8_t> CapabilitySet;
+
+// SatisfiesConstraintSets (plural) answers for the capture device as a whole
+// whether it can satisfy an accumulated number of capabilitySets.
+
+bool
+MediaEngineWebRTCVideoSource::SatisfiesConstraintSets(
+    const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
+{
+  NS_ConvertUTF16toUTF8 uniqueId(mUniqueId);
+  int num = mViECapture->NumberOfCapabilities(uniqueId.get(), kMaxUniqueIdLength);
+  if (num <= 0) {
+    return true;
+  }
+
+  CapabilitySet candidateSet;
+  for (int i = 0; i < num; i++) {
+    candidateSet.AppendElement(i);
+  }
+
+  for (size_t j = 0; j < aConstraintSets.Length(); j++) {
+    for (size_t i = 0; i < candidateSet.Length();  ) {
+      webrtc::CaptureCapability cap;
+      mViECapture->GetCaptureCapability(uniqueId.get(), kMaxUniqueIdLength,
+                                        candidateSet[i], cap);
+      if (!SatisfiesConstraintSet(*aConstraintSets[j], cap)) {
+        candidateSet.RemoveElementAt(i);
+      } else {
+        ++i;
+      }
+    }
+  }
+  return !!candidateSet.Length();
+}
+
 void
 MediaEngineWebRTCVideoSource::ChooseCapability(
     const VideoTrackConstraintsN &aConstraints,
     const MediaEnginePrefs &aPrefs)
 {
   NS_ConvertUTF16toUTF8 uniqueId(mUniqueId);
   int num = mViECapture->NumberOfCapabilities(uniqueId.get(), kMaxUniqueIdLength);
   if (num <= 0) {
@@ -172,50 +208,48 @@ MediaEngineWebRTCVideoSource::ChooseCapa
     return GuessCapability(aConstraints, aPrefs);
   }
 
   // The rest is the full algorithm for cameras that can list their capabilities.
 
   LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
        aPrefs.mWidth, aPrefs.mHeight, aPrefs.mFPS, aPrefs.mMinFPS));
 
-  typedef nsTArray<uint8_t> SourceSet;
-
-  SourceSet candidateSet;
+  CapabilitySet candidateSet;
   for (int i = 0; i < num; i++) {
     candidateSet.AppendElement(i);
   }
 
   // Pick among capabilities: First apply required constraints.
 
   for (uint32_t i = 0; i < candidateSet.Length();) {
     webrtc::CaptureCapability cap;
     mViECapture->GetCaptureCapability(uniqueId.get(), kMaxUniqueIdLength,
                                       candidateSet[i], cap);
-    if (!SatisfyConstraintSet(aConstraints.mRequired, cap)) {
+    if (!SatisfiesConstraintSet(aConstraints.mRequired, cap)) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
-  SourceSet tailSet;
+  CapabilitySet tailSet;
 
   // Then apply advanced (formerly known as optional) constraints.
 
   if (aConstraints.mAdvanced.WasPassed()) {
     auto &array = aConstraints.mAdvanced.Value();
 
     for (uint32_t i = 0; i < array.Length(); i++) {
-      SourceSet rejects;
+      CapabilitySet rejects;
       for (uint32_t j = 0; j < candidateSet.Length();) {
         webrtc::CaptureCapability cap;
         mViECapture->GetCaptureCapability(uniqueId.get(), kMaxUniqueIdLength,
                                           candidateSet[j], cap);
-        if (!SatisfyConstraintSet(array[i], cap)) {
+        if (!SatisfiesConstraintSet(array[i], cap)) {
           rejects.AppendElement(candidateSet[j]);
           candidateSet.RemoveElementAt(j);
         } else {
           ++j;
         }
       }
       (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
     }
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -416,41 +416,47 @@ VideoDevice::VideoDevice(MediaEngineVide
 /**
  * Helper functions that implement the constraints algorithm from
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
  */
 
 // Reminder: add handling for new constraints both here and in GetSources below!
 
 bool
-VideoDevice::SatisfyConstraintSet(const MediaTrackConstraintSet &aConstraints)
+VideoDevice::SatisfiesConstraintSets(
+    const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
-  if (aConstraints.mFacingMode.WasPassed()) {
+  // Interrogate device-inherent properties first.
+  for (size_t i = 0; i < aConstraintSets.Length(); i++) {
+    auto& c = *aConstraintSets[i];
+    if (c.mFacingMode.WasPassed()) {
+      nsString s;
+      GetFacingMode(s);
+      if (!s.EqualsASCII(dom::VideoFacingModeEnumValues::strings[
+          static_cast<uint32_t>(c.mFacingMode.Value())].value)) {
+        return false;
+      }
+    }
     nsString s;
-    GetFacingMode(s);
-    if (!s.EqualsASCII(dom::VideoFacingModeEnumValues::strings[
-        uint32_t(aConstraints.mFacingMode.Value())].value)) {
+    GetMediaSource(s);
+    if (!s.EqualsASCII(dom::MediaSourceEnumValues::strings[
+        static_cast<uint32_t>(c.mMediaSource)].value)) {
       return false;
     }
   }
-  nsString s;
-  GetMediaSource(s);
-  if (!s.EqualsASCII(dom::MediaSourceEnumValues::strings[
-      uint32_t(aConstraints.mMediaSource)].value)) {
-    return false;
-  }
-  // TODO: Add more video-specific constraints
-  return true;
+  // Forward request to underlying object to interrogate per-mode capabilities.
+  return GetSource()->SatisfiesConstraintSets(aConstraintSets);
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
 bool
-AudioDevice::SatisfyConstraintSet(const MediaTrackConstraintSet &aConstraints)
+AudioDevice::SatisfiesConstraintSets(
+    const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
   // TODO: Add audio-specific constraints
   return true;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
 {
@@ -955,26 +961,32 @@ static void
   // Apply constraints to the list of sources.
 
   auto& c = aConstraints;
   if (c.mUnsupportedRequirement) {
     // Check upfront the names of required constraints that are unsupported for
     // this media-type. The spec requires these to fail, so getting them out of
     // the way early provides a necessary invariant for the remaining algorithm
     // which maximizes code-reuse by ignoring constraints of the other type
-    // (specifically, SatisfyConstraintSet is reused for the advanced algorithm
+    // (specifically, SatisfiesConstraintSets is reused for the advanced algorithm
     // where the spec requires it to ignore constraints of the other type)
     return;
   }
 
   // Now on to the actual algorithm: First apply required constraints.
 
+  // Stack constraintSets that pass, starting with the required one, because the
+  // whole stack must be re-satisfied each time a capability-set is ruled out
+  // (this avoids storing state and pushing algorithm into the lower-level code).
+  nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
+  aggregateConstraints.AppendElement(&c.mRequired);
+
   for (uint32_t i = 0; i < candidateSet.Length();) {
     // Overloading instead of template specialization keeps things local
-    if (!candidateSet[i]->SatisfyConstraintSet(c.mRequired)) {
+    if (!candidateSet[i]->SatisfiesConstraintSets(aggregateConstraints)) {
       candidateSet.RemoveElementAt(i);
     } else {
       ++i;
     }
   }
 
   // TODO(jib): Proper non-ordered handling of nonrequired constraints (907352)
   //
@@ -1003,26 +1015,30 @@ static void
   // the wrong one (UX-fail). Webpage-preferred devices will be listed first.
 
   SourceSet tailSet;
 
   if (c.mAdvanced.WasPassed()) {
     auto &array = c.mAdvanced.Value();
 
     for (int i = 0; i < int(array.Length()); i++) {
+      aggregateConstraints.AppendElement(&array[i]);
       SourceSet rejects;
       for (uint32_t j = 0; j < candidateSet.Length();) {
-        if (!candidateSet[j]->SatisfyConstraintSet(array[i])) {
+        if (!candidateSet[j]->SatisfiesConstraintSets(aggregateConstraints)) {
           rejects.AppendElement(candidateSet[j]);
           candidateSet.RemoveElementAt(j);
         } else {
           ++j;
         }
       }
       (candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
+      if (!candidateSet.Length()) {
+        aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
+      }
     }
   }
 
   // TODO: Proper non-ordered handling of nonrequired constraints (Bug 907352)
 
   aResult.MoveElementsFrom(candidateSet);
   aResult.MoveElementsFrom(tailSet);
 }
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -516,28 +516,30 @@ protected:
 class VideoDevice : public MediaDevice
 {
 public:
   typedef MediaEngineVideoSource Source;
 
   explicit VideoDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
-  bool SatisfyConstraintSet(const dom::MediaTrackConstraintSet &aConstraints);
+  bool SatisfiesConstraintSets(
+    const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
 };
 
 class AudioDevice : public MediaDevice
 {
 public:
   typedef MediaEngineAudioSource Source;
 
   explicit AudioDevice(Source* aSource);
   NS_IMETHOD GetType(nsAString& aType);
   Source* GetSource();
-  bool SatisfyConstraintSet(const dom::MediaTrackConstraintSet &aConstraints);
+  bool SatisfiesConstraintSets(
+    const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
 };
 
 // we could add MediaManager if needed
 typedef void (*WindowListenerCallback)(MediaManager *aThis,
                                        uint64_t aWindowID,
                                        StreamListeners *aListeners,
                                        void *aData);