Bug 1572281 - Enhance CubebDeviceEnumerator to accept listeners and signal notifications when an audio device changes. r=pehrsons
authorAlex Chronopoulos <achronop@gmail.com>
Fri, 20 Sep 2019 10:13:51 +0000
changeset 494260 9f15627684961ce192df91efb997b88b56e9cd98
parent 494259 90286c5d1ef171475be1d02d337825ef5fcf3b14
child 494261 7bf7263db30b794139332691f4fbc98b4bfcfdd7
push id114114
push userdluca@mozilla.com
push dateFri, 20 Sep 2019 22:00:08 +0000
treeherdermozilla-inbound@56e11fddf939 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons
bugs1572281
milestone71.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 1572281 - Enhance CubebDeviceEnumerator to accept listeners and signal notifications when an audio device changes. r=pehrsons CubebDeviceEnumerator already knows when an audio device changes. It is enhanced to allow listeners/observers registration and to create notifications when that happens. Also, it is hooked to the existing notification path. On a minor note, it has been revisited the way the enumerator is touched in MediaEngineWebRTC class. Differential Revision: https://phabricator.services.mozilla.com/D46271
dom/media/systemservices/DeviceChangeCallback.h
dom/media/webrtc/CubebDeviceEnumerator.cpp
dom/media/webrtc/CubebDeviceEnumerator.h
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/media/webrtc/MediaEngineWebRTC.h
--- a/dom/media/systemservices/DeviceChangeCallback.h
+++ b/dom/media/systemservices/DeviceChangeCallback.h
@@ -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/. */
 
 #ifndef mozilla_DeviceChangeCallback_h
 #define mozilla_DeviceChangeCallback_h
 
 #include "mozilla/Mutex.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 
 class DeviceChangeCallback {
  public:
   virtual ~DeviceChangeCallback() = default;
   virtual void OnDeviceChange() = 0;
 };
--- a/dom/media/webrtc/CubebDeviceEnumerator.cpp
+++ b/dom/media/webrtc/CubebDeviceEnumerator.cpp
@@ -1,32 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "CubebDeviceEnumerator.h"
 #include "mozilla/StaticPtr.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 
 using namespace CubebUtils;
 
 /* static */
 StaticRefPtr<CubebDeviceEnumerator> CubebDeviceEnumerator::sInstance;
 
 /* static */
-already_AddRefed<CubebDeviceEnumerator> CubebDeviceEnumerator::GetInstance() {
+CubebDeviceEnumerator* CubebDeviceEnumerator::GetInstance() {
   if (!sInstance) {
     sInstance = new CubebDeviceEnumerator();
   }
-  RefPtr<CubebDeviceEnumerator> instance = sInstance.get();
-  MOZ_ASSERT(instance);
-  return instance.forget();
+  return sInstance.get();
 }
 
 CubebDeviceEnumerator::CubebDeviceEnumerator()
     : mMutex("CubebDeviceListMutex"),
       mManualInputInvalidation(false),
       mManualOutputInvalidation(false) {
   int rv = cubeb_register_device_collection_changed(
       GetCubebContext(), CUBEB_DEVICE_TYPE_OUTPUT,
@@ -300,19 +299,26 @@ void CubebDeviceEnumerator::InputAudioDe
 
 void CubebDeviceEnumerator::OutputAudioDeviceListChanged_s(cubeb* aContext,
                                                            void* aUser) {
   CubebDeviceEnumerator* self = reinterpret_cast<CubebDeviceEnumerator*>(aUser);
   self->AudioDeviceListChanged(CubebDeviceEnumerator::Side::OUTPUT);
 }
 
 void CubebDeviceEnumerator::AudioDeviceListChanged(Side aSide) {
-  MutexAutoLock lock(mMutex);
+  {
+    MutexAutoLock lock(mMutex);
+    if (aSide == Side::INPUT) {
+      mInputDevices.Clear();
+    } else {
+      MOZ_ASSERT(aSide == Side::OUTPUT);
+      mOutputDevices.Clear();
+    }
+  }
 
-  if (aSide == Side::INPUT) {
-    mInputDevices.Clear();
-  } else {
-    MOZ_ASSERT(aSide == Side::OUTPUT);
-    mOutputDevices.Clear();
-  }
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "CubebDeviceEnumerator::AudioDeviceListChanged",
+      [self = RefPtr<CubebDeviceEnumerator>(this)]() {
+        self->NotifyDeviceChange();
+      }));
 }
 
 }  // namespace mozilla
--- a/dom/media/webrtc/CubebDeviceEnumerator.h
+++ b/dom/media/webrtc/CubebDeviceEnumerator.h
@@ -3,27 +3,28 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CUBEBDEVICEENUMERATOR_H_
 #define CUBEBDEVICEENUMERATOR_H_
 
 #include "AudioDeviceInfo.h"
 #include "CubebUtils.h"
 #include "cubeb/cubeb.h"
+#include "mozilla/media/DeviceChangeCallback.h"
 #include "mozilla/Mutex.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 // This class implements a cache for accessing the audio device list.
 // It can be accessed on any thread.
