Bug 1286429 - implement mediaDevices.ondevicechange for Mac OSX; r?jib draft
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Fri, 12 Aug 2016 01:04:49 +0800
changeset 401602 36e633a7f81d1e03bde037da091cd94197677752
parent 401500 fe895421dfbe1f1f8f1fc6a39bb20774423a6d74
child 401603 e502ad0c379ca1a2a45e01ebbac06239b3ab9c30
push id26510
push usermchiang@mozilla.com
push dateWed, 17 Aug 2016 07:54:22 +0000
reviewersjib
bugs1286429
milestone51.0a1
Bug 1286429 - implement mediaDevices.ondevicechange for Mac OSX; r?jib 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
@@ -120,16 +120,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);
@@ -415,16 +419,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