Bug 1286429 - implement mediaDevices.ondevicechange for Mac OSX; r=jib,smaug
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Fri, 12 Aug 2016 01:04:49 +0800
changeset 309758 df5bd6813b75684d50ddf76be46a546589831d49
parent 309757 16ed9b5cf1e8cc47297dd6b3d9d2fa908ebc6dd5
child 309759 5022a33fd3e950aafb09ac6dc64219ce71063857
push id31455
push userryanvm@gmail.com
push dateWed, 17 Aug 2016 19:55:02 +0000
treeherderautoland@5ff0ce53fccf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib, smaug
bugs1286429
milestone51.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 1286429 - implement mediaDevices.ondevicechange for Mac OSX; r=jib,smaug MozReview-Commit-ID: D1Jr6I4qPyr
dom/base/nsGkAtomList.h
dom/media/MediaDevices.cpp
dom/media/MediaDevices.h
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/systemservices/CamerasChild.cpp
dom/media/systemservices/CamerasChild.h
dom/media/systemservices/CamerasParent.cpp
dom/media/systemservices/CamerasParent.h
dom/media/systemservices/DeviceChangeCallback.h
dom/media/systemservices/PCameras.ipdl
dom/media/systemservices/moz.build
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/webidl/MediaDevices.webidl
media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h
media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info.mm
media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.h
media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.mm
media/webrtc/trunk/webrtc/video_engine/include/vie_capture.h
media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc
media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.h
media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc
media/webrtc/trunk/webrtc/video_engine/vie_input_manager.h
modules/libpref/init/all.js
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -1951,16 +1951,19 @@ GK_ATOM(onuserproximity, "onuserproximit
 
 // light sensor support
 GK_ATOM(ondevicelight, "ondevicelight")
 
 // Audio channel events
 GK_ATOM(onmozinterruptbegin, "onmozinterruptbegin")
 GK_ATOM(onmozinterruptend, "onmozinterruptend")
 
+// MediaDevices device change event
+GK_ATOM(ondevicechange, "ondevicechange")
+
 //---------------------------------------------------------------------------
 // Special atoms
 //---------------------------------------------------------------------------
 
 // Node types
 GK_ATOM(cdataTagName, "#cdata-section")
 GK_ATOM(commentTagName, "#comment")
 GK_ATOM(documentNodeName, "#document")
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -174,16 +174,82 @@ MediaDevices::EnumerateDevices(ErrorResu
 }
 
 NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN(MediaDevices)
   NS_INTERFACE_MAP_ENTRY(MediaDevices)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
+void
+MediaDevices::OnDeviceChange()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsresult rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv))
+    return;
+
+  if (!(MediaManager::Get()->IsActivelyCapturingOrHasAPermission(GetOwner()->WindowID()) ||
+    Preferences::GetBool("media.navigator.permission.disabled", false))) {
+    return;
+  }
+
+  DispatchTrustedEvent(NS_LITERAL_STRING("devicechange"));
+}
+
+mozilla::dom::EventHandlerNonNull*
+MediaDevices::GetOndevicechange()
+{
+  if (NS_IsMainThread()) {
+    return GetEventHandler(nsGkAtoms::ondevicechange, EmptyString());
+  }
+  return GetEventHandler(nullptr, NS_LITERAL_STRING("devicechange"));
+}
+
+void
+MediaDevices::SetOndevicechange(mozilla::dom::EventHandlerNonNull* aCallback)
+{
+  if (NS_IsMainThread()) {
+    SetEventHandler(nsGkAtoms::ondevicechange, EmptyString(), aCallback);
+  } else {
+    SetEventHandler(nullptr, NS_LITERAL_STRING("devicechange"), aCallback);
+  }
+
+  MediaManager::Get()->AddDeviceChangeCallback(this);
+}
+
+nsresult
+MediaDevices::AddEventListener(const nsAString& aType,
+  nsIDOMEventListener* aListener,
+  bool aUseCapture, bool aWantsUntrusted,
+  uint8_t optional_argc)
+{
+  MediaManager::Get()->AddDeviceChangeCallback(this);
+
+  return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
+    aUseCapture,
+    aWantsUntrusted,
+    optional_argc);
+}
+
+void
+MediaDevices::AddEventListener(const nsAString& aType,
+  dom::EventListener* aListener,
+  const dom::AddEventListenerOptionsOrBoolean& aOptions,
+  const dom::Nullable<bool>& aWantsUntrusted,
+  ErrorResult& aRv)
+{
+  MediaManager::Get()->AddDeviceChangeCallback(this);
+
+  return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
+    aOptions,
+    aWantsUntrusted,
+    aRv);
+}
+
 JSObject*
 MediaDevices::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MediaDevicesBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaDevices.h
+++ b/dom/media/MediaDevices.h
@@ -5,29 +5,31 @@
 #ifndef mozilla_dom_MediaDevices_h
 #define mozilla_dom_MediaDevices_h
 
 #include "mozilla/ErrorResult.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPIDOMWindow.h"
+#include "DeviceChangeCallback.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 struct MediaStreamConstraints;
 struct MediaTrackSupportedConstraints;
 
 #define MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID \
 { 0x2f784d8a, 0x7485, 0x4280, \
  { 0x9a, 0x36, 0x74, 0xa4, 0xd6, 0x71, 0xa6, 0xc8 } }
 
 class MediaDevices final : public DOMEventTargetHelper
