Bug 1197045 - part2: Expose audio channels, layout, sample-rate, and native device information via DOMWindowUtils; r=padenot
☠☠ backed out by ff34fb39ccb5 ☠ ☠
authorChun-Min Chang <chun.m.chang@gmail.com>
Tue, 18 Jul 2017 16:09:10 +0800
changeset 421660 cf756c62b0a61b02b1b599fda6c680240e7b6756
parent 421659 a4ea1d5fcb4e3de29b4d4df582c698da338f4ded
child 421661 fd48cc71efefb5c4eee80a8a6fcb6b540dac8567
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1197045
milestone56.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 1197045 - part2: Expose audio channels, layout, sample-rate, and native device information via DOMWindowUtils; r=padenot MozReview-Commit-ID: 9Kh2w0MioUQ
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/media/CubebUtils.cpp
dom/media/CubebUtils.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -70,16 +70,17 @@
 #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK)
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "gfxPrefs.h"
 
+#include "mozilla/dom/AudioDeviceInfo.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/IDBMutableFile.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/quota/PersistenceType.h"
@@ -2472,16 +2473,63 @@ nsDOMWindowUtils::GetSupportsHardwareH26
 NS_IMETHODIMP
 nsDOMWindowUtils::GetCurrentAudioBackend(nsAString& aBackend)
 {
   CubebUtils::GetCurrentBackend(aBackend);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetCurrentMaxAudioChannels(uint32_t* aChannels)
+{
+  *aChannels = CubebUtils::MaxNumberOfChannels();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetCurrentPreferredChannelLayout(nsAString& aLayout)
+{
+  CubebUtils::GetPreferredChannelLayout(aLayout);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetCurrentPreferredSampleRate(uint32_t* aRate)
+{
+  *aRate = CubebUtils::PreferredSampleRate();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::AudioDevices(uint16_t aSide, nsIArray** aDevices)
+{
+  NS_ENSURE_ARG_POINTER(aDevices);
+  NS_ENSURE_ARG((aSide == AUDIO_INPUT) || (aSide == AUDIO_OUTPUT));
+  *aDevices = nullptr;
+
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIMutableArray> devices =
+    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<RefPtr<AudioDeviceInfo>> collection;
+  CubebUtils::GetDeviceCollection(collection,
+                                  aSide == AUDIO_INPUT
+                                    ? CubebUtils::Side::Input
+                                    : CubebUtils::Side::Output);
+  for (auto device: collection) {
+    devices->AppendElement(device, false);
+  }
+
+  devices.forget(aDevices);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::StartFrameTimeRecording(uint32_t *startIndex)
 {
   NS_ENSURE_ARG_POINTER(startIndex);
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -24,16 +24,17 @@ class gfxContext;
 struct nsRect;
 %}
 
 [ref] native nsConstRect(const nsRect);
 native nscolor(nscolor);
 [ptr] native gfxContext(gfxContext);
 typedef unsigned long long nsViewID;
 
+interface nsIArray;
 interface nsICycleCollectorListener;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsIPreloadedStyleSheet;
 interface nsITransferable;
@@ -1422,16 +1423,38 @@ interface nsIDOMWindowUtils : nsISupport
   readonly attribute jsval supportsHardwareH264Decoding;
 
   /**
    * Returns the current audio backend as a free-form string.
    */
   readonly attribute AString currentAudioBackend;
 
   /**
+   * Returns the max channel counts of the current audio device.
+   */
+  readonly attribute unsigned long currentMaxAudioChannels;
+
+  /**
+   * Returns the preferred channel layout of the current audio device.
+   */
+  readonly attribute AString currentPreferredChannelLayout;
+
+  /**
+   * Returns the preferred sample rate of the current audio device.
+   */
+  readonly attribute unsigned long currentPreferredSampleRate;
+
+  /**
+   * Returns all the audio input/output devices.
+   */
+  const unsigned short AUDIO_INPUT   = 0;
+  const unsigned short AUDIO_OUTPUT  = 1;
+  nsIArray audioDevices(in unsigned short aSide);
+
+  /**
    * Record (and return) frame-intervals for frames which were presented
    *   between calling StartFrameTimeRecording and StopFrameTimeRecording.
    *
    * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
    *     (elements were overwritten since Start), result is considered invalid and hence empty.
    * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
    *     Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
    * - Note: the first frame-interval may be longer than expected because last frame
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -99,16 +99,43 @@ const char* AUDIOSTREAM_BACKEND_ID_STR[]
 /* Index for failures to create an audio stream the first time. */
 const int CUBEB_BACKEND_INIT_FAILURE_FIRST =
   ArrayLength(AUDIOSTREAM_BACKEND_ID_STR);
 /* Index for failures to create an audio stream after the first time */
 const int CUBEB_BACKEND_INIT_FAILURE_OTHER = CUBEB_BACKEND_INIT_FAILURE_FIRST + 1;
 /* Index for an unknown backend. */
 const int CUBEB_BACKEND_UNKNOWN = CUBEB_BACKEND_INIT_FAILURE_FIRST + 2;
 
+typedef struct {
+  const char* name;
+  const unsigned int channels;
+  const uint32_t mask;
+} layoutInfo;
+
+const layoutInfo kLayoutInfos[CUBEB_LAYOUT_MAX] = {
+  { "undefined",      0, 0 },               // CUBEB_LAYOUT_UNDEFINED
+  { "dual mono",      2, MASK_STEREO },     // CUBEB_LAYOUT_DUAL_MONO
+  { "dual mono lfe",  3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_DUAL_MONO_LFE
+  { "mono",           1, MASK_MONO },       // CUBEB_LAYOUT_MONO
+  { "mono lfe",       2, MASK_MONO_LFE },   // CUBEB_LAYOUT_MONO_LFE
+  { "stereo",         2, MASK_STEREO },     // CUBEB_LAYOUT_STEREO
+  { "stereo lfe",     3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_STEREO_LFE
+  { "3f",             3, MASK_3F },         // CUBEB_LAYOUT_3F
+  { "3f lfe",         4, MASK_3F_LFE },     // CUBEB_LAYOUT_3F_LFE
+  { "2f1",            3, MASK_2F1 },        // CUBEB_LAYOUT_2F1
+  { "2f1 lfe",        4, MASK_2F1_LFE },    // CUBEB_LAYOUT_2F1_LFE
+  { "3f1",            4, MASK_3F1 },        // CUBEB_LAYOUT_3F1
+  { "3f1 lfe",        5, MASK_3F1_LFE },    // CUBEB_LAYOUT_3F1_LFE
+  { "2f2",            4, MASK_2F2_LFE },    // CUBEB_LAYOUT_2F2
+  { "2f2 lfe",        5, MASK_2F2_LFE },    // CUBEB_LAYOUT_2F2_LFE
+  { "3f2",            5, MASK_3F2 },        // CUBEB_LAYOUT_3F2
+  { "3f2 lfe",        6, MASK_3F2_LFE },    // CUBEB_LAYOUT_3F2_LFE
+  { "3f3r lfe",       7, MASK_3F3R_LFE },   // CUBEB_LAYOUT_3F3R_LFE
+  { "3f4 lfe",        8, MASK_3F4_LFE }     // CUBEB_LAYOUT_3F4_LFE
+};
 
 // Prefered samplerate, in Hz (characteristic of the hardware, mixer, platform,
 // and API used).
 //
 // sMutex protects *initialization* of this, which must be performed from each
 // thread before fetching, after which it is safe to fetch without holding the
 // mutex because it is only written once per process execution (by the first
 // initialization to complete).  Since the init must have been called on a
@@ -259,52 +286,25 @@ bool InitPreferredChannelLayout()
 
   StaticMutexAutoLock lock(sMutex);
   sPreferredChannelLayout = layout;
   return true;
 }
 
 uint32_t PreferredChannelMap(uint32_t aChannels)
 {
-  // The first element of the following mapping table is channel counts,
-  // and the second one is its bit mask. It will be used in many times,
-  // so we shoule avoid to allocate it in stack, or it will be created
-  // and removed repeatedly. Use static to allocate this local variable
-  // in data space instead of stack.
-  static uint32_t layoutInfo[CUBEB_LAYOUT_MAX][2] = {
-    { 0, 0 },               // CUBEB_LAYOUT_UNDEFINED
-    { 2, MASK_STEREO },     // CUBEB_LAYOUT_DUAL_MONO
-    { 3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_DUAL_MONO_LFE
-    { 1, MASK_MONO },       // CUBEB_LAYOUT_MONO
-    { 2, MASK_MONO_LFE },   // CUBEB_LAYOUT_MONO_LFE
-    { 2, MASK_STEREO },     // CUBEB_LAYOUT_STEREO
-    { 3, MASK_STEREO_LFE }, // CUBEB_LAYOUT_STEREO_LFE
-    { 3, MASK_3F },         // CUBEB_LAYOUT_3F
-    { 4, MASK_3F_LFE },     // CUBEB_LAYOUT_3F_LFE
-    { 3, MASK_2F1 },        // CUBEB_LAYOUT_2F1
-    { 4, MASK_2F1_LFE },    // CUBEB_LAYOUT_2F1_LFE
-    { 4, MASK_3F1 },        // CUBEB_LAYOUT_3F1
-    { 5, MASK_3F1_LFE },    // CUBEB_LAYOUT_3F1_LFE
-    { 4, MASK_2F2 },        // CUBEB_LAYOUT_2F2
-    { 5, MASK_2F2_LFE },    // CUBEB_LAYOUT_2F2_LFE
-    { 5, MASK_3F2 },        // CUBEB_LAYOUT_3F2
-    { 6, MASK_3F2_LFE },    // CUBEB_LAYOUT_3F2_LFE
-    { 7, MASK_3F3R_LFE },   // CUBEB_LAYOUT_3F3R_LFE
-    { 8, MASK_3F4_LFE },    // CUBEB_LAYOUT_3F4_LFE
-  };
-
   // Use SMPTE default channel map if we can't get preferred layout
   // or the channel counts of preferred layout is different from input's one
   if (!InitPreferredChannelLayout()
-      || layoutInfo[sPreferredChannelLayout][0] != aChannels) {
+      || kLayoutInfos[sPreferredChannelLayout].channels != aChannels) {
     AudioConfig::ChannelLayout smpteLayout(aChannels);
     return smpteLayout.Map();
   }
 
-  return layoutInfo[sPreferredChannelLayout][1];
+  return kLayoutInfos[sPreferredChannelLayout].mask;
 }
 
 void InitBrandName()
 {
   if (sBrandName) {
     return;
   }
   nsXPIDLString brandName;
@@ -526,10 +526,110 @@ void GetCurrentBackend(nsAString& aBacke
     if (backend) {
       aBackend.AssignASCII(backend);
       return;
     }
   }
   aBackend.AssignLiteral("unknown");
 }
 
+void GetPreferredChannelLayout(nsAString& aLayout)
+{
+  const char* layout = InitPreferredChannelLayout() ?
+    kLayoutInfos[sPreferredChannelLayout].name : "unknown";
+  aLayout.AssignASCII(layout);
+}
+
+uint16_t ConvertCubebType(cubeb_device_type aType)
+{
+  uint16_t map[] = {
+    nsIAudioDeviceInfo::TYPE_UNKNOWN, // CUBEB_DEVICE_TYPE_UNKNOWN
+    nsIAudioDeviceInfo::TYPE_INPUT,   // CUBEB_DEVICE_TYPE_INPUT,
+    nsIAudioDeviceInfo::TYPE_OUTPUT   // CUBEB_DEVICE_TYPE_OUTPUT
+  };
+  return map[aType];
+}
+
+uint16_t ConvertCubebState(cubeb_device_state aState)
+{
+  uint16_t map[] = {
+    nsIAudioDeviceInfo::STATE_DISABLED,   // CUBEB_DEVICE_STATE_DISABLED
+    nsIAudioDeviceInfo::STATE_UNPLUGGED,  // CUBEB_DEVICE_STATE_UNPLUGGED
+    nsIAudioDeviceInfo::STATE_ENABLED     // CUBEB_DEVICE_STATE_ENABLED
+  };
+  return map[aState];
+}
+
+uint16_t ConvertCubebPreferred(cubeb_device_pref aPreferred)
+{
+  if (aPreferred == CUBEB_DEVICE_PREF_NONE) {
+    return nsIAudioDeviceInfo::PREF_NONE;
+  } else if (aPreferred == CUBEB_DEVICE_PREF_ALL) {
+    return nsIAudioDeviceInfo::PREF_ALL;
+  }
+
+  uint16_t preferred = 0;
+  if (aPreferred & CUBEB_DEVICE_PREF_MULTIMEDIA) {
+    preferred |= nsIAudioDeviceInfo::PREF_MULTIMEDIA;
+  }
+  if (aPreferred & CUBEB_DEVICE_PREF_VOICE) {
+    preferred |= nsIAudioDeviceInfo::PREF_VOICE;
+  }
+  if (aPreferred & CUBEB_DEVICE_PREF_NOTIFICATION) {
+    preferred |= nsIAudioDeviceInfo::PREF_NOTIFICATION;
+  }
+  return preferred;
+}
+
+uint16_t ConvertCubebFormat(cubeb_device_fmt aFormat)
+{
+  uint16_t format = 0;
+  if (aFormat & CUBEB_DEVICE_FMT_S16LE) {
+    format |= nsIAudioDeviceInfo::FMT_S16LE;
+  }
+  if (aFormat & CUBEB_DEVICE_FMT_S16BE) {
+    format |= nsIAudioDeviceInfo::FMT_S16BE;
+  }
+  if (aFormat & CUBEB_DEVICE_FMT_F32LE) {
+    format |= nsIAudioDeviceInfo::FMT_F32LE;
+  }
+  if (aFormat & CUBEB_DEVICE_FMT_F32BE) {
+    format |= nsIAudioDeviceInfo::FMT_F32BE;
+  }
+  return format;
+}
+
+void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
+                         Side aSide)
+{
+  cubeb* context = GetCubebContext();
+  if (context) {
+    cubeb_device_collection collection = { nullptr, 0 };
+    if (cubeb_enumerate_devices(context,
+                                aSide == Input ? CUBEB_DEVICE_TYPE_INPUT :
+                                                 CUBEB_DEVICE_TYPE_OUTPUT,
+                                &collection) == CUBEB_OK) {
+      for (unsigned int i = 0; i < collection.count; ++i) {
+        auto device = collection.device[i];
+        RefPtr<AudioDeviceInfo> info =
+          new AudioDeviceInfo(NS_ConvertASCIItoUTF16(device.friendly_name),
+                              NS_ConvertASCIItoUTF16(device.group_id),
+                              NS_ConvertASCIItoUTF16(device.vendor_name),
+                              ConvertCubebType(device.type),
+                              ConvertCubebState(device.state),
+                              ConvertCubebPreferred(device.preferred),
+                              ConvertCubebFormat(device.format),
+                              ConvertCubebFormat(device.default_format),
+                              device.max_channels,
+                              device.default_rate,
+                              device.max_rate,
+                              device.min_rate,
+                              device.latency_hi,
+                              device.latency_lo);
+        aDeviceInfos.AppendElement(info);
+      }
+    }
+    cubeb_device_collection_destroy(context, &collection);
+  }
+}
+
 } // namespace CubebUtils
 } // namespace mozilla
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.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/. */
 
 #if !defined(CubebUtils_h_)
 #define CubebUtils_h_
 
 #include "cubeb/cubeb.h"
+#include "mozilla/dom/AudioDeviceInfo.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Maybe.h"
 
 namespace mozilla {
 namespace CubebUtils {
 
 typedef cubeb_devid AudioDeviceID;
 
@@ -28,28 +29,35 @@ void ShutdownLibrary();
 uint32_t MaxNumberOfChannels();
 
 // Get the sample rate the hardware/mixer runs at. Thread safe.
 uint32_t PreferredSampleRate();
 
 // Get the bit mask of the connected audio device's preferred layout.
 uint32_t PreferredChannelMap(uint32_t aChannels);
 
+enum Side {
+  Input,
+  Output
+};
+
 void PrefChanged(const char* aPref, void* aClosure);
 double GetVolumeScale();
 bool GetFirstStream();
 cubeb* GetCubebContext();
 cubeb* GetCubebContextUnlocked();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebPlaybackLatencyInMilliseconds();
 Maybe<uint32_t> GetCubebMSGLatencyInFrames();
 bool CubebLatencyPrefSet();
 cubeb_channel_layout ConvertChannelMapToCubebLayout(uint32_t aChannelMap);
 #if defined(__ANDROID__) && defined(MOZ_B2G)
 cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
 #endif
 void GetCurrentBackend(nsAString& aBackend);
-
+void GetPreferredChannelLayout(nsAString& aLayout);
+void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
+                         Side aSide);
 } // namespace CubebUtils
 } // namespace mozilla
 
 #endif // CubebUtils_h_