-class CubebDeviceEnumerator final {
+class CubebDeviceEnumerator final : public DeviceChangeNotifier {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CubebDeviceEnumerator)
 
-  static already_AddRefed<CubebDeviceEnumerator> GetInstance();
+  static CubebDeviceEnumerator* GetInstance();
   static void Shutdown();
   // This method returns a list of all the input audio devices
   // (sources) available on this machine.
   // This method is safe to call from all threads.
   void EnumerateAudioInputDevices(
       nsTArray<RefPtr<AudioDeviceInfo>>& aOutDevices);
   // Similar for the audio audio devices (sinks). Also thread safe.
   void EnumerateAudioOutputDevices(
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -22,29 +22,34 @@
 #include "prenv.h"
 
 static mozilla::LazyLogModule sGetUserMediaLog("GetUserMedia");
 #undef LOG
 #define LOG(args) MOZ_LOG(sGetUserMediaLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 
+CubebDeviceEnumerator* GetEnumerator() {
+  return CubebDeviceEnumerator::GetInstance();
+}
+
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs& aPrefs)
     : mMutex("mozilla::MediaEngineWebRTC"),
       mDelayAgnostic(aPrefs.mDelayAgnostic),
       mExtendedFilter(aPrefs.mExtendedFilter),
       mHasTabVideoSource(false) {
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID,
                                     &mHasTabVideoSource);
   }
 
   camera::GetChildAndCall(&camera::CamerasChild::AddDeviceChangeCallback, this);
+  GetEnumerator()->AddDeviceChangeCallback(this);
 }
 
 void MediaEngineWebRTC::SetFakeDeviceChangeEvents() {
   camera::GetChildAndCall(&camera::CamerasChild::SetFakeDeviceChangeEvents);
 }
 
 void MediaEngineWebRTC::EnumerateVideoDevices(
     uint64_t aWindowId, camera::CaptureEngine aCapEngine,
@@ -144,20 +149,18 @@ void MediaEngineWebRTC::EnumerateVideoDe
         tabVideoSource->GetGroupId(), NS_LITERAL_STRING("")));
   }
 }
 
 void MediaEngineWebRTC::EnumerateMicrophoneDevices(
     uint64_t aWindowId, nsTArray<RefPtr<MediaDevice>>* aDevices) {
   mMutex.AssertCurrentThreadOwns();
 
-  mEnumerator = CubebDeviceEnumerator::GetInstance();
-
   nsTArray<RefPtr<AudioDeviceInfo>> devices;
-  mEnumerator->EnumerateAudioInputDevices(devices);
+  GetEnumerator()->EnumerateAudioInputDevices(devices);
 
   DebugOnly<bool> foundPreferredDevice = false;
 
   for (uint32_t i = 0; i < devices.Length(); i++) {
 #ifndef ANDROID
     MOZ_ASSERT(devices[i]->DeviceID());
 #endif
     LOG(("Cubeb device %u: type 0x%x, state 0x%x, name %s, id %p", i,
@@ -195,21 +198,18 @@ void MediaEngineWebRTC::EnumerateMicroph
         aDevices->AppendElement(device);
       }
     }
   }
 }
 
 void MediaEngineWebRTC::EnumerateSpeakerDevices(
     uint64_t aWindowId, nsTArray<RefPtr<MediaDevice>>* aDevices) {
-  if (!mEnumerator) {
-    mEnumerator = CubebDeviceEnumerator::GetInstance();
-  }
   nsTArray<RefPtr<AudioDeviceInfo>> devices;
-  mEnumerator->EnumerateAudioOutputDevices(devices);
+  GetEnumerator()->EnumerateAudioOutputDevices(devices);
 
 #ifndef XP_WIN
   DebugOnly<bool> preferredDeviceFound = false;
 #endif
   for (auto& device : devices) {
     if (device->State() == CUBEB_DEVICE_STATE_ENABLED) {
       MOZ_ASSERT(device->Type() == CUBEB_DEVICE_TYPE_OUTPUT);
       nsString uuid(device->Name());
@@ -265,32 +265,31 @@ void MediaEngineWebRTC::EnumerateDevices
   } else if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
         new MediaEngineWebRTCAudioCaptureSource(nullptr);
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
         audioCaptureSource, audioCaptureSource->GetName(),
         NS_ConvertUTF8toUTF16(audioCaptureSource->GetUUID()),
         audioCaptureSource->GetGroupId(), NS_LITERAL_STRING("")));
   } else if (aMediaSource == dom::MediaSourceEnum::Microphone) {
-    MOZ_ASSERT(aMediaSource == dom::MediaSourceEnum::Microphone);
     EnumerateMicrophoneDevices(aWindowId, aDevices);
   }
 
   if (aMediaSink == MediaSinkEnum::Speaker) {
     EnumerateSpeakerDevices(aWindowId, aDevices);
   }
 }
 
 void MediaEngineWebRTC::Shutdown() {
   // This is likely paranoia
   MutexAutoLock lock(mMutex);
 
   if (camera::GetCamerasChildIfExists()) {
     camera::GetChildAndCall(&camera::CamerasChild::RemoveDeviceChangeCallback,
                             this);
   }
+  GetEnumerator()->RemoveDeviceChangeCallback(this);
 
   LOG(("%s", __FUNCTION__));
-  mEnumerator = nullptr;
   mozilla::camera::Shutdown();
 }
 
 }  // namespace mozilla
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -72,17 +72,16 @@ class MediaEngineWebRTC : public MediaEn
                              nsTArray<RefPtr<MediaDevice>>*);
   void EnumerateMicrophoneDevices(uint64_t aWindowId,
                                   nsTArray<RefPtr<MediaDevice>>*);
   void EnumerateSpeakerDevices(uint64_t aWindowId,
                                nsTArray<RefPtr<MediaDevice>>*);
 
   // gUM runnables can e.g. Enumerate from multiple threads
   Mutex mMutex;
-  RefPtr<mozilla::CubebDeviceEnumerator> mEnumerator;
   const bool mDelayAgnostic;
   const bool mExtendedFilter;
   // This also is set in the ctor and then never changed, but we can't make it
   // const because we pass it to a function that takes bool* in the ctor.
   bool mHasTabVideoSource;
 };
 
 }  // namespace mozilla