+                          ,public DeviceChangeCallback
 {
 public:
   explicit MediaDevices(nsPIDOMWindowInner* aWindow) :
     DOMEventTargetHelper(aWindow) {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
 
@@ -37,16 +39,33 @@ public:
   void GetSupportedConstraints(MediaTrackSupportedConstraints& aResult) {};
 
   already_AddRefed<Promise>
   GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
 
   already_AddRefed<Promise>
   EnumerateDevices(ErrorResult &aRv);
 
+  virtual void OnDeviceChange() override;
+
+  mozilla::dom::EventHandlerNonNull* GetOndevicechange();
+
+  void SetOndevicechange(mozilla::dom::EventHandlerNonNull* aCallback);
+
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+    nsIDOMEventListener* aListener,
+    bool aUseCapture, bool aWantsUntrusted,
+    uint8_t optional_argc) override;
+
+  virtual void AddEventListener(const nsAString& aType,
+                                dom::EventListener* aListener,
+                                const dom::AddEventListenerOptionsOrBoolean& aOptions,
+                                const dom::Nullable<bool>& aWantsUntrusted,
+                                ErrorResult& aRv) override;
+
 private:
   class GumResolver;
   class EnumDevResolver;
   class GumRejecter;
 
   virtual ~MediaDevices() {}
 };
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/Types.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/MediaDevices.h"
 #include "mozilla/Base64.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/media/MediaTaskUtils.h"
 #include "MediaTrackConstraints.h"
 #include "VideoUtils.h"
 #include "Latency.h"
 #include "nsProxyRelease.h"
