Bug 1286429 - Fire fake devicechange event in Camera IPC thread for mochitest; r?jib draft
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Tue, 09 Aug 2016 16:37:04 +0800
changeset 401603 e502ad0c379ca1a2a45e01ebbac06239b3ab9c30
parent 401602 36e633a7f81d1e03bde037da091cd94197677752
child 401604 1e063af36bd2ec245b445cf9a357ae82e07d05d4
push id26510
push usermchiang@mozilla.com
push dateWed, 17 Aug 2016 07:54:22 +0000
reviewersjib
bugs1286429
milestone51.0a1
Bug 1286429 - Fire fake devicechange event in Camera IPC thread for mochitest; r?jib MozReview-Commit-ID: 9sDLnMGh3xL
dom/media/MediaManager.cpp
dom/media/systemservices/CamerasChild.cpp
dom/media/systemservices/CamerasChild.h
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/media/webrtc/MediaEngineWebRTC.h
modules/libpref/init/all.js
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1704,16 +1704,17 @@ MediaManager::MediaManager()
   mPrefs.mHeight       = 0; // adaptive default
   mPrefs.mFPS          = MediaEngine::DEFAULT_VIDEO_FPS;
   mPrefs.mMinFPS       = MediaEngine::DEFAULT_VIDEO_MIN_FPS;
   mPrefs.mAecOn        = false;
   mPrefs.mAgcOn        = false;
   mPrefs.mNoiseOn      = false;
   mPrefs.mExtendedFilter = true;
   mPrefs.mDelayAgnostic = true;
+  mPrefs.mFakeDeviceChangeEventOn = false;
 #ifdef MOZ_WEBRTC
   mPrefs.mAec          = webrtc::kEcUnchanged;
   mPrefs.mAgc          = webrtc::kAgcUnchanged;
   mPrefs.mNoise        = webrtc::kNsUnchanged;
 #else
   mPrefs.mAec          = 0;
   mPrefs.mAgc          = 0;
   mPrefs.mNoise        = 0;
@@ -1802,16 +1803,17 @@ MediaManager::Get() {
 #ifdef MOZ_WEBRTC
       prefs->AddObserver("media.getusermedia.aec_enabled", sSingleton, false);
       prefs->AddObserver("media.getusermedia.aec", sSingleton, false);
       prefs->AddObserver("media.getusermedia.agc_enabled", sSingleton, false);
       prefs->AddObserver("media.getusermedia.agc", sSingleton, false);
       prefs->AddObserver("media.getusermedia.noise_enabled", sSingleton, false);
       prefs->AddObserver("media.getusermedia.noise", sSingleton, false);
       prefs->AddObserver("media.getusermedia.playout_delay", sSingleton, false);
+      prefs->AddObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", sSingleton, false);
 #endif
     }
 
     // Prepare async shutdown
 
     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
 
     class Blocker : public media::ShutdownBlocker
