Bug 1388219 - Set the highest resolution requested from multiple content processes. r?jib draft
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Tue, 12 Sep 2017 10:24:01 +0800
changeset 663119 a66b75a159ec5fd203778dc031f407653061741d
parent 662738 bda524beac249b64aa36016800502a34073bf35a
child 663120 025ecccedd3a24892a6199f943136aa2e3970a2d
push id79331
push userbmo:mchiang@mozilla.com
push dateTue, 12 Sep 2017 17:23:50 +0000
reviewersjib
bugs1388219
milestone57.0a1
Bug 1388219 - Set the highest resolution requested from multiple content processes. r?jib MozReview-Commit-ID: Ezy5ZrtHswi
dom/media/systemservices/CamerasParent.cpp
dom/media/systemservices/CamerasParent.h
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -34,16 +34,39 @@
 mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
 #define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
 #define LOG_VERBOSE(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Verbose, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
 
 namespace mozilla {
 namespace camera {
 
+std::map<uint32_t, const char *> sDeviceUniqueIDs;
+std::map<uint32_t, webrtc::VideoCaptureCapability> sAllRequestedCapabilities;
+
+uint32_t
+ResolutionFeasibilityDistance(int32_t candidate, int32_t requested)
+{
+  // The purpose of this function is to find a smallest resolution
+  // which is larger than all requested capabilities.
+  // Then we can use down-scaling to fulfill each request.
+  if (candidate >= requested) {
+    return (candidate - requested) * 1000 / std::max(candidate, requested);
+  } else {
+    return (UINT32_MAX / 2) + (requested - candidate) *
+      1000 / std::max(candidate, requested);
+  }
+}
+
+uint32_t
+FeasibilityDistance(int32_t candidate, int32_t requested)
+{
+  return std::abs(candidate - requested) * 1000 / std::max(candidate, requested);
+}
+
 // 3 threads are involved in this code:
 // - the main thread for some setups, and occassionally for video capture setup
 //   calls that don't work correctly elsewhere.
 // - the IPC thread on which PBackground is running and which receives and
 //   sends messages
 // - a thread which will execute the actual (possibly slow) camera access
 //   called "VideoCapture". On Windows this is a thread with an event loop
 //   suitable for UI access.
@@ -543,16 +566,27 @@ CamerasParent::RecvGetCaptureCapability(
   RefPtr<Runnable> webrtc_runnable =
     media::NewRunnableFrom([self, unique_id, aCapEngine, num]() -> nsresult {
       webrtc::VideoCaptureCapability webrtcCaps;
       int error = -1;
       if (auto engine = self->EnsureInitialized(aCapEngine)) {
         if (auto devInfo = engine->GetOrCreateVideoCaptureDeviceInfo()){
           error = devInfo->GetCapability(unique_id.get(), num, webrtcCaps);
         }
+
+        if (!error && aCapEngine == CameraEngine) {
+          auto iter = self->mAllCandidateCapabilities.find(unique_id);
+          if (iter == self->mAllCandidateCapabilities.end()) {
+            std::map<uint32_t, webrtc::VideoCaptureCapability> candidateCapabilities;
+            candidateCapabilities.emplace(num, webrtcCaps);
+            self->mAllCandidateCapabilities.emplace(nsCString(unique_id), candidateCapabilities);
+          } else {
+            (iter->second).emplace(num, webrtcCaps);
+          }
+        }
       }
       RefPtr<nsIRunnable> ipc_runnable =
         media::NewRunnableFrom([self, webrtcCaps, error]() -> nsresult {
           if (self->IsShuttingDown()) {
             return NS_ERROR_FAILURE;
           }
           VideoCaptureCapability capCap(webrtcCaps.width,
                                    webrtcCaps.height,
@@ -803,33 +837,83 @@ CamerasParent::RecvStartCapture(const Ca
       CallbackHelper** cbh;
       VideoEngine* engine = nullptr;
       int error = -1;
       if (self->EnsureInitialized(aCapEngine)) {
         cbh = self->mCallbacks.AppendElement(
           new CallbackHelper(static_cast<CaptureEngine>(aCapEngine), capnum, self));
 
         engine = self->mEngines[aCapEngine];
-        engine->WithEntry(capnum, [&engine, &error, &ipcCaps, &cbh](VideoEngine::CaptureEntry& cap) {
-          error = 0;
+        std::map<nsCString, std::map<uint32_t, webrtc::VideoCaptureCapability>>*
+          allCandidateCapabilities = &(self->mAllCandidateCapabilities);
+        engine->WithEntry(capnum,
+          [&capnum, &aCapEngine, &engine, &error, &ipcCaps, &cbh, &allCandidateCapabilities]
+          (VideoEngine::CaptureEntry& cap) {
           webrtc::VideoCaptureCapability capability;
           capability.width = ipcCaps.width();
           capability.height = ipcCaps.height();
           capability.maxFPS = ipcCaps.maxFPS();
           capability.expectedCaptureDelay = ipcCaps.expectedCaptureDelay();
           capability.rawType = static_cast<webrtc::RawVideoType>(ipcCaps.rawType());
           capability.codecType = static_cast<webrtc::VideoCodecType>(ipcCaps.codecType());
           capability.interlaced = ipcCaps.interlaced();
 
-          if (!error) {
-            error = cap.VideoCapture()->StartCapture(capability);
+          if (aCapEngine == CameraEngine) {
+            auto deviceUniqueID = sDeviceUniqueIDs.find(capnum);
+            MOZ_ASSERT(deviceUniqueID == sDeviceUniqueIDs.end());
+            Unused <<  deviceUniqueID;
+            sDeviceUniqueIDs.emplace(capnum, cap.VideoCapture()->CurrentDeviceName());
+            sAllRequestedCapabilities.emplace(capnum, capability);
+
+            for (const auto &it : sDeviceUniqueIDs) {
+              if (strcmp(it.second, cap.VideoCapture()->CurrentDeviceName()) == 0) {
+                capability.width = std::max(
+                  capability.width, sAllRequestedCapabilities[it.first].width);
+                capability.height = std::max(
+                  capability.height, sAllRequestedCapabilities[it.first].height);
+                capability.maxFPS = std::max(
+                  capability.maxFPS, sAllRequestedCapabilities[it.first].maxFPS);
+              }
+            }
+
+            auto candidateCapabilities = allCandidateCapabilities->find(
+              nsCString(cap.VideoCapture()->CurrentDeviceName()));
+            MOZ_ASSERT(candidateCapabilities != allCandidateCapabilities->end());
+            MOZ_ASSERT(candidateCapabilities->second.size() > 0);
+            int32_t minIdx = -1;
+            uint64_t minDistance = UINT64_MAX;
+
+            for (auto & candidateCapability : candidateCapabilities->second) {
+              if (candidateCapability.second.rawType != capability.rawType) {
+                continue;
+              }
+              // The first priority is finding a suitable resolution.
+              // So here we raise the weight of width and height
+              uint64_t distance =
+                uint64_t(ResolutionFeasibilityDistance(
+                  candidateCapability.second.width, capability.width)) +
+                uint64_t(ResolutionFeasibilityDistance(
+                  candidateCapability.second.height, capability.height)) +
+                uint64_t(FeasibilityDistance(
+                  candidateCapability.second.maxFPS, capability.maxFPS));
+              if (distance < minDistance) {
+                minIdx = candidateCapability.first;;
+                minDistance = distance;
+              }
+            }
+            MOZ_ASSERT(minIdx != -1);
+            capability = candidateCapabilities->second[minIdx];
           }
+
+          error = cap.VideoCapture()->StartCapture(capability);
+
           if (!error) {
             engine->Startup();
-            cap.VideoCapture()->RegisterCaptureDataCallback(static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(*cbh));
+            cap.VideoCapture()->RegisterCaptureDataCallback(
+              static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(*cbh));
           }
         });
       }
       RefPtr<nsIRunnable> ipc_runnable =
         media::NewRunnableFrom([self, error]() -> nsresult {
           if (self->IsShuttingDown()) {
             return NS_ERROR_FAILURE;
           }
@@ -848,20 +932,25 @@ CamerasParent::RecvStartCapture(const Ca
   return IPC_OK();
 }
 
 void
 CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
                            const int& capnum)
 {
   if (auto engine = EnsureInitialized(aCapEngine)) {
-    engine->WithEntry(capnum,[](VideoEngine::CaptureEntry& cap){
+    engine->WithEntry(capnum,[&capnum, &aCapEngine](VideoEngine::CaptureEntry& cap){
       if (cap.VideoCapture()) {
         cap.VideoCapture()->StopCapture();
         cap.VideoCapture()->DeRegisterCaptureDataCallback();
+
+        if (aCapEngine == CameraEngine) {
+          sDeviceUniqueIDs.erase(capnum);
+          sAllRequestedCapabilities.erase(capnum);
+        }
       }
     });
     // we're removing elements, iterate backwards
     for (size_t i = mCallbacks.Length(); i > 0; i--) {
       if (mCallbacks[i - 1]->mCapEngine == aCapEngine &&
           mCallbacks[i - 1]->mStreamId == (uint32_t)capnum) {
         delete mCallbacks[i - 1];
         mCallbacks.RemoveElementAt(i - 1);
@@ -984,16 +1073,17 @@ CamerasParent::CamerasParent()
       self->mThreadMonitor.NotifyAll();
       return NS_OK;
     });
   NS_DispatchToMainThread(threadStart);
 }
 
 CamerasParent::~CamerasParent()
 {
+  mAllCandidateCapabilities.clear();
   LOG(("~CamerasParent: %p", this));
 
 #ifdef DEBUG
   // Verify we have shut down the webrtc engines, this is
   // supposed to happen in ActorDestroy.
   // That runnable takes a ref to us, so it must have finished
   // by the time we get here.
   for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -152,16 +152,18 @@ protected:
 
   // Shutdown handling
   bool mChildIsAlive;
   bool mDestroyed;
   // Above 2 are PBackground only, but this is potentially
   // read cross-thread.
   mozilla::Atomic<bool> mWebRTCAlive;
   nsTArray<RefPtr<InputObserver>> mObservers;
+  std::map<nsCString, std::map<uint32_t, webrtc::VideoCaptureCapability>>
+    mAllCandidateCapabilities;
 };
 
 PCamerasParent* CreateCamerasParent();
 
 } // namespace camera
 } // namespace mozilla
 
 #endif  // mozilla_CameraParent_h