@@ -1991,16 +1992,35 @@ bool MediaManager::IsLoop(nsIURI* aDocUR
 
 bool MediaManager::IsPrivateBrowsing(nsPIDOMWindowInner* window)
 {
   nsCOMPtr<nsIDocument> doc = window->GetDoc();
   nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
   return loadContext && loadContext->UsePrivateBrowsing();
 }
 
+int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
+{
+  MediaManager::PostTask(NewTaskFrom([]() {
+    RefPtr<MediaManager> manager = MediaManager_GetInstance();
+    manager->GetBackend(0)->AddDeviceChangeCallback(manager);
+  }));
+
+  return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
+}
+
+void MediaManager::OnDeviceChange() {
+  RefPtr<MediaManager> self(this);
+  NS_DispatchToMainThread(media::NewRunnableFrom([self,this]() mutable {
+    MOZ_ASSERT(NS_IsMainThread());
+    DeviceChangeCallback::OnDeviceChange();
+    return NS_OK;
+  }));
+}
+
 nsresult MediaManager::GenerateUUID(nsAString& aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
       do_GetService("@mozilla.org/uuid-generator;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Generate a call ID.
@@ -2720,16 +2740,35 @@ MediaManager::OnNavigation(uint64_t aWin
   // This is safe since we're on main-thread, and the windowlist can only
   // be added to from the main-thread
   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowID);
   if (window) {
     IterateWindowListeners(window->AsInner(), StopSharingCallback, nullptr);
   } else {
     RemoveWindowID(aWindowID);
   }
+
+  RemoveMediaDevicesCallback(aWindowID);
+}
+
+void
+MediaManager::RemoveMediaDevicesCallback(uint64_t aWindowID)
+{
+  for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
+  {
+    dom::MediaDevices* mediadevices = static_cast<dom::MediaDevices *>(observer);
+    MOZ_ASSERT(mediadevices);
+    if (mediadevices) {
+      nsPIDOMWindowInner* window = mediadevices->GetOwner();
+      MOZ_ASSERT(window);
+      if (window && window->WindowID() == aWindowID)
+        DeviceChangeCallback::RemoveDeviceChangeCallback(observer);
+        return;
+    }
+  }
 }
 
 StreamListeners*
 MediaManager::AddWindowID(uint64_t aWindowId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Store the WindowID in a hash table and mark as active. The entry is removed
   // when this window is closed or navigated away from.
@@ -2899,16 +2938,17 @@ MediaManager::Shutdown()
     Run() override
     {
       LOG(("MediaManager Thread Shutdown"));
       MOZ_ASSERT(MediaManager::IsInMediaThread());
       // Must shutdown backend on MediaManager thread, since that's where we started it from!
       {
         if (mManager->mBackend) {
           mManager->mBackend->Shutdown(); // ok to invoke multiple times
+          mManager->mBackend->RemoveDeviceChangeCallback(mManager);
         }
       }
       mozilla::ipc::BackgroundChild::CloseForCurrentThread();
       // must explicitly do this before dispatching the reply, since the reply may kill us with Stop()
       mManager->mBackend = nullptr; // last reference, will invoke Shutdown() again
 
       if (NS_FAILED(NS_DispatchToMainThread(mReply.forget()))) {
         LOG(("Will leak thread: DispatchToMainthread of reply runnable failed in MediaManager shutdown"));
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_MEDIAMANAGER_H
 #define MOZILLA_MEDIAMANAGER_H
 
 #include "MediaEngine.h"
+#include "DeviceChangeCallback.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsIMediaManager.h"
 
 #include "nsHashKeys.h"
 #include "nsGlobalWindow.h"
 #include "nsClassHashtable.h"
@@ -182,16 +183,17 @@ typedef nsClassHashtable<nsUint64HashKey
 // we could add MediaManager if needed
 typedef void (*WindowListenerCallback)(MediaManager *aThis,
                                        uint64_t aWindowID,
                                        StreamListeners *aListeners,
                                        void *aData);
 
 class MediaManager final : public nsIMediaManagerService,
                            public nsIObserver
+                          ,public DeviceChangeCallback
 {
   friend GetUserMediaCallbackMediaStreamListener;
 public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
@@ -251,16 +253,19 @@ public:
   nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow, dom::Promise& aPromise);
   void OnNavigation(uint64_t aWindowID);
   bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<RefPtr<MediaDevice>> SourceSet;
   static bool IsPrivateBrowsing(nsPIDOMWindowInner* window);
+
+  virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
+  virtual void OnDeviceChange() override;
 private:
   typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
   typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
   typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
 
   static bool IsPrivileged();
   static bool IsLoop(nsIURI* aDocURI);
   static nsresult GenerateUUID(nsAString& aResult);
@@ -303,16 +308,17 @@ private:
   void Shutdown();
 
   void StopScreensharing(uint64_t aWindowID);
   void IterateWindowListeners(nsPIDOMWindowInner *aWindow,
                               WindowListenerCallback aCallback,
                               void *aData);
 
   void StopMediaStreams();
+  void RemoveMediaDevicesCallback(uint64_t aWindowID);
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsRefPtrHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
 
   // Always exists
   nsAutoPtr<base::Thread> mMediaThread;
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -103,16 +103,22 @@ GetCamerasChild() {
     CamerasSingleton::Child() = runnable->GetCamerasChild();
   }
   if (!CamerasSingleton::Child()) {
     LOG(("Failed to set up CamerasChild, are we in shutdown?"));
   }
   return CamerasSingleton::Child();
 }
 
+CamerasChild*
+GetCamerasChildIfExists() {
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  return CamerasSingleton::Child();
+}
+
 bool
 CamerasChild::RecvReplyFailure(void)
 {
   LOG((__PRETTY_FUNCTION__));
   MonitorAutoLock monitor(mReplyMonitor);
   mReceivedReply = true;
   mReplySuccess = false;
   monitor.Notify();
@@ -575,16 +581,23 @@ CamerasChild::RecvDeliverFrame(const int
   } else {
     LOG(("DeliverFrame called with dead callback"));
   }
   SendReleaseFrame(shmem);
   return true;
 }
 
 bool
+CamerasChild::RecvDeviceChange()
+{
+  this->OnDeviceChange();
+  return true;
+}
+
+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);
   if (Callback(capEng, capId)) {
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_CamerasChild_h
 #define mozilla_CamerasChild_h
 
 #include "mozilla/Move.h"
 #include "mozilla/Pair.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/camera/PCamerasChild.h"
 #include "mozilla/camera/PCamerasParent.h"
+#include "DeviceChangeCallback.h"
 #include "mozilla/Mutex.h"
 #include "base/singleton.h"
 #include "nsCOMPtr.h"
 
 // conflicts with #include of scoped_ptr.h
 #undef FF
 #include "webrtc/common.h"
 // Video Engine
@@ -113,16 +114,18 @@ private:
 
 // Get a pointer to a CamerasChild object we can use to do IPC with.
 // This does everything needed to set up, including starting the IPC
 // channel with PBackground, blocking until thats done, and starting the
 // thread to do IPC on. This will fail if we're in shutdown. On success
 // it will set up the CamerasSingleton.
 CamerasChild* GetCamerasChild();
 
+CamerasChild* GetCamerasChildIfExists();
+
 // Shut down the IPC channel and everything associated, like WebRTC.
 // This is a static call because the CamerasChild object may not even
 // be alive when we're called.
 void Shutdown(void);
 
 // Obtain the CamerasChild object (if possible, i.e. not shutting down),
 // and maintain a grip on the object for the duration of the call.
 template <class MEM_FUN, class... ARGS>
@@ -133,16 +136,17 @@ int GetChildAndCall(MEM_FUN&& f, ARGS&&.
   if (child) {
     return (child->*f)(mozilla::Forward<ARGS>(args)...);
   } else {
     return -1;
   }
 }
 
 class CamerasChild final : public PCamerasChild
+                          ,public DeviceChangeCallback
 {
   friend class mozilla::ipc::BackgroundChildImpl;
   template <class T> friend class mozilla::camera::LockAndDispatch;
 
 public:
   // We are owned by the PBackground thread only. CamerasSingleton
   // takes a non-owning reference.
   NS_INLINE_DECL_REFCOUNTING(CamerasChild)
@@ -150,16 +154,18 @@ public:
   // IPC messages recevied, received on the PBackground thread
   // 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;
+
   // 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;
   virtual bool RecvReplyFailure(void) override;
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -34,16 +34,34 @@ namespace camera {
 // - 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.
 
+void InputObserver::DeviceChange() {
+  LOG((__PRETTY_FUNCTION__));
+  MOZ_ASSERT(mParent);
+
+  RefPtr<nsIRunnable> ipc_runnable =
+    media::NewRunnableFrom([this]() -> nsresult {
+      if (mParent->IsShuttingDown()) {
+        return NS_ERROR_FAILURE;
+      }
+      Unused << mParent->SendDeviceChange();
+      return NS_OK;
+    });
+
+  nsIThread* thread = mParent->GetBackgroundThread();
+  MOZ_ASSERT(thread != nullptr);
+  thread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+};
+
 class FrameSizeChangeRunnable : public Runnable {
 public:
   FrameSizeChangeRunnable(CamerasParent *aParent, CaptureEngine capEngine,
                           int cap_id, unsigned int aWidth, unsigned int aHeight)
     : mParent(aParent), mCapEngine(capEngine), mCapId(cap_id),
       mWidth(aWidth), mHeight(aHeight) {}
 
   NS_IMETHOD Run() override {
@@ -394,16 +412,25 @@ CamerasParent::SetupEngine(CaptureEngine
   }
 
   helper->mPtrViECapture = webrtc::ViECapture::GetInterface(helper->mEngine);
   if (!helper->mPtrViECapture) {
     LOG(("ViECapture::GetInterface failed"));
     return false;
   }
 
+  InputObserver** observer = mObservers.AppendElement(
+          new InputObserver(this));
+
+#ifdef DEBUG
+  MOZ_ASSERT(0 == helper->mPtrViECapture->RegisterInputObserver(*observer));
+#else
+  helper->mPtrViECapture->RegisterInputObserver(*observer);
+#endif
+
   helper->mPtrViERender = webrtc::ViERender::GetInterface(helper->mEngine);
   if (!helper->mPtrViERender) {
     LOG(("ViERender::GetInterface failed"));
     return false;
   }
 
   return true;
 }
@@ -430,30 +457,41 @@ CamerasParent::CloseEngines()
     if (mEngines[i].mEngineIsRunning) {
       LOG(("Being closed down while engine %d is running!", i));
     }
     if (mEngines[i].mPtrViERender) {
       mEngines[i].mPtrViERender->Release();
       mEngines[i].mPtrViERender = nullptr;
     }
     if (mEngines[i].mPtrViECapture) {
+#ifdef DEBUG
+      MOZ_ASSERT(0 == mEngines[i].mPtrViECapture->DeregisterInputObserver());
+#else
+      mEngines[i].mPtrViECapture->DeregisterInputObserver();
+#endif
+
       mEngines[i].mPtrViECapture->Release();
         mEngines[i].mPtrViECapture = nullptr;
     }
     if(mEngines[i].mPtrViEBase) {
       mEngines[i].mPtrViEBase->Release();
       mEngines[i].mPtrViEBase = nullptr;
     }
     if (mEngines[i].mEngine) {
       mEngines[i].mEngine->SetTraceCallback(nullptr);
       webrtc::VideoEngine::Delete(mEngines[i].mEngine);
       mEngines[i].mEngine = nullptr;
     }
   }
 
+  for (InputObserver* observer : mObservers) {
+    delete observer;
+  }
+  mObservers.Clear();
+
   mWebRTCAlive = false;
 }
 
 bool
 CamerasParent::EnsureInitialized(int aEngine)
 {
   LOG((__PRETTY_FUNCTION__));
   // We're shutting down, don't try to do new WebRTC ops.
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -71,16 +71,29 @@ public:
 
   // The webrtc code keeps a reference to this one.
   webrtc::Config mConfig;
 
   // Engine alive
   bool mEngineIsRunning;
 };
 
+class InputObserver :  public webrtc::ViEInputObserver
+{
+public:
+  explicit InputObserver(CamerasParent* aParent)
+    : mParent(aParent) {};
+  virtual void DeviceChange();
+
+  friend CamerasParent;
+
+private:
+  RefPtr<CamerasParent> mParent;
+};
+
 class CamerasParent :  public PCamerasParent,
                        public nsIObserver
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
 public:
   static already_AddRefed<CamerasParent> Create();
@@ -148,16 +161,17 @@ protected:
   base::Thread* mVideoCaptureThread;
 
   // Shutdown handling
   bool mChildIsAlive;
   bool mDestroyed;
   // Above 2 are PBackground only, but this is potentially
   // read cross-thread.
   mozilla::Atomic<bool> mWebRTCAlive;
+  nsTArray<InputObserver*> mObservers;
 };
 
 PCamerasParent* CreateCamerasParent();
 
 } // namespace camera
 } // namespace mozilla
 
 #endif  // mozilla_CameraParent_h
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/DeviceChangeCallback.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DeviceChangeCallback_h
+#define mozilla_DeviceChangeCallback_h
+
+namespace mozilla {
+
+class DeviceChangeCallback
+{
+public:
+  virtual void OnDeviceChange()
+  {
+    MutexAutoLock lock(mCallbackMutex);
+    for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
+    {
+      observer->OnDeviceChange();
+    }
+  }
+
+  virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
+  {
+    MutexAutoLock lock(mCallbackMutex);
+    if (mDeviceChangeCallbackList.IndexOf(aCallback) == mDeviceChangeCallbackList.NoIndex)
+      mDeviceChangeCallbackList.AppendElement(aCallback);
+    return 0;
+  }
+
+  virtual int RemoveDeviceChangeCallback(DeviceChangeCallback* aCallback)
+  {
+    MutexAutoLock lock(mCallbackMutex);
+    if (mDeviceChangeCallbackList.IndexOf(aCallback) != mDeviceChangeCallbackList.NoIndex)
+      mDeviceChangeCallbackList.RemoveElement(aCallback);
+    return 0;
+  }
+
+  DeviceChangeCallback() : mCallbackMutex("mozilla::media::DeviceChangeCallback::mCallbackMutex")
+  {
+    mDeviceChangeCallbackList.Clear();
+  }
+
+  virtual ~DeviceChangeCallback()
+  {
+    mDeviceChangeCallbackList.Clear();
+  }
+
+protected:
+  nsTArray<DeviceChangeCallback*> mDeviceChangeCallbackList;
+  Mutex mCallbackMutex;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DeviceChangeCallback_h
--- a/dom/media/systemservices/PCameras.ipdl
+++ b/dom/media/systemservices/PCameras.ipdl
@@ -24,16 +24,17 @@ async protocol PCameras
   manager PBackground;
 
 child:
   async FrameSizeChange(int capEngine, int cap_id, int w, int h);
   // transfers ownership of |buffer| from parent to child
   async DeliverFrame(int capEngine, int cap_id,
                      Shmem buffer, size_t size, uint32_t time_stamp,
                      int64_t ntp_time, int64_t render_time);
+  async DeviceChange();
   async ReplyNumberOfCaptureDevices(int numdev);
   async ReplyNumberOfCapabilities(int numdev);
   async ReplyAllocateCaptureDevice(int numdev);
   async ReplyGetCaptureCapability(CaptureCapability cap);
   async ReplyGetCaptureDevice(nsCString device_name, nsCString device_id);
   async ReplyFailure();
   async ReplySuccess();
   async __delete__();
--- a/dom/media/systemservices/moz.build
+++ b/dom/media/systemservices/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += [
         'CamerasChild.h',
         'CamerasParent.h',
+        'DeviceChangeCallback.h',
         'LoadManager.h',
         'LoadManagerFactory.h',
         'LoadMonitor.h',
     ]
     UNIFIED_SOURCES += [
         'CamerasChild.cpp',
         'CamerasParent.cpp',
         'LoadManager.cpp',
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -6,16 +6,17 @@
 #define MEDIAENGINE_H_
 
 #include "mozilla/RefPtr.h"
 #include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
 #include "MediaTrackConstraints.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/VideoStreamTrack.h"
+#include "DeviceChangeCallback.h"
 
 namespace mozilla {
 
 namespace dom {
 class Blob;
 } // namespace dom
 
 enum {
@@ -36,17 +37,17 @@ class MediaEngineAudioSource;
 
 enum MediaEngineState {
   kAllocated,
   kStarted,
   kStopped,
   kReleased
 };
 
-class MediaEngine
+class MediaEngine : public DeviceChangeCallback
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -121,16 +121,20 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   }
 #else
 #ifdef MOZ_WIDGET_GONK
   AsyncLatencyLogger::Get()->AddRef();
 #endif
 #endif
   // XXX
   gFarendObserver = new AudioOutputObserver();
+
+  camera::GetChildAndCall(
+    &camera::CamerasChild::AddDeviceChangeCallback,
+    this);
 }
 
 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);
@@ -416,16 +420,21 @@ MediaEngineWebRTC::EnumerateAudioDevices
 }
 
 void
 MediaEngineWebRTC::Shutdown()
 {
   // This is likely paranoia
   MutexAutoLock lock(mMutex);
 
+  if (camera::GetCamerasChildIfExists()) {
+    camera::GetChildAndCall(
+      &camera::CamerasChild::RemoveDeviceChangeCallback, this);
+  }
+
   LOG(("%s", __FUNCTION__));
   // Shutdown all the sources, since we may have dangling references to the
   // sources in nsDOMUserMediaStreams waiting for GC/CC
   for (auto iter = mVideoSources.Iter(); !iter.Done(); iter.Next()) {
     MediaEngineVideoSource* source = iter.UserData();
     if (source) {
       source->Shutdown();
     }
--- a/dom/webidl/MediaDevices.webidl
+++ b/dom/webidl/MediaDevices.webidl
@@ -7,17 +7,18 @@
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Func="Navigator::HasUserMediaSupport"]
 interface MediaDevices : EventTarget {
-//  attribute EventHandler ondevicechange;
+  [Pref="media.ondevicechange.enabled"]
+  attribute EventHandler ondevicechange;
   MediaTrackSupportedConstraints getSupportedConstraints();
 
   [Throws]
   Promise<sequence<MediaDeviceInfo>> enumerateDevices();
 
   [Throws]
   Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
 };
--- a/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/include/video_capture.h
@@ -16,27 +16,45 @@
 #include "webrtc/modules/video_capture/include/video_capture_defines.h"
 
 #if defined(ANDROID) && !defined(WEBRTC_GONK)
 #include <jni.h>
 #endif
 
 namespace webrtc {
 
+class VideoInputFeedBack
+{
+public:
+    virtual void OnDeviceChange() = 0;
+protected:
+    virtual ~VideoInputFeedBack(){}
+};
+
 #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_GONK)
   int32_t SetCaptureAndroidVM(JavaVM* javaVM);
 #endif
 
 class VideoCaptureModule: public RefCountedModule {
  public:
   // Interface for receiving information about available camera devices.
   class DeviceInfo {
    public:
     virtual uint32_t NumberOfDevices() = 0;
     virtual int32_t Refresh() = 0;
+    virtual void DeviceChange() {
+     if (_inputCallBack)
+      _inputCallBack->OnDeviceChange();
+    }
+    virtual void RegisterVideoInputFeedBack(VideoInputFeedBack& callBack) {
+     _inputCallBack = &callBack;
+    }
+    virtual void DeRegisterVideoInputFeedBack() {
+     _inputCallBack = NULL;
+    }
 
     // Returns the available capture devices.
     // deviceNumber   - Index of capture device.
     // deviceNameUTF8 - Friendly name of the capture device.
     // deviceUniqueIdUTF8 - Unique name of the capture device if it exist.
     //                      Otherwise same as deviceNameUTF8.
     // productUniqueIdUTF8 - Unique product id if it exist.
     //                       Null terminated otherwise.
@@ -77,16 +95,18 @@ class VideoCaptureModule: public RefCoun
     virtual int32_t DisplayCaptureSettingsDialogBox(
         const char* deviceUniqueIdUTF8,
         const char* dialogTitleUTF8,
         void* parentWindow,
         uint32_t positionX,
         uint32_t positionY) = 0;
 
     virtual ~DeviceInfo() {}
+   private:
+    VideoInputFeedBack* _inputCallBack = NULL;
   };
 
   class VideoCaptureEncodeInterface {
    public:
     virtual int32_t ConfigureEncoder(const VideoCodec& codec,
                                      uint32_t maxPayloadSize) = 0;
     // Inform the encoder about the new target bit rate.
     //  - newBitRate       : New target bit rate in Kbit/s.
--- a/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info.mm
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info.mm
@@ -19,21 +19,23 @@ namespace webrtc
 namespace videocapturemodule
 {
 
 VideoCaptureMacAVFoundationInfo::VideoCaptureMacAVFoundationInfo(const int32_t id) :
     DeviceInfoImpl(id)
 {
     nsAutoreleasePool localPool;
     _captureInfo = [[VideoCaptureMacAVFoundationInfoObjC alloc] init];
+    [_captureInfo registerOwner:this];
 }
 
 VideoCaptureMacAVFoundationInfo::~VideoCaptureMacAVFoundationInfo()
 {
     nsAutoreleasePool localPool;
+    [_captureInfo registerOwner:nil];
     [_captureInfo release];
 }
 
 int32_t VideoCaptureMacAVFoundationInfo::Init()
 {
 
     return 0;
 }
--- a/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.h
@@ -21,16 +21,19 @@
 
 #include "webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info.h"
 #include "webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_utility.h"
 
 @interface VideoCaptureMacAVFoundationInfoObjC : NSObject{
     bool                                _OSSupportedInfo;
     NSArray*                            _captureDevicesInfo;
     int                                    _captureDeviceCountInfo;
+    NSArray*                            _observers;
+    NSLock*                             _lock;
+    webrtc::videocapturemodule::VideoCaptureMacAVFoundationInfo* _owner;
 
 }
 
 /**************************************************************************
  *
  *   The following functions are considered to be private
  *
  ***************************************************************************/
@@ -41,16 +44,18 @@
 
 
 /**************************************************************************
  *
  *   The following functions are considered to be public and called by VideoCaptureMacAVFoundationInfo class
  *
  ***************************************************************************/
 
+- (void)registerOwner:(webrtc::videocapturemodule::VideoCaptureMacAVFoundationInfo*)owner;
+
 - (NSNumber*)getCaptureDeviceCount;
 
 - (NSNumber*)getCaptureCapabilityCount:(const char*)uniqueId;
 
 - (NSNumber*)getCaptureCapability:(const char*)uniqueId
                      CapabilityId:(uint32_t)capabilityId
                  Capability_width:(int32_t*)width
                 Capability_height:(int32_t*)height
--- a/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.mm
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.mm
@@ -10,16 +10,17 @@
 
 #pragma mark **** imports/includes
 
 #import "webrtc/modules/video_capture/mac/avfoundation/video_capture_avfoundation_info_objc.h"
 
 #include "webrtc/system_wrappers/interface/trace.h"
 
 using namespace webrtc;
+using namespace videocapturemodule;
 
 #pragma mark **** hidden class interface
 
 @implementation VideoCaptureMacAVFoundationInfoObjC
 
 // ****************** over-written OS methods ***********************
 #pragma mark **** over-written OS methods
 
@@ -33,22 +34,35 @@ using namespace webrtc;
     }
     else
     {
         return nil;
     }
     return self;
 }
 
+- (void)registerOwner:(VideoCaptureMacAVFoundationInfo*)owner {
+    [_lock lock];
+    _owner = owner;
+    [_lock unlock];
+}
+
 /// ***** Objective-C. Similar to C++ destructor
 /// ***** Returns nothing
 - (void)dealloc {
 
     [_captureDevicesInfo release];
 
+    // Remove Observers
+    NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
+    for (id observer in _observers)
+        [notificationCenter removeObserver:observer];
+    [_observers release];
+    [_lock release];
+
     [super dealloc];
 }
 
 // ****************** public methods ******************
 #pragma mark **** public method implementations
 
 /// ***** Creates a message box with Cocoa framework
 /// ***** Returns 0 on success, -1 otherwise.
@@ -218,16 +232,43 @@ using namespace webrtc;
     if(NO == _OSSupportedInfo)
     {
         return [NSNumber numberWithInt:0];
     }
 
     _captureDeviceCountInfo = 0;
     [self getCaptureDevices];
 
+    _lock = [[NSLock alloc] init];
+
+    //register device connected / disconnected event
+    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+
+    id deviceWasConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification
+        object:nil
+        queue:[NSOperationQueue mainQueue]
+        usingBlock:^(NSNotification *note) {
+            [_lock lock];
+            if(_owner)
+                _owner->DeviceChange();
+            [_lock unlock];
+        }];
+
+    id deviceWasDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification
+        object:nil
+        queue:[NSOperationQueue mainQueue]
+        usingBlock:^(NSNotification *note) {
+            [_lock lock];
+            if(_owner)
+                _owner->DeviceChange();
+            [_lock unlock];
+        }];
+
+    _observers = [[NSArray alloc] initWithObjects:deviceWasConnectedObserver, deviceWasDisconnectedObserver, nil];
+
     return [NSNumber numberWithInt:0];
 }
 
 // ***** Checks to see if the AVCaptureSession framework is available in the OS
 // ***** If it is not, isOSSupprted = NO
 // ***** Throughout the rest of the class isOSSupprted is checked and functions
 // ***** are/aren't called depending
 // ***** The user can use weak linking to the AVFoundation framework and run on older
--- a/media/webrtc/trunk/webrtc/video_engine/include/vie_capture.h
+++ b/media/webrtc/trunk/webrtc/video_engine/include/vie_capture.h
@@ -22,16 +22,27 @@
 #include "webrtc/common_video/interface/i420_video_frame.h"
 #include "webrtc/common_video/rotation.h"
 
 namespace webrtc {
 
 class VideoEngine;
 class VideoCaptureModule;
 
+// The observer is registered using RegisterInputObserver() and
+// deregistered using DeregisterInputObserver().
+class WEBRTC_DLLEXPORT ViEInputObserver {
+ public:
+  // This method is called if an input device is connected or disconnected .
+  virtual void DeviceChange() = 0;
+
+ protected:
+  virtual ~ViEInputObserver() {}
+};
+
 // This structure describes one set of the supported capabilities for a capture
 // device.
 struct CaptureCapability {
   unsigned int width;
   unsigned int height;
   unsigned int maxFPS;
   RawVideoType rawType;
   VideoCodecType codecType;
@@ -211,19 +222,23 @@ class WEBRTC_DLLEXPORT ViECapture {
   // Enables brightness alarm detection and the brightness alarm callback.
   virtual int EnableBrightnessAlarm(const int capture_id,
                                     const bool enable) = 0;
 
   // Registers an instance of a user implementation of the ViECaptureObserver.
   virtual int RegisterObserver(const int capture_id,
                                ViECaptureObserver& observer) = 0;
 
+  virtual int RegisterInputObserver(ViEInputObserver* observer) = 0;
+
   // Removes an already registered instance of ViECaptureObserver.
   virtual int DeregisterObserver(const int capture_id) = 0;
 
+  virtual int DeregisterInputObserver() = 0;
+
  protected:
   ViECapture() {}
   virtual ~ViECapture() {}
 };
 
 }  // namespace webrtc
 
 #endif  // WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_CAPTURE_H_
--- a/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.cc
@@ -368,16 +368,24 @@ int ViECaptureImpl::RegisterObserver(con
   }
   if (vie_capture->RegisterObserver(&observer) != 0) {
     shared_data_->SetLastError(kViECaptureDeviceUnknownError);
     return -1;
   }
   return 0;
 }
 
+int ViECaptureImpl::RegisterInputObserver(ViEInputObserver* observer) {
+  if (shared_data_->input_manager()->RegisterObserver(observer) != 0) {
+    shared_data_->SetLastError(kViECaptureDeviceUnknownError);
+    return -1;
+  }
+  return 0;
+}
+
 int ViECaptureImpl::DeregisterObserver(const int capture_id) {
   ViEInputManagerScoped is(*(shared_data_->input_manager()));
   ViECapturer* vie_capture = is.Capture(capture_id);
   if (!vie_capture) {
     shared_data_->SetLastError(kViECaptureDeviceDoesNotExist);
     return -1;
   }
   if (!vie_capture->IsObserverRegistered()) {
@@ -387,9 +395,17 @@ int ViECaptureImpl::DeregisterObserver(c
 
   if (vie_capture->DeRegisterObserver() != 0) {
     shared_data_->SetLastError(kViECaptureDeviceUnknownError);
     return -1;
   }
   return 0;
 }
 
+int ViECaptureImpl::DeregisterInputObserver() {
+  if (shared_data_->input_manager()->DeRegisterObserver() != 0) {
+    shared_data_->SetLastError(kViECaptureDeviceUnknownError);
+    return -1;
+  }
+  return 0;
+}
+
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.h
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_capture_impl.h
@@ -61,17 +61,19 @@ class ViECaptureImpl
     const char* unique_idUTF8, const unsigned int unique_idUTF8Length,
     const char* dialog_title, void* parent_window = NULL,
     const unsigned int x = 200, const unsigned int y = 200);
   virtual int GetOrientation(const char* unique_idUTF8,
                              VideoRotation& orientation);
   virtual int EnableBrightnessAlarm(const int capture_id, const bool enable);
   virtual int RegisterObserver(const int capture_id,
                                ViECaptureObserver& observer);
+  virtual int RegisterInputObserver(ViEInputObserver* observer);
   virtual int DeregisterObserver(const int capture_id);
+  virtual int DeregisterInputObserver();
 
  protected:
   explicit ViECaptureImpl(ViESharedData* shared_data);
   virtual ~ViECaptureImpl();
 
  private:
   ViESharedData* shared_data_;
 };
--- a/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.cc
@@ -27,16 +27,18 @@
 
 namespace webrtc {
 
 ViEInputManager::ViEInputManager(const int engine_id, const Config& config)
     : config_(config),
       engine_id_(engine_id),
       map_cs_(CriticalSectionWrapper::CreateCriticalSection()),
       device_info_cs_(CriticalSectionWrapper::CreateCriticalSection()),
+      observer_cs_(CriticalSectionWrapper::CreateCriticalSection()),
+      observer_(NULL),
       vie_frame_provider_map_(),
       capture_device_info_(NULL),
       module_process_thread_(NULL) {
   for (int idx = 0; idx < kViEMaxCaptureDevices; idx++) {
     free_capture_device_id_[idx] = true;
   }
 }
 
@@ -329,16 +331,23 @@ ViECapturer* ViEInputManager::ViECapture
         capture_id <= kViECaptureIdBase + kViEMaxCaptureDevices)) {
     LOG(LS_ERROR) << "Capture device doesn't exist " << capture_id << ".";
     return NULL;
   }
 
   return static_cast<ViECapturer*>(ViEFrameProvider(capture_id));
 }
 
+void ViEInputManager::OnDeviceChange() {
+  CriticalSectionScoped cs(observer_cs_.get());
+  if (observer_) {
+    observer_->DeviceChange();
+  }
+}
+
 // Create different DeviceInfo by _config;
 VideoCaptureModule::DeviceInfo* ViEInputManager::GetDeviceInfo() {
   CaptureDeviceType type = config_.Get<CaptureDeviceInfo>().type;
 
   if (capture_device_info_ == NULL) {
     switch (type) {
       case CaptureDeviceType::Screen:
       case CaptureDeviceType::Application:
@@ -356,16 +365,45 @@ VideoCaptureModule::DeviceInfo* ViEInput
         break;
       default:
         // Don't try to build anything for unknown/unsupported types
         break;
     }
   }
   return capture_device_info_;
 }
+
+int32_t ViEInputManager::RegisterObserver(ViEInputObserver* observer) {
+  {
+    CriticalSectionScoped cs(observer_cs_.get());
+    if (observer_) {
+      LOG_F(LS_ERROR) << "Observer already registered.";
+      return -1;
+    }
+    observer_ = observer;
+  }
+
+  if (!GetDeviceInfo())
+    return -1;
+
+  if (capture_device_info_ != NULL)
+    capture_device_info_->RegisterVideoInputFeedBack(*this);
+
+  return 0;
+}
+
+int32_t ViEInputManager::DeRegisterObserver() {
+  if (capture_device_info_ != NULL)
+    capture_device_info_->DeRegisterVideoInputFeedBack();
+
+  CriticalSectionScoped cs(observer_cs_.get());
+  observer_ = NULL;
+  return 0;
+}
+
 ViEInputManagerScoped::ViEInputManagerScoped(
     const ViEInputManager& vie_input_manager)
     : ViEManagerScopedBase(vie_input_manager) {
 }
 
 ViECapturer* ViEInputManagerScoped::Capture(int capture_id) const {
   return static_cast<const ViEInputManager*>(vie_manager_)->ViECapturePtr(
       capture_id);
--- a/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.h
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_input_manager.h
@@ -27,17 +27,18 @@ namespace webrtc {
 class Config;
 class CriticalSectionWrapper;
 class ProcessThread;
 class RWLockWrapper;
 class ViECapturer;
 class ViEExternalCapture;
 class VoiceEngine;
 
-class ViEInputManager : private ViEManagerBase {
+class ViEInputManager : private ViEManagerBase,
+                        protected VideoInputFeedBack {
   friend class ViEInputManagerScoped;
  public:
   ViEInputManager(int engine_id, const Config& config);
   ~ViEInputManager();
 
   void SetModuleProcessThread(ProcessThread* module_process_thread);
 
   // Returns number of capture devices.
@@ -73,18 +74,22 @@ class ViEInputManager : private ViEManag
   int CreateCaptureDevice(const char* device_unique_idUTF8,
                           const uint32_t device_unique_idUTF8Length,
                           int& capture_id);
   int CreateCaptureDevice(VideoCaptureModule* capture_module,
                           int& capture_id);
   int CreateExternalCaptureDevice(ViEExternalCapture*& external_capture,
                                   int& capture_id);
   int DestroyCaptureDevice(int capture_id);
+  int32_t RegisterObserver(ViEInputObserver* observer);
+  int32_t DeRegisterObserver();
  protected:
   VideoCaptureModule::DeviceInfo* GetDeviceInfo();
+  // Implements VideoInputFeedBack.
+  virtual void OnDeviceChange();
  private:
   // Gets and allocates a free capture device id. Assumed protected by caller.
   bool GetFreeCaptureId(int* freecapture_id);
 
   // Frees a capture id assigned in GetFreeCaptureId.
   void ReturnCaptureId(int capture_id);
 
   // Gets the ViEFrameProvider for this capture observer.
@@ -96,16 +101,18 @@ class ViEInputManager : private ViEManag
 
   // Gets the ViECapturer for the capture device id.
   ViECapturer* ViECapturePtr(int capture_id) const;
 
   const Config& config_;
   int engine_id_;
   rtc::scoped_ptr<CriticalSectionWrapper> map_cs_;
   rtc::scoped_ptr<CriticalSectionWrapper> device_info_cs_;
+  rtc::scoped_ptr<CriticalSectionWrapper> observer_cs_;
+  ViEInputObserver* observer_ GUARDED_BY(observer_cs_.get());
 
   typedef std::map<int, ViEFrameProviderBase*> FrameProviderMap;
   FrameProviderMap vie_frame_provider_map_;
 
   // Capture devices.
   VideoCaptureModule::DeviceInfo* capture_device_info_;
   int free_capture_device_id_[kViEMaxCaptureDevices];
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4789,16 +4789,19 @@ pref("dom.w3c_touch_events.enabled", 2);
 #endif
 
 // W3C draft pointer events
 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 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