Bug 1152401 - Update enumerate methods of MediaManager and MediaEngine to accept audio sink type. r?jib draft
authorAlex Chronopoulos <achronop@gmail.com>
Mon, 16 Jul 2018 11:30:46 +0200
changeset 818647 a69cc63b6866c8dac9c327853212323ee060c84b
parent 818646 ceb39c8f5154e00896a80df9e19493daf4fe3271
child 818648 536ceeec8a925001bd342a9f1b4eefb85eabbc8c
push id116311
push userachronop@gmail.com
push dateMon, 16 Jul 2018 09:31:43 +0000
reviewersjib
bugs1152401
milestone63.0a1
Bug 1152401 - Update enumerate methods of MediaManager and MediaEngine to accept audio sink type. r?jib MozReview-Commit-ID: 1RClh9UNQck
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineDefault.cpp
dom/media/webrtc/MediaEngineDefault.h
dom/media/webrtc/MediaEngineSource.cpp
dom/media/webrtc/MediaEngineWebRTC.cpp
dom/media/webrtc/MediaEngineWebRTC.h
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1548,17 +1548,17 @@ GetMediaDevices(MediaEngine *aEngine,
                 const char* aMediaDeviceName = nullptr)
 {
   MOZ_ASSERT(MediaManager::IsInMediaThread());
 
   LOG(("%s: aEngine=%p, aWindowId=%" PRIu64 ", aSrcType=%" PRIu8 ", aMediaDeviceName=%s",
        __func__, aEngine, aWindowId, static_cast<uint8_t>(aSrcType),
        aMediaDeviceName ? aMediaDeviceName : "null"));
   nsTArray<RefPtr<MediaDevice>> devices;
-  aEngine->EnumerateDevices(aWindowId, aSrcType, &devices);
+  aEngine->EnumerateDevices(aWindowId, aSrcType, MediaSinkEnum::Other, &devices);
 
   /*
    * We're allowing multiple tabs to access the same camera for parity
    * with Chrome.  See bug 811757 for some of the issues surrounding
    * this decision.  To disallow, we'd filter by IsAvailable() as we used
    * to.
    */
   if (aMediaDeviceName && *aMediaDeviceName)  {
@@ -1916,102 +1916,117 @@ private:
 
 /**
  * EnumerateRawDevices - Enumerate a list of audio & video devices that
  * satisfy passed-in constraints. List contains raw id's.
  */
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateRawDevices(uint64_t aWindowId,
-                                  MediaSourceEnum aVideoType,
-                                  MediaSourceEnum aAudioType,
-                                  DeviceEnumerationType aVideoEnumType /* = DeviceEnumerationType::Normal */,
-                                  DeviceEnumerationType aAudioEnumType /* = DeviceEnumerationType::Normal */)
+                                  MediaSourceEnum aVideoInputType,
+                                  MediaSourceEnum aAudioInputType,
+                                  MediaSinkEnum aAudioOutputType,
+                                  DeviceEnumerationType aVideoInputEnumType /* = DeviceEnumerationType::Normal */,
+                                  DeviceEnumerationType aAudioInputEnumType /* = DeviceEnumerationType::Normal */)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aVideoType != MediaSourceEnum::Other ||
-             aAudioType != MediaSourceEnum::Other);
+  MOZ_ASSERT(aVideoInputType != MediaSourceEnum::Other ||
+             aAudioInputType != MediaSourceEnum::Other ||
+             aAudioOutputType != MediaSinkEnum::Other);
   // Since the enums can take one of several values, the following asserts rely
-  // on short circuting behavior. E.g. aVideoEnumType != Fake will be true if
+  // on short circuting behavior. E.g. aVideoInputEnumType != Fake will be true if
   // the requested device is not fake and thus the assert will pass. However,
-  // if the device is fake, aVideoType == MediaSourceEnum::Camera will be
+  // if the device is fake, aVideoInputType == MediaSourceEnum::Camera will be
   // checked as well, ensuring that fake devices are of the camera type.