@@ -1994,19 +1996,22 @@ bool MediaManager::IsPrivateBrowsing(nsP
 {
   nsCOMPtr<nsIDocument> doc = window->GetDoc();
   nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
   return loadContext && loadContext->UsePrivateBrowsing();
 }
 
 int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
 {
-  MediaManager::PostTask(NewTaskFrom([]() {
+  bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
+  MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
     RefPtr<MediaManager> manager = MediaManager_GetInstance();
     manager->GetBackend(0)->AddDeviceChangeCallback(manager);
+    if (fakeDeviceChangeEventOn)
+      manager->GetBackend(0)->SetFakeDeviceChangeEvents();
   }));
 
   return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
 }
 
 void MediaManager::OnDeviceChange() {
   RefPtr<MediaManager> self(this);
   NS_DispatchToMainThread(media::NewRunnableFrom([self,this]() mutable {
@@ -2870,16 +2875,17 @@ MediaManager::GetPrefs(nsIPrefBranch *aB
   GetPrefBool(aBranch, "media.getusermedia.agc_enabled", aData, &mPrefs.mAgcOn);
   GetPrefBool(aBranch, "media.getusermedia.noise_enabled", aData, &mPrefs.mNoiseOn);
   GetPref(aBranch, "media.getusermedia.aec", aData, &mPrefs.mAec);
   GetPref(aBranch, "media.getusermedia.agc", aData, &mPrefs.mAgc);
   GetPref(aBranch, "media.getusermedia.noise", aData, &mPrefs.mNoise);
   GetPref(aBranch, "media.getusermedia.playout_delay", aData, &mPrefs.mPlayoutDelay);
   GetPrefBool(aBranch, "media.getusermedia.aec_extended_filter", aData, &mPrefs.mExtendedFilter);
   GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData, &mPrefs.mDelayAgnostic);
+  GetPrefBool(aBranch, "media.ondevicechange.fakeDeviceChangeEvent.enabled", aData, &mPrefs.mFakeDeviceChangeEventOn);
 #endif
   GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData, &mPrefs.mFullDuplex);
 }
 
 void
 MediaManager::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -2906,16 +2912,17 @@ MediaManager::Shutdown()
 #ifdef MOZ_WEBRTC
     prefs->RemoveObserver("media.getusermedia.aec_enabled", this);
     prefs->RemoveObserver("media.getusermedia.aec", this);
     prefs->RemoveObserver("media.getusermedia.agc_enabled", this);
     prefs->RemoveObserver("media.getusermedia.agc", this);
     prefs->RemoveObserver("media.getusermedia.noise_enabled", this);
     prefs->RemoveObserver("media.getusermedia.noise", this);
     prefs->RemoveObserver("media.getusermedia.playout_delay", this);
+    prefs->RemoveObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", this);
 #endif
     prefs->RemoveObserver("media.navigator.audio.full_duplex", this);
   }
 
   // Close off any remaining active windows.
   GetActiveWindows()->Clear();
   mActiveCallbacks.Clear();
   mCallIds.Clear();
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -20,30 +20,61 @@
 #include "nsThreadUtils.h"
 
 #undef LOG
 #undef LOG_ENABLED
 mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
 #define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)
 
+#define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000
+#define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30
+
 namespace mozilla {
 namespace camera {
 
 CamerasSingleton::CamerasSingleton()
   : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
     mCameras(nullptr),
     mCamerasChildThread(nullptr) {
   LOG(("CamerasSingleton: %p", this));
 }
 
 CamerasSingleton::~CamerasSingleton() {
   LOG(("~CamerasSingleton: %p", this));
 }
 
+class FakeOnDeviceChangeEventRunnable : public Runnable
+{
+public:
+  explicit FakeOnDeviceChangeEventRunnable(uint8_t counter)
+    : mCounter(counter) {}
+
+  NS_IMETHOD Run() override
+  {
+    OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+
+    CamerasChild* child = CamerasSingleton::Child();
+    if (child) {
+      child->OnDeviceChange();
+
+      if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) {
+        RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter);
+        CamerasSingleton::Thread()->DelayedDispatch(evt.forget(),
+          FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS);
+      }
+    }
+
+    return NS_OK;
+  }
+
+private:
+  uint8_t mCounter;
+};
+
 class InitializeIPCThread : public Runnable
 {
 public:
   InitializeIPCThread()
     : mCamerasChild(nullptr) {}
 
   NS_IMETHOD Run() override {
     // Try to get the PBackground handle
@@ -587,16 +618,29 @@ CamerasChild::RecvDeliverFrame(const int
 
 bool
 CamerasChild::RecvDeviceChange()
 {
   this->OnDeviceChange();
   return true;
 }
 
+int
+CamerasChild::SetFakeDeviceChangeEvents()
+{
+  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
+
+  // To simulate the devicechange event in mochitest,
+  // we fire a fake devicechange event in Camera IPC thread periodically
+  RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
+  CamerasSingleton::Thread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
+
+  return 0;
+}
+
 bool
 CamerasChild::RecvFrameSizeChange(const int& capEngine,
                                   const int& capId,
                                   const int& w, const int& h)
 {
   LOG((__PRETTY_FUNCTION__));
   MutexAutoLock lock(mCallbackMutex);
   CaptureEngine capEng = static_cast<CaptureEngine>(capEngine);
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -155,16 +155,17 @@ public:
   // these are the actual callbacks with data
   virtual bool RecvDeliverFrame(const int&, const int&, mozilla::ipc::Shmem&&,
                                 const size_t&, const uint32_t&, const int64_t&,
                                 const int64_t&) override;
   virtual bool RecvFrameSizeChange(const int&, const int&,
                                    const int& w, const int& h) override;
 
   virtual bool RecvDeviceChange() override;
+  int SetFakeDeviceChangeEvents();
 
   // these are response messages to our outgoing requests
   virtual bool RecvReplyNumberOfCaptureDevices(const int&) override;
   virtual bool RecvReplyNumberOfCapabilities(const int&) override;
   virtual bool RecvReplyAllocateCaptureDevice(const int&) override;
   virtual bool RecvReplyGetCaptureCapability(const CaptureCapability& capability) override;
   virtual bool RecvReplyGetCaptureDevice(const nsCString& device_name,
                                          const nsCString& device_id) override;
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -71,16 +71,18 @@ public:
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
                                      nsTArray<RefPtr<MediaEngineAudioSource> >*) = 0;
 
   virtual void Shutdown() = 0;
 
+  virtual void SetFakeDeviceChangeEvents() {}
+
 protected:
   virtual ~MediaEngine() {}
 };
 
 /**
  * Video source and friends.
  */
 class MediaEnginePrefs {
@@ -96,16 +98,17 @@ public:
     , mNoiseOn(false)
     , mAec(0)
     , mAgc(0)
     , mNoise(0)
     , mPlayoutDelay(0)
     , mFullDuplex(false)
     , mExtendedFilter(false)
     , mDelayAgnostic(false)
+    , mFakeDeviceChangeEventOn(false)
   {}
 
   int32_t mWidth;
   int32_t mHeight;
   int32_t mFPS;
   int32_t mMinFPS;
   int32_t mFreq; // for test tones (fake:true)
   bool mAecOn;
@@ -113,16 +116,17 @@ public:
   bool mNoiseOn;
   int32_t mAec;
   int32_t mAgc;
   int32_t mNoise;
   int32_t mPlayoutDelay;
   bool mFullDuplex;
   bool mExtendedFilter;
   bool mDelayAgnostic;
+  bool mFakeDeviceChangeEventOn;
 
   // mWidth and/or mHeight may be zero (=adaptive default), so use functions.
 
   int32_t GetWidth(bool aHD = false) const {
     return mWidth? mWidth : (mHeight?
                              (mHeight * GetDefWidth(aHD)) / GetDefHeight(aHD) :
                              GetDefWidth(aHD));
   }
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -127,16 +127,23 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   gFarendObserver = new AudioOutputObserver();
 
   camera::GetChildAndCall(
     &camera::CamerasChild::AddDeviceChangeCallback,
     this);
 }
 
 void
+MediaEngineWebRTC::SetFakeDeviceChangeEvents()
+{
+  camera::GetChildAndCall(
+    &camera::CamerasChild::SetFakeDeviceChangeEvents);
+}
+
+void
 MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
                                          nsTArray<RefPtr<MediaEngineVideoSource> >* aVSources)
 {
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (aMediaSource != dom::MediaSourceEnum::Camera) {
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -566,16 +566,18 @@ private:
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
   typedef MediaEngine Super;
 public:
   explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
 
+  virtual void SetFakeDeviceChangeEvents() override;
+
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
   void Shutdown() override;
 
   // Returns whether the host supports duplex audio stream.
   bool SupportsDuplex();
 
   void EnumerateVideoDevices(dom::MediaSourceEnum,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4792,16 +4792,19 @@ pref("dom.w3c_touch_events.enabled", 2);
 pref("dom.w3c_pointer_events.enabled", false);
 
 // W3C draft ImageCapture API
 pref("dom.imagecapture.enabled", false);
 
 // W3C MediaDevices devicechange event
 pref("media.ondevicechange.enabled", false);
 
+// W3C MediaDevices devicechange fake event
+pref("media.ondevicechange.fakeDeviceChangeEvent.enabled", false);
+
 // W3C touch-action css property (related to touch and pointer events)
 // Note that we turn this on even on platforms/configurations where touch
 // events are not supported (e.g. OS X, or Windows with e10s disabled). For
 // those platforms we don't handle touch events anyway so it's conceptually
 // a no-op.
 #ifdef NIGHTLY_BUILD
 pref("layout.css.touch_action.enabled", true);
 #else