-  MOZ_ASSERT(aVideoEnumType != DeviceEnumerationType::Fake ||
-             aVideoType == MediaSourceEnum::Camera,
+  MOZ_ASSERT(aVideoInputEnumType != DeviceEnumerationType::Fake ||
+             aVideoInputType == MediaSourceEnum::Camera,
              "If fake cams are requested video type should be camera!");
-  MOZ_ASSERT(aVideoEnumType != DeviceEnumerationType::Loopback ||
-             aVideoType == MediaSourceEnum::Camera,
+  MOZ_ASSERT(aVideoInputEnumType != DeviceEnumerationType::Loopback ||
+             aVideoInputType == MediaSourceEnum::Camera,
              "If loopback video is requested video type should be camera!");
-  MOZ_ASSERT(aAudioEnumType != DeviceEnumerationType::Fake ||
-             aAudioType == MediaSourceEnum::Microphone,
+  MOZ_ASSERT(aAudioInputEnumType != DeviceEnumerationType::Fake ||
+             aAudioInputType == MediaSourceEnum::Microphone,
              "If fake mics are requested audio type should be microphone!");
-  MOZ_ASSERT(aAudioEnumType != DeviceEnumerationType::Loopback ||
-             aAudioType == MediaSourceEnum::Microphone,
+  MOZ_ASSERT(aAudioInputEnumType != DeviceEnumerationType::Loopback ||
+             aAudioInputType == MediaSourceEnum::Microphone,
              "If loopback audio is requested audio type should be microphone!");
 
-  LOG(("%s: aWindowId=%" PRIu64 ", aVideoType=%" PRIu8 ", aAudioType=%" PRIu8
-       ", aVideoEnumType=%" PRIu8 ", aAudioEnumType=%" PRIu8,
+  LOG(("%s: aWindowId=%" PRIu64 ", aVideoInputType=%" PRIu8 ", aAudioInputType=%" PRIu8
+       ", aVideoInputEnumType=%" PRIu8 ", aAudioInputEnumType=%" PRIu8,
        __func__, aWindowId,
-       static_cast<uint8_t>(aVideoType), static_cast<uint8_t>(aAudioType),
-       static_cast<uint8_t>(aVideoEnumType), static_cast<uint8_t>(aAudioEnumType)));
+       static_cast<uint8_t>(aVideoInputType), static_cast<uint8_t>(aAudioInputType),
+       static_cast<uint8_t>(aVideoInputEnumType), static_cast<uint8_t>(aAudioInputEnumType)));
   RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
   uint32_t id = mOutstandingPledges.Append(*p);
 
-  bool hasVideo = aVideoType != MediaSourceEnum::Other;
-  bool hasAudio = aAudioType != MediaSourceEnum::Other;
-
-  // True of at least one of video or audio is a fake device
-  bool fakeDeviceRequested = (aVideoEnumType == DeviceEnumerationType::Fake && hasVideo) ||
-                             (aAudioEnumType == DeviceEnumerationType::Fake && hasAudio);
-  // True if at least one of video or audio is a real device
-  bool realDeviceRequested = (aVideoEnumType != DeviceEnumerationType::Fake && hasVideo) ||
-                             (aAudioEnumType != DeviceEnumerationType::Fake && hasAudio);
+  bool hasVideo = aVideoInputType != MediaSourceEnum::Other;
+  bool hasAudio = aAudioInputType != MediaSourceEnum::Other;
+  bool hasAudioOutput = aAudioOutputType == MediaSinkEnum::Speaker;
+
+  // True of at least one of video input or audio input is a fake device
+  bool fakeDeviceRequested = (aVideoInputEnumType == DeviceEnumerationType::Fake && hasVideo) ||
+                             (aAudioInputEnumType == DeviceEnumerationType::Fake && hasAudio);
+  // True if at least one of video input or audio input is a real device
+  // or there is audio output.
+  bool realDeviceRequested = (aVideoInputEnumType != DeviceEnumerationType::Fake && hasVideo) ||
+                             (aAudioInputEnumType != DeviceEnumerationType::Fake && hasAudio) ||
+                             hasAudioOutput;
 
   nsAutoCString videoLoopDev, audioLoopDev;
-  if (hasVideo && aVideoEnumType == DeviceEnumerationType::Loopback) {
+  if (hasVideo && aVideoInputEnumType == DeviceEnumerationType::Loopback) {
     Preferences::GetCString("media.video_loopback_dev", videoLoopDev);
   }
-  if (hasAudio && aAudioEnumType == DeviceEnumerationType::Loopback) {
+  if (hasAudio && aAudioInputEnumType == DeviceEnumerationType::Loopback) {
     Preferences::GetCString("media.audio_loopback_dev", audioLoopDev);
   }
 
-  RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, aVideoType, aAudioType,
-                                       aVideoEnumType, aAudioEnumType,
+  RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, aVideoInputType, aAudioInputType,
+                                       aVideoInputEnumType, aAudioInputEnumType,
                                        videoLoopDev, audioLoopDev,
-                                       hasVideo, hasAudio, fakeDeviceRequested,
-                                       realDeviceRequested]() {
+                                       hasVideo, hasAudio, hasAudioOutput,
+                                       fakeDeviceRequested, realDeviceRequested]() {
     // Only enumerate what's asked for, and only fake cams and mics.
     RefPtr<MediaEngine> fakeBackend, realBackend;
     if (fakeDeviceRequested) {
       fakeBackend = new MediaEngineDefault();
     }
     if (realDeviceRequested) {
       MediaManager* manager = MediaManager::GetIfExists();
       MOZ_RELEASE_ASSERT(manager); // Must exist while media thread is alive
       realBackend = manager->GetBackend(aWindowId);
     }
 
     auto result = MakeUnique<SourceSet>();
 
     if (hasVideo) {
       SourceSet videos;
       LOG(("EnumerateRawDevices Task: Getting video sources with %s backend",
-           aVideoEnumType == DeviceEnumerationType::Fake ? "fake" : "real"));
-      GetMediaDevices(aVideoEnumType == DeviceEnumerationType::Fake ? fakeBackend : realBackend,
-                      aWindowId, aVideoType, videos, videoLoopDev.get());
+           aVideoInputEnumType == DeviceEnumerationType::Fake ? "fake" : "real"));
+      GetMediaDevices(aVideoInputEnumType == DeviceEnumerationType::Fake ? fakeBackend : realBackend,
+                      aWindowId, aVideoInputType, videos, videoLoopDev.get());
       result->AppendElements(videos);
     }
     if (hasAudio) {
       SourceSet audios;
       LOG(("EnumerateRawDevices Task: Getting audio sources with %s backend",
-           aVideoEnumType == DeviceEnumerationType::Fake ? "fake" : "real"));
-      GetMediaDevices(aAudioEnumType == DeviceEnumerationType::Fake ? fakeBackend : realBackend,
-                      aWindowId, aAudioType, audios, audioLoopDev.get());
+           aAudioInputEnumType == DeviceEnumerationType::Fake ? "fake" : "real"));
+      GetMediaDevices(aAudioInputEnumType == DeviceEnumerationType::Fake ? fakeBackend : realBackend,
+                      aWindowId, aAudioInputType, audios, audioLoopDev.get());
       result->AppendElements(audios);
     }
+    if (hasAudioOutput) {
+      SourceSet outputs;
+      MOZ_ASSERT(realBackend);
+      realBackend->EnumerateDevices(aWindowId,
+                                    MediaSourceEnum::Other,
+                                    MediaSinkEnum::Speaker,
+                                    &outputs);
+      result->AppendElements(outputs);
+    }
+
     NS_DispatchToMainThread(NewRunnableFrom([id, result = std::move(result)]() mutable {
       MediaManager* mgr = MediaManager::GetIfExists();
       if (!mgr) {
         return NS_OK;
       }
       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
       if (p) {
         p->Resolve(result.release());
@@ -2020,19 +2035,19 @@ MediaManager::EnumerateRawDevices(uint64
     }));
   });
 
   if (realDeviceRequested &&
       Preferences::GetBool("media.navigator.permission.device", false)) {
     // Need to ask permission to retrieve list of all devices;
     // notify frontend observer and wait for callback notification to post task.
     const char16_t* const type =
-      (aVideoType != MediaSourceEnum::Camera)     ? u"audio" :
-      (aAudioType != MediaSourceEnum::Microphone) ? u"video" :
-                                                    u"all";
+      (aVideoInputType != MediaSourceEnum::Camera)     ? u"audio" :
+      (aAudioInputType != MediaSourceEnum::Microphone) ? u"video" :
+                                                         u"all";
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     obs->NotifyObservers(static_cast<nsIRunnable*>(task),
                          "getUserMedia:ask-device-permission",
                          type);
   } else {
     // Don't need to ask permission to retrieve list of all devices;
     // post the retrieval task immediately.
     MediaManager::PostTask(task.forget());
@@ -2341,22 +2356,23 @@ void MediaManager::OnDeviceChange() {
   RefPtr<MediaManager> self(this);
   NS_DispatchToMainThread(media::NewRunnableFrom([self]() mutable {
     MOZ_ASSERT(NS_IsMainThread());
     if (sHasShutdown) {
       return NS_OK;
     }
     self->DeviceChangeCallback::OnDeviceChange();
 
-    // On some Windows machine, if we call EnumertaeRawDevices immediately after receiving
+    // On some Windows machine, if we call EnumerateRawDevices immediately after receiving
     // devicechange event, sometimes we would get outdated devices list.
     PR_Sleep(PR_MillisecondsToInterval(100));
     RefPtr<PledgeSourceSet> p = self->EnumerateRawDevices(0,
                                                           MediaSourceEnum::Camera,
-                                                          MediaSourceEnum::Microphone);
+                                                          MediaSourceEnum::Microphone,
+                                                          MediaSinkEnum::Speaker);
     p->Then([self](SourceSet*& aDevices) mutable {
       UniquePtr<SourceSet> devices(aDevices);
       nsTArray<nsString> deviceIDs;
 
       for (auto& device : *devices) {
         nsString id;
         device->GetId(id);
         id.ReplaceSubstring(NS_LITERAL_STRING("default: "), NS_LITERAL_STRING(""));
@@ -2854,18 +2870,20 @@ MediaManager::GetUserMedia(nsPIDOMWindow
        ", videoType=%" PRIu8 ", audioType=%" PRIu8
        ", videoEnumerationType=%" PRIu8 ", audioEnumerationType=%" PRIu8
        ", askPermission=%s",
        __func__, windowID,
        static_cast<uint8_t>(videoType), static_cast<uint8_t>(audioType),
        static_cast<uint8_t>(videoEnumerationType), static_cast<uint8_t>(audioEnumerationType),
        askPermission ? "true" : "false"));
 
-  RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
+  RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID,
+                                                   videoType,
                                                    audioType,
+                                                   MediaSinkEnum::Other,
                                                    videoEnumerationType,
                                                    audioEnumerationType);
   RefPtr<MediaManager> self = this;
   p->Then([self, onSuccess, onFailure, windowID, c, windowListener,
            sourceListener, askPermission, prefs, isHTTPS, isHandlingUserInput,
            callID, principalInfo, isChrome, resistFingerprinting](SourceSet*& aDevices) mutable {
     LOG(("GetUserMedia: post enumeration pledge success callback starting"));
     // grab result
@@ -3073,28 +3091,29 @@ MediaManager::ToJSArray(SourceSet& aDevi
   } else {
     var->SetAsEmptyArray(); // because SetAsArray() fails on zero length arrays.
   }
   return var.forget();
 }
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
-                                   MediaSourceEnum aVideoType,
-                                   MediaSourceEnum aAudioType,
-                                   DeviceEnumerationType aVideoEnumType /* = DeviceEnumerationType::Normal */,
-                                   DeviceEnumerationType aAudioEnumType /* = DeviceEnumerationType::Normal */)
+                                   MediaSourceEnum       aVideoInputType,
+                                   MediaSourceEnum       aAudioInputType,
+                                   MediaSinkEnum  aAudioOutputType,
+                                   DeviceEnumerationType aVideoInputEnumType,
+                                   DeviceEnumerationType aAudioInputEnumType)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  LOG(("%s: aWindowId=%" PRIu64 ", aVideoType=%" PRIu8 ", aAudioType=%" PRIu8
-       ", aVideoEnumType=%" PRIu8 ", aAudioEnumType=%" PRIu8,
+  LOG(("%s: aWindowId=%" PRIu64 ", aVideoInputType=%" PRIu8 ", aAudioInputType=%" PRIu8
+       ", aVideoInputEnumType=%" PRIu8 ", aAudioInputEnumType=%" PRIu8,
        __func__, aWindowId,
-       static_cast<uint8_t>(aVideoType), static_cast<uint8_t>(aAudioType),
-       static_cast<uint8_t>(aVideoEnumType), static_cast<uint8_t>(aAudioEnumType)));
+       static_cast<uint8_t>(aVideoInputType), static_cast<uint8_t>(aAudioInputType),
+       static_cast<uint8_t>(aVideoInputEnumType), static_cast<uint8_t>(aAudioInputEnumType)));
   nsPIDOMWindowInner* window =
     nsGlobalWindowInner::GetInnerWindowWithId(aWindowId)->AsInner();
 
   // This function returns a pledge, a promise-like object with the future result
   RefPtr<PledgeSourceSet> pledge = new PledgeSourceSet();
   uint32_t id = mOutstandingPledges.Append(*pledge);
 
   // To get a device list anonymized for a particular origin, we must:
@@ -3119,50 +3138,51 @@ MediaManager::EnumerateDevicesImpl(uint6
   bool persist = IsActivelyCapturingOrHasAPermission(aWindowId);
 
   // GetPrincipalKey is an async API that returns a pledge (a promise-like
   // pattern). We use .Then() to pass in a lambda to run back on this same
   // thread later once GetPrincipalKey resolves. Needed variables are "captured"
   // (passed by value) safely into the lambda.
 
   RefPtr<Pledge<nsCString>> p = media::GetPrincipalKey(principalInfo, persist);
-  p->Then([id, aWindowId, aVideoType, aAudioType,
-          aVideoEnumType, aAudioEnumType](const nsCString& aOriginKey) mutable {
+  p->Then([id, aWindowId, aVideoInputType, aAudioInputType,
+          aVideoInputEnumType, aAudioInputEnumType, aAudioOutputType](const nsCString& aOriginKey) mutable {
     MOZ_ASSERT(NS_IsMainThread());
     MediaManager* mgr = MediaManager::GetIfExists();
     if (!mgr) {
       return;
     }
 
     RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId,
-                                                         aVideoType,
-                                                         aAudioType,
-                                                         aVideoEnumType,
-                                                         aAudioEnumType);
+                                                         aVideoInputType,
+                                                         aAudioInputType,
+                                                         aAudioOutputType,
+                                                         aVideoInputEnumType,
+                                                         aAudioInputEnumType);
     p->Then([id,
              aWindowId,
              aOriginKey,
-             aVideoEnumType,
-             aAudioEnumType,
-             aVideoType,
-             aAudioType](SourceSet*& aDevices) mutable {
+             aVideoInputEnumType,
+             aAudioInputEnumType,
+             aVideoInputType,
+             aAudioInputType](SourceSet*& aDevices) mutable {
       UniquePtr<SourceSet> devices(aDevices); // secondary result
 
       // Only run if window is still on our active list.
       MediaManager* mgr = MediaManager::GetIfExists();
       if (!mgr) {
         return NS_OK;
       }
 
       // If we fetched any real cameras or mics, remove the "default" part of
       // their IDs.
-      if (aVideoType == MediaSourceEnum::Camera &&
-          aAudioType == MediaSourceEnum::Microphone &&
-          (aVideoEnumType != DeviceEnumerationType::Fake ||
-           aAudioEnumType != DeviceEnumerationType::Fake)) {
+      if (aVideoInputType == MediaSourceEnum::Camera &&
+          aAudioInputType == MediaSourceEnum::Microphone &&
+          (aVideoInputEnumType != DeviceEnumerationType::Fake ||
+           aAudioInputEnumType != DeviceEnumerationType::Fake)) {
         mgr->mDeviceIDs.Clear();
         for (auto& device : *devices) {
           nsString id;
           device->GetId(id);
           id.ReplaceSubstring(NS_LITERAL_STRING("default: "), NS_LITERAL_STRING(""));
           if (!mgr->mDeviceIDs.Contains(id)) {
             mgr->mDeviceIDs.AppendElement(id);
           }
@@ -3240,16 +3260,17 @@ MediaManager::EnumerateDevices(nsPIDOMWi
     } else if (wantFakes) {
       audioEnumerationType = DeviceEnumerationType::Fake;
     }
   }
 
   RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId,
                                                    MediaSourceEnum::Camera,
                                                    MediaSourceEnum::Microphone,
+                                                   MediaSinkEnum::Speaker,
                                                    videoEnumerationType,
                                                    audioEnumerationType);
   p->Then([onSuccess, windowListener, sourceListener](SourceSet*& aDevices) mutable {
     UniquePtr<SourceSet> devices(aDevices); // grab result
     DebugOnly<bool> rv = windowListener->Remove(sourceListener);
     MOZ_ASSERT(rv);
     nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
     onSuccess->OnSuccess(array);
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -245,26 +245,28 @@ private:
   enum class DeviceEnumerationType :uint8_t {
     Normal, // Enumeration should not return loopback or fake devices
     Fake, // Enumeration should return fake device(s)
     Loopback /* Enumeration should return loopback device(s) (possibly in
              addition to normal devices) */
   };
   already_AddRefed<PledgeSourceSet>
   EnumerateRawDevices(uint64_t aWindowId,
-                      dom::MediaSourceEnum aVideoType,
-                      dom::MediaSourceEnum aAudioType,
-                      DeviceEnumerationType aVideoEnumType = DeviceEnumerationType::Normal,
-                      DeviceEnumerationType aAudioEnumType = DeviceEnumerationType::Normal);
+                      dom::MediaSourceEnum      aVideoInputType,
+                      dom::MediaSourceEnum      aAudioInputType,
+                      MediaSinkEnum             aAudioOutputType,
+                      DeviceEnumerationType     aVideoInputEnumType = DeviceEnumerationType::Normal,
+                      DeviceEnumerationType     aAudioInputEnumType = DeviceEnumerationType::Normal);
   already_AddRefed<PledgeSourceSet>
   EnumerateDevicesImpl(uint64_t aWindowId,
-                       dom::MediaSourceEnum aVideoType,
-                       dom::MediaSourceEnum aAudioType,
-                       DeviceEnumerationType aVideoEnumType = DeviceEnumerationType::Normal,
-                       DeviceEnumerationType aAudioEnumType = DeviceEnumerationType::Normal);
+                       dom::MediaSourceEnum      aVideoInputType,
+                       dom::MediaSourceEnum      aAudioInputType,
+                       MediaSinkEnum             aAudioOutputType,
+                       DeviceEnumerationType     aVideoInputEnumType,
+                       DeviceEnumerationType     aAudioInputEnumType);
   already_AddRefed<PledgeChar>
   SelectSettings(
       dom::MediaStreamConstraints& aConstraints,
       bool aIsChrome,
       RefPtr<media::Refcountable<UniquePtr<SourceSet>>>& aSources);
 
   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
                const char *aData, int32_t *aVal);
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -46,16 +46,17 @@ public:
   }
 
   /**
    * Populate an array of sources of the requested type in the nsTArray.
    * Also include devices that are currently unavailable.
    */
   virtual void EnumerateDevices(uint64_t aWindowId,
                                 dom::MediaSourceEnum,
+                                MediaSinkEnum,
                                 nsTArray<RefPtr<MediaDevice>>*) = 0;
 
   virtual void ReleaseResourcesForWindow(uint64_t aWindowId) = 0;
   virtual void Shutdown() = 0;
 
   virtual void SetFakeDeviceChangeEvents() {}
 
 protected:
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -549,16 +549,17 @@ MediaEngineDefaultAudioSource::Pull(cons
   mLastNotify += delta;
   AppendToSegment(segment, delta, aPrincipalHandle);
   aStream->AppendToTrack(aTrackID, &segment);
 }
 
 void
 MediaEngineDefault::EnumerateDevices(uint64_t aWindowId,
                                      dom::MediaSourceEnum aMediaSource,
+                                     MediaSinkEnum aMediaSink,
                                      nsTArray<RefPtr<MediaDevice>>* aDevices)
 {
   AssertIsOnOwningThread();
 
   switch (aMediaSource) {
     case dom::MediaSourceEnum::Camera: {
       // Only supports camera video sources. See Bug 1038241.
 
@@ -598,16 +599,20 @@ MediaEngineDefault::EnumerateDevices(uin
                                   NS_ConvertUTF8toUTF16(newSource->GetUUID())));
       }
       return;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported source type");
       return;
   }
+
+  if (aMediaSink == MediaSinkEnum::Speaker) {
+    NS_WARNING("No default implementation for MediaSinkEnum::Speaker");
+  }
 }
 
 void
 MediaEngineDefault::ReleaseResourcesForWindow(uint64_t aWindowId)
 {
   nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* audioDevicesForThisWindow =
    mASources.Get(aWindowId);
 
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -184,16 +184,17 @@ protected:
 
 class MediaEngineDefault : public MediaEngine
 {
 public:
   MediaEngineDefault() = default;
 
   void EnumerateDevices(uint64_t aWindowId,
                         dom::MediaSourceEnum,
+                        MediaSinkEnum,
                         nsTArray<RefPtr<MediaDevice>>*) override;
   void Shutdown() override;
   void ReleaseResourcesForWindow(uint64_t aWindowId) override;
 
 private:
   ~MediaEngineDefault() = default;
 
   // WindowID -> Array of devices.
--- a/dom/media/webrtc/MediaEngineSource.cpp
+++ b/dom/media/webrtc/MediaEngineSource.cpp
@@ -25,16 +25,17 @@ MediaEngineSource::IsVideo(MediaSourceEn
     case MediaSourceEnum::Camera:
     case MediaSourceEnum::Screen:
     case MediaSourceEnum::Application:
     case MediaSourceEnum::Window:
     case MediaSourceEnum::Browser:
       return true;
     case MediaSourceEnum::Microphone:
     case MediaSourceEnum::AudioCapture:
+    case MediaSourceEnum::Other:
       return false;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown type");
       return false;
   }
 }
 
 bool
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -308,35 +308,48 @@ MediaEngineWebRTC::EnumerateMicrophoneDe
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
                               micSource,
                               micSource->GetName(),
                               NS_ConvertUTF8toUTF16(micSource->GetUUID())));
   }
 }
 
 void
+MediaEngineWebRTC::EnumerateSpeakerDevices(uint64_t aWindowId,
+                                           nsTArray<RefPtr<MediaDevice> >* aDevices)
+{
+}
+
+void
 MediaEngineWebRTC::EnumerateDevices(uint64_t aWindowId,
                                     dom::MediaSourceEnum aMediaSource,
+                                    MediaSinkEnum aMediaSink,
                                     nsTArray<RefPtr<MediaDevice> >* aDevices)
 {
+  MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other ||
+             aMediaSink != MediaSinkEnum::Other);
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
   if (MediaEngineSource::IsVideo(aMediaSource)) {
     EnumerateVideoDevices(aWindowId, aMediaSource, aDevices);
   } else if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
       new MediaEngineWebRTCAudioCaptureSource(nullptr);
     aDevices->AppendElement(MakeRefPtr<MediaDevice>(
                               audioCaptureSource,
                               audioCaptureSource->GetName(),
                               NS_ConvertUTF8toUTF16(audioCaptureSource->GetUUID())));
-  } else {
+  } else if (aMediaSource == dom::MediaSourceEnum::Microphone) {
     MOZ_ASSERT(aMediaSource == dom::MediaSourceEnum::Microphone);
     EnumerateMicrophoneDevices(aWindowId, aDevices);
   }
+
+  if (aMediaSink == MediaSinkEnum::Speaker) {
+    EnumerateSpeakerDevices(aWindowId, aDevices);
+  }
 }
 
 bool
 MediaEngineWebRTC::SupportsDuplex()
 {
   return mFullDuplex;
 }
 
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -630,25 +630,28 @@ public:
   // before invoking Shutdown on this class.
   void Shutdown() override;
 
   // Returns whether the host supports duplex audio stream.
   bool SupportsDuplex();
 
   void EnumerateDevices(uint64_t aWindowId,
                         dom::MediaSourceEnum,
+                        MediaSinkEnum,
                         nsTArray<RefPtr<MediaDevice>>*) override;
   void ReleaseResourcesForWindow(uint64_t aWindowId) override;
 private:
   ~MediaEngineWebRTC() = default;
   void EnumerateVideoDevices(uint64_t aWindowId,
                              dom::MediaSourceEnum,
                              nsTArray<RefPtr<MediaDevice>>*);
   void EnumerateMicrophoneDevices(uint64_t aWindowId,
                                   nsTArray<RefPtr<MediaDevice>>*);
+  void EnumerateSpeakerDevices(uint64_t aWindowId,
+                               nsTArray<RefPtr<MediaDevice> >*);
 
   nsCOMPtr<nsIThread> mThread;
 
   // gUM runnables can e.g. Enumerate from multiple threads
   Mutex mMutex;
   RefPtr<mozilla::AudioInput> mAudioInput;
   bool mFullDuplex;
   bool mDelayAgnostic;