summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2021-09-23 22:46:30 -0400
committerMatt A. Tobin <email@mattatobin.com>2021-09-23 22:46:30 -0400
commit6600d6128a3ff8e05aafb301521f5a3b7f351b0d (patch)
treedda4799f1e73eb4bbb0b809746e753230aea9453
parent8d99a8794771736ca946d775c90fc378363b756f (diff)
downloadaura-central-6600d6128a3ff8e05aafb301521f5a3b7f351b0d.tar.gz
Issue mcp-graveyard/UXP%1806 - Part 2: Adjust the platform for lib changes
-rw-r--r--dom/base/nsDOMWindowUtils.cpp41
-rw-r--r--dom/interfaces/base/nsIDOMWindowUtils.idl18
-rw-r--r--dom/media/AccurateSeekTask.cpp3
-rw-r--r--dom/media/AudioConverter.cpp2
-rw-r--r--dom/media/AudioDeviceInfo.cpp167
-rw-r--r--dom/media/AudioDeviceInfo.h52
-rw-r--r--dom/media/AudioStream.cpp3
-rw-r--r--dom/media/AudioStream.h5
-rw-r--r--dom/media/CubebUtils.cpp142
-rw-r--r--dom/media/CubebUtils.h10
-rw-r--r--dom/media/GraphDriver.cpp10
-rw-r--r--dom/media/MediaData.cpp3
-rw-r--r--dom/media/MediaData.h279
-rw-r--r--dom/media/MediaInfo.cpp194
-rw-r--r--dom/media/MediaInfo.h174
-rw-r--r--dom/media/MediaPrefs.h6
-rw-r--r--dom/media/mediasink/DecodedAudioDataSink.cpp22
-rw-r--r--dom/media/moz.build3
-rw-r--r--dom/media/nsIAudioDeviceInfo.idl54
-rw-r--r--dom/media/platforms/agnostic/VorbisDecoder.cpp59
-rw-r--r--dom/media/platforms/apple/AppleATDecoder.cpp28
-rw-r--r--dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp5
-rw-r--r--dom/media/platforms/wmf/WMFAudioMFTManager.cpp13
-rw-r--r--dom/media/platforms/wmf/WMFAudioMFTManager.h1
-rw-r--r--modules/libpref/init/all.js3
-rw-r--r--toolkit/content/aboutSupport.js106
-rw-r--r--toolkit/content/aboutSupport.xhtml85
-rw-r--r--toolkit/locales/en-US/chrome/global/aboutSupport.dtd13
-rw-r--r--toolkit/locales/en-US/chrome/global/aboutSupport.properties6
-rw-r--r--toolkit/modules/Troubleshoot.jsm46
30 files changed, 1305 insertions, 248 deletions
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
index d05f3b71f..eb09a6f14 100644
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -74,6 +74,7 @@
#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"
@@ -2371,6 +2372,46 @@ nsDOMWindowUtils::GetCurrentAudioBackend(nsAString& aBackend)
}
NS_IMETHODIMP
+nsDOMWindowUtils::GetCurrentMaxAudioChannels(uint32_t* aChannels)
+{
+ *aChannels = CubebUtils::MaxNumberOfChannels();
+ 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);
diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl
index 70ec7e0ae..200bd3caf 100644
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -29,6 +29,7 @@ native nscolor(nscolor);
[ptr] native gfxContext(gfxContext);
typedef unsigned long long nsViewID;
+interface nsIArray;
interface nsICycleCollectorListener;
interface nsIDOMNode;
interface nsIDOMNodeList;
@@ -1426,6 +1427,23 @@ interface nsIDOMWindowUtils : nsISupports {
readonly attribute AString currentAudioBackend;
/**
+ * Returns the max channel counts of the current audio device.
+ */
+ readonly attribute unsigned long currentMaxAudioChannels;
+
+ /**
+ * 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.
*
diff --git a/dom/media/AccurateSeekTask.cpp b/dom/media/AccurateSeekTask.cpp
index 30eeb5904..88ac67386 100644
--- a/dom/media/AccurateSeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -182,7 +182,8 @@ AccurateSeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
frames,
Move(audioData),
channels,
- audio->mRate));
+ audio->mRate,
+ audio->mChannelMap));
MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
mSeekedAudioData = data;
mDoneAudioSeeking = true;
diff --git a/dom/media/AudioConverter.cpp b/dom/media/AudioConverter.cpp
index ab9b11543..a77ea57f4 100644
--- a/dom/media/AudioConverter.cpp
+++ b/dom/media/AudioConverter.cpp
@@ -143,7 +143,7 @@ AudioConverter::DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const
mIn.Format() == AudioConfig::FORMAT_FLT);
MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() >= mOut.Channels());
MOZ_DIAGNOSTIC_ASSERT(
- mIn.Layout() == AudioConfig::ChannelLayout(mIn.Channels()),
+ mIn.Layout() == AudioConfig::ChannelLayout::SMPTEDefault(mIn.Layout()),
"Can only downmix input data in SMPTE layout");
MOZ_DIAGNOSTIC_ASSERT(mOut.Layout() == AudioConfig::ChannelLayout(2) ||
mOut.Layout() == AudioConfig::ChannelLayout(1),
diff --git a/dom/media/AudioDeviceInfo.cpp b/dom/media/AudioDeviceInfo.cpp
new file mode 100644
index 000000000..6f2721873
--- /dev/null
+++ b/dom/media/AudioDeviceInfo.cpp
@@ -0,0 +1,167 @@
+/* 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 "AudioDeviceInfo.h"
+
+NS_IMPL_ISUPPORTS(AudioDeviceInfo, nsIAudioDeviceInfo)
+
+AudioDeviceInfo::AudioDeviceInfo(const nsAString& aName,
+ const nsAString& aGroupId,
+ const nsAString& aVendor,
+ uint16_t aType,
+ uint16_t aState,
+ uint16_t aPreferred,
+ uint16_t aSupportedFormat,
+ uint16_t aDefaultFormat,
+ uint32_t aMaxChannels,
+ uint32_t aDefaultRate,
+ uint32_t aMaxRate,
+ uint32_t aMinRate,
+ uint32_t aMaxLatency,
+ uint32_t aMinLatency)
+ : mName(aName)
+ , mGroupId(aGroupId)
+ , mVendor(aVendor)
+ , mType(aType)
+ , mState(aState)
+ , mPreferred(aPreferred)
+ , mSupportedFormat(aSupportedFormat)
+ , mDefaultFormat(aDefaultFormat)
+ , mMaxChannels(aMaxChannels)
+ , mDefaultRate(aDefaultRate)
+ , mMaxRate(aMaxRate)
+ , mMinRate(aMinRate)
+ , mMaxLatency(aMaxLatency)
+ , mMinLatency(aMinLatency)
+{
+ MOZ_ASSERT(mType == TYPE_UNKNOWN ||
+ mType == TYPE_INPUT ||
+ mType == TYPE_OUTPUT, "Wrong type");
+ MOZ_ASSERT(mState == STATE_DISABLED ||
+ mState == STATE_UNPLUGGED ||
+ mState == STATE_ENABLED, "Wrong state");
+ MOZ_ASSERT(mPreferred == PREF_NONE ||
+ mPreferred == PREF_ALL ||
+ mPreferred & (PREF_MULTIMEDIA | PREF_VOICE | PREF_NOTIFICATION),
+ "Wrong preferred value");
+ MOZ_ASSERT(mSupportedFormat & (FMT_S16LE | FMT_S16BE | FMT_F32LE | FMT_F32BE),
+ "Wrong supported format");
+ MOZ_ASSERT(mDefaultFormat == FMT_S16LE ||
+ mDefaultFormat == FMT_S16BE ||
+ mDefaultFormat == FMT_F32LE ||
+ mDefaultFormat == FMT_F32BE, "Wrong default format");
+}
+
+/* readonly attribute DOMString name; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetName(nsAString& aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+/* readonly attribute DOMString groupId; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetGroupId(nsAString& aGroupId)
+{
+ aGroupId = mGroupId;
+ return NS_OK;
+}
+
+/* readonly attribute DOMString vendor; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetVendor(nsAString& aVendor)
+{
+ aVendor = mVendor;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned short type; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetType(uint16_t* aType)
+{
+ *aType = mType;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned short state; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetState(uint16_t* aState)
+{
+ *aState = mState;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned short preferred; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetPreferred(uint16_t* aPreferred)
+{
+ *aPreferred = mPreferred;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned short supportedFormat; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetSupportedFormat(uint16_t* aSupportedFormat)
+{
+ *aSupportedFormat = mSupportedFormat;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned short defaultFormat; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetDefaultFormat(uint16_t* aDefaultFormat)
+{
+ *aDefaultFormat = mDefaultFormat;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long maxChannels; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetMaxChannels(uint32_t* aMaxChannels)
+{
+ *aMaxChannels = mMaxChannels;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long defaultRate; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetDefaultRate(uint32_t* aDefaultRate)
+{
+ *aDefaultRate = mDefaultRate;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long maxRate; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetMaxRate(uint32_t* aMaxRate)
+{
+ *aMaxRate = mMaxRate;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long minRate; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetMinRate(uint32_t* aMinRate)
+{
+ *aMinRate = mMinRate;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long maxLatency; */
+NS_IMETHODIMP
+AudioDeviceInfo::GetMaxLatency(uint32_t* aMaxLatency)
+{
+ *aMaxLatency = mMaxLatency;
+ return NS_OK;
+}
+
+/* readonly attribute unsigned long minLatency; */
+NS_IMETHODIMP
+
+AudioDeviceInfo::GetMinLatency(uint32_t* aMinLatency)
+{
+ *aMinLatency = mMinLatency;
+ return NS_OK;
+}
diff --git a/dom/media/AudioDeviceInfo.h b/dom/media/AudioDeviceInfo.h
new file mode 100644
index 000000000..15930810d
--- /dev/null
+++ b/dom/media/AudioDeviceInfo.h
@@ -0,0 +1,52 @@
+/* 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_AudioDeviceInfo_H
+#define MOZILLA_AudioDeviceInfo_H
+
+#include "nsIAudioDeviceInfo.h"
+#include "nsString.h"
+
+// This is mapped to the cubeb_device_info.
+class AudioDeviceInfo final : public nsIAudioDeviceInfo
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIAUDIODEVICEINFO
+
+ explicit AudioDeviceInfo(const nsAString& aName,
+ const nsAString& aGroupId,
+ const nsAString& aVendor,
+ uint16_t aType,
+ uint16_t aState,
+ uint16_t aPreferred,
+ uint16_t aSupportedFormat,
+ uint16_t aDefaultFormat,
+ uint32_t aMaxChannels,
+ uint32_t aDefaultRate,
+ uint32_t aMaxRate,
+ uint32_t aMinRate,
+ uint32_t aMaxLatency,
+ uint32_t aMinLatency);
+
+private:
+ virtual ~AudioDeviceInfo() = default;
+
+ nsString mName;
+ nsString mGroupId;
+ nsString mVendor;
+ uint16_t mType;
+ uint16_t mState;
+ uint16_t mPreferred;
+ uint16_t mSupportedFormat;
+ uint16_t mDefaultFormat;
+ uint32_t mMaxChannels;
+ uint32_t mDefaultRate;
+ uint32_t mMaxRate;
+ uint32_t mMinRate;
+ uint32_t mMaxLatency;
+ uint32_t mMinLatency;
+};
+
+#endif // MOZILLA_AudioDeviceInfo_H
diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp
index 77ed512ee..dc1daa2e0 100644
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -317,7 +317,7 @@ int AudioStream::InvokeCubeb(Function aFunction, Args&&... aArgs)
}
nsresult
-AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
+AudioStream::Init(uint32_t aNumChannels, uint32_t aChannelMap, uint32_t aRate,
const dom::AudioChannel aAudioChannel)
{
auto startTime = TimeStamp::Now();
@@ -331,6 +331,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
cubeb_stream_params params;
params.rate = aRate;
params.channels = mOutChannels;
+ params.layout = CUBEB_LAYOUT_UNDEFINED;
params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
mAudioClock.Init(aRate);
diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h
index 199314d4b..e138ab991 100644
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -192,9 +192,10 @@ public:
explicit AudioStream(DataSource& aSource);
// Initialize the audio stream. aNumChannels is the number of audio
- // channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
+ // channels (1 for mono, 2 for stereo, etc), aChannelMap is the indicator for
+ // channel layout(mono, stereo, 5.1 or 7.1 ) and aRate is the sample rate
// (22050Hz, 44100Hz, etc).
- nsresult Init(uint32_t aNumChannels, uint32_t aRate,
+ nsresult Init(uint32_t aNumChannels, uint32_t aChannelMap, uint32_t aRate,
const dom::AudioChannel aAudioStreamChannel);
// Closes the stream. All future use of the stream is an error.
diff --git a/dom/media/CubebUtils.cpp b/dom/media/CubebUtils.cpp
index aa611973d..f5f5cb3b1 100644
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -22,6 +22,7 @@
#include "prdtoa.h"
#define PREF_VOLUME_SCALE "media.volume_scale"
+#define PREF_CUBEB_BACKEND "media.cubeb.backend"
#define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
#define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
@@ -50,13 +51,14 @@ enum class CubebState {
Shutdown
} sCubebState = CubebState::Uninitialized;
cubeb* sCubebContext;
-double sVolumeScale;
-uint32_t sCubebPlaybackLatencyInMilliseconds;
-uint32_t sCubebMSGLatencyInFrames;
-bool sCubebPlaybackLatencyPrefSet;
-bool sCubebMSGLatencyPrefSet;
+double sVolumeScale = 1.0;
+uint32_t sCubebPlaybackLatencyInMilliseconds = 100;
+uint32_t sCubebMSGLatencyInFrames = 512;
+bool sCubebPlaybackLatencyPrefSet = false;
+bool sCubebMSGLatencyPrefSet = false;
bool sAudioStreamInitEverSucceeded = false;
StaticAutoPtr<char> sBrandName;
+StaticAutoPtr<char> sCubebBackendName;
const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
@@ -133,9 +135,21 @@ void PrefChanged(const char* aPref, void* aClosure)
// We don't want to limit the upper limit too much, so that people can
// experiment.
sCubebMSGLatencyInFrames = std::min<uint32_t>(std::max<uint32_t>(value, 128), 1e6);
+ } else if (strcmp(aPref, PREF_CUBEB_BACKEND) == 0) {
+ nsAdoptingString value = Preferences::GetString(aPref);
+ if (value.IsEmpty()) {
+ sCubebBackendName = nullptr;
+ } else {
+ NS_LossyConvertUTF16toASCII ascii(value);
+ sCubebBackendName = new char[ascii.Length() + 1];
+ PodCopy(sCubebBackendName.get(), ascii.get(), ascii.Length());
+ sCubebBackendName[ascii.Length()] = 0;
+ }
}
}
+
+
bool GetFirstStream()
{
static bool sFirstStream = true;
@@ -227,7 +241,7 @@ cubeb* GetCubebContextUnlocked()
sBrandName, "Did not initialize sbrandName, and not on the main thread?");
}
- int rv = cubeb_init(&sCubebContext, sBrandName);
+ int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context.");
sCubebState = (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
@@ -258,14 +272,23 @@ bool CubebMSGLatencyPrefSet()
return sCubebMSGLatencyPrefSet;
}
-Maybe<uint32_t> GetCubebMSGLatencyInFrames()
+uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params * params)
{
StaticMutexAutoLock lock(sMutex);
- if (!sCubebMSGLatencyPrefSet) {
- return Maybe<uint32_t>();
+ if (sCubebMSGLatencyPrefSet) {
+ MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
+ return sCubebMSGLatencyInFrames;
+ }
+ cubeb* context = GetCubebContextUnlocked();
+ if (!context) {
+ return sCubebMSGLatencyInFrames; // default 512
}
- MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
- return Some(sCubebMSGLatencyInFrames);
+ uint32_t latency_frames = 0;
+ if (cubeb_get_min_latency(context, params, &latency_frames) != CUBEB_OK) {
+ NS_WARNING("Could not get minimal latency from cubeb.");
+ return sCubebMSGLatencyInFrames; // default 512
+ }
+ return latency_frames;
}
void InitLibrary()
@@ -274,6 +297,8 @@ void InitLibrary()
Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
PrefChanged(PREF_CUBEB_LATENCY_PLAYBACK, nullptr);
PrefChanged(PREF_CUBEB_LATENCY_MSG, nullptr);
+ PrefChanged(PREF_CUBEB_BACKEND, nullptr);
+ Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_BACKEND);
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
NS_DispatchToMainThread(NS_NewRunnableFunction(&InitBrandName));
@@ -282,6 +307,7 @@ void InitLibrary()
void ShutdownLibrary()
{
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
+ Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_BACKEND);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
@@ -291,6 +317,7 @@ void ShutdownLibrary()
sCubebContext = nullptr;
}
sBrandName = nullptr;
+ sCubebBackendName = nullptr;
// This will ensure we don't try to re-create a context.
sCubebState = CubebState::Shutdown;
}
@@ -321,5 +348,98 @@ void GetCurrentBackend(nsAString& aBackend)
aBackend.AssignLiteral("unknown");
}
+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
diff --git a/dom/media/CubebUtils.h b/dom/media/CubebUtils.h
index f43492374..844946244 100644
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -8,6 +8,7 @@
#define CubebUtils_h_
#include "cubeb/cubeb.h"
+#include "mozilla/dom/AudioDeviceInfo.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/Maybe.h"
@@ -30,15 +31,22 @@ uint32_t MaxNumberOfChannels();
// Get the sample rate the hardware/mixer runs at. Thread safe.
uint32_t PreferredSampleRate();
+enum Side {
+ Input,
+ Output
+};
+
void PrefChanged(const char* aPref, void* aClosure);
double GetVolumeScale();
bool GetFirstStream();
cubeb* GetCubebContext();
cubeb* GetCubebContextUnlocked();
uint32_t GetCubebPlaybackLatencyInMilliseconds();
-Maybe<uint32_t> GetCubebMSGLatencyInFrames();
+uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params * params);
bool CubebLatencyPrefSet();
void GetCurrentBackend(nsAString& aBackend);
+void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
+ Side aSide);
} // namespace CubebUtils
} // namespace mozilla
diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp
index 90680d8c6..70b59c61f 100644
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -593,7 +593,6 @@ AudioCallbackDriver::Init()
cubeb_stream_params output;
cubeb_stream_params input;
- uint32_t latency_frames;
MOZ_ASSERT(!NS_IsMainThread(),
"This is blocking and should never run on the main thread.");
@@ -609,14 +608,7 @@ AudioCallbackDriver::Init()
output.format = CUBEB_SAMPLE_FLOAT32NE;
}
- Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
- if (latencyPref) {
- latency_frames = latencyPref.value();
- } else {
- if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
- NS_WARNING("Could not get minimal latency from cubeb.");
- }
- }
+ uint32_t latency_frames = CubebUtils::GetCubebMSGLatencyInFrames(&output);
input = output;
input.channels = mInputChannels; // change to support optional stereo capture
diff --git a/dom/media/MediaData.cpp b/dom/media/MediaData.cpp
index fb3eff7b5..5b68200ef 100644
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -76,7 +76,8 @@ AudioData::TransferAndUpdateTimestampAndDuration(AudioData* aOther,
aOther->mFrames,
Move(aOther->mAudioData),
aOther->mChannels,
- aOther->mRate);
+ aOther->mRate,
+ aOther->mChannelMap);
return v.forget();
}
diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h
index bec9c6076..dd1d0243e 100644
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -22,6 +22,276 @@
namespace mozilla {
+ // Maximum channel number we can currently handle (7.1)
+#define MAX_AUDIO_CHANNELS 8
+
+class AudioConfig {
+public:
+// Channel definition is conveniently defined to be in the same order as
+// WAVEFORMAT && SMPTE, even though this is unused for now.
+ enum Channel {
+ CHANNEL_INVALID = -1,
+ CHANNEL_FRONT_LEFT = 0,
+ CHANNEL_FRONT_RIGHT,
+ CHANNEL_FRONT_CENTER,
+ CHANNEL_LFE,
+ CHANNEL_BACK_LEFT,
+ CHANNEL_BACK_RIGHT,
+ CHANNEL_FRONT_LEFT_OF_CENTER,
+ CHANNEL_FRONT_RIGHT_OF_CENTER,
+ CHANNEL_BACK_CENTER,
+ CHANNEL_SIDE_LEFT,
+ CHANNEL_SIDE_RIGHT,
+ // From WAVEFORMAT definition.
+ CHANNEL_TOP_CENTER,
+ CHANNEL_TOP_FRONT_LEFT,
+ CHANNEL_TOP_FRONT_CENTER,
+ CHANNEL_TOP_FRONT_RIGHT,
+ CHANNEL_TOP_BACK_LEFT,
+ CHANNEL_TOP_BACK_CENTER,
+ CHANNEL_TOP_BACK_RIGHT
+ };
+
+ class ChannelLayout {
+ public:
+ ChannelLayout()
+ : mChannelMap(0)
+ , mValid(false)
+ {}
+ explicit ChannelLayout(uint32_t aChannels)
+ : ChannelLayout(aChannels, DefaultLayoutForChannels(aChannels))
+ {}
+ ChannelLayout(uint32_t aChannels, const Channel* aConfig)
+ : ChannelLayout()
+ {
+ if (aChannels == 0 || !aConfig) {
+ mValid = false;
+ return;
+ }
+ mChannels.AppendElements(aConfig, aChannels);
+ UpdateChannelMap();
+ }
+ ChannelLayout(std::initializer_list<Channel> aChannelList)
+ : ChannelLayout(aChannelList.size(), aChannelList.begin())
+ {
+ }
+ bool operator==(const ChannelLayout& aOther) const
+ {
+ return mChannels == aOther.mChannels;
+ }
+ bool operator!=(const ChannelLayout& aOther) const
+ {
+ return mChannels != aOther.mChannels;
+ }
+ const Channel& operator[](uint32_t aIndex) const
+ {
+ return mChannels[aIndex];
+ }
+ uint32_t Count() const
+ {
+ return mChannels.Length();
+ }
+ uint32_t Map() const;
+
+ // Calculate the mapping table from the current layout to aOther such that
+ // one can easily go from one layout to the other by doing:
+ // out[channel] = in[map[channel]].
+ // Returns true if the reordering is possible or false otherwise.
+ // If true, then aMap, if set, will be updated to contain the mapping table
+ // allowing conversion from the current layout to aOther.
+ // If aMap is nullptr, then MappingTable can be used to simply determine if
+ // the current layout can be easily reordered to aOther.
+ // aMap must be an array of size MAX_AUDIO_CHANNELS.
+ bool MappingTable(const ChannelLayout& aOther, uint8_t* aMap = nullptr) const;
+ bool IsValid() const {
+ return mValid;
+ }
+ bool HasChannel(Channel aChannel) const
+ {
+ return mChannelMap & (1 << aChannel);
+ }
+
+ static ChannelLayout SMPTEDefault(
+ const ChannelLayout& aChannelLayout);
+ static ChannelLayout SMPTEDefault(uint32_t aMap);
+
+ static constexpr uint32_t UNKNOWN_MAP = 0;
+
+ // Common channel layout definitions.
+ static ChannelLayout LMONO;
+ static constexpr uint32_t LMONO_MAP = 1 << CHANNEL_FRONT_CENTER;
+ static ChannelLayout LMONO_LFE;
+ static constexpr uint32_t LMONO_LFE_MAP =
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE;
+ static ChannelLayout LSTEREO;
+ static constexpr uint32_t LSTEREO_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT;
+ static ChannelLayout LSTEREO_LFE;
+ static constexpr uint32_t LSTEREO_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT | 1 << CHANNEL_LFE;
+ static ChannelLayout L3F;
+ static constexpr uint32_t L3F_MAP = 1 << CHANNEL_FRONT_LEFT |
+ 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER;
+ static ChannelLayout L3F_LFE;
+ static constexpr uint32_t L3F_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE;
+ static ChannelLayout L2F1;
+ static constexpr uint32_t L2F1_MAP = 1 << CHANNEL_FRONT_LEFT |
+ 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_BACK_CENTER;
+ static ChannelLayout L2F1_LFE;
+ static constexpr uint32_t L2F1_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT | 1 << CHANNEL_LFE |
+ 1 << CHANNEL_BACK_CENTER;
+ static ChannelLayout L3F1;
+ static constexpr uint32_t L3F1_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_BACK_CENTER;
+ static ChannelLayout LSURROUND; // Same as 3F1
+ static constexpr uint32_t LSURROUND_MAP = L3F1_MAP;
+ static ChannelLayout L3F1_LFE;
+ static constexpr uint32_t L3F1_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE | 1 << CHANNEL_BACK_CENTER;
+ static ChannelLayout L2F2;
+ static constexpr uint32_t L2F2_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_SIDE_LEFT | 1 << CHANNEL_SIDE_RIGHT;
+ static ChannelLayout L2F2_LFE;
+ static constexpr uint32_t L2F2_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT | 1 << CHANNEL_LFE |
+ 1 << CHANNEL_SIDE_LEFT | 1 << CHANNEL_SIDE_RIGHT;
+ static ChannelLayout LQUAD;
+ static constexpr uint32_t LQUAD_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_BACK_LEFT | 1 << CHANNEL_BACK_RIGHT;
+ static ChannelLayout LQUAD_LFE;
+ static constexpr uint32_t LQUAD_MAP_LFE =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT | 1 << CHANNEL_LFE |
+ 1 << CHANNEL_BACK_LEFT | 1 << CHANNEL_BACK_RIGHT;
+ static ChannelLayout L3F2;
+ static constexpr uint32_t L3F2_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_SIDE_LEFT |
+ 1 << CHANNEL_SIDE_RIGHT;
+ static ChannelLayout L3F2_LFE;
+ static constexpr uint32_t L3F2_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE | 1 << CHANNEL_SIDE_LEFT |
+ 1 << CHANNEL_SIDE_RIGHT;
+ // 3F2_LFE Alias
+ static ChannelLayout L5POINT1_SURROUND;
+ static constexpr uint32_t L5POINT1_SURROUND_MAP = L3F2_LFE_MAP;
+ static ChannelLayout L3F3R_LFE;
+ static constexpr uint32_t L3F3R_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE | 1 << CHANNEL_BACK_CENTER |
+ 1 << CHANNEL_SIDE_LEFT | 1 << CHANNEL_SIDE_RIGHT;
+ static ChannelLayout L3F4_LFE;
+ static constexpr uint32_t L3F4_LFE_MAP =
+ 1 << CHANNEL_FRONT_LEFT | 1 << CHANNEL_FRONT_RIGHT |
+ 1 << CHANNEL_FRONT_CENTER | 1 << CHANNEL_LFE | 1 << CHANNEL_BACK_LEFT |
+ 1 << CHANNEL_BACK_RIGHT | 1 << CHANNEL_SIDE_LEFT |
+ 1 << CHANNEL_SIDE_RIGHT;
+ // 3F4_LFE Alias
+ static ChannelLayout L7POINT1_SURROUND;
+ static constexpr uint32_t L7POINT1_SURROUND_MAP = L3F4_LFE_MAP;
+
+ private:
+ void UpdateChannelMap();
+ const Channel* DefaultLayoutForChannels(uint32_t aChannels) const;
+ AutoTArray<Channel, MAX_AUDIO_CHANNELS> mChannels;
+ uint32_t mChannelMap;
+ bool mValid;
+ };
+
+ enum SampleFormat {
+ FORMAT_NONE = 0,
+ FORMAT_U8,
+ FORMAT_S16,
+ FORMAT_S24LSB,
+ FORMAT_S24,
+ FORMAT_S32,
+ FORMAT_FLT,
+#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
+ FORMAT_DEFAULT = FORMAT_FLT
+#elif defined(MOZ_SAMPLE_TYPE_S16)
+ FORMAT_DEFAULT = FORMAT_S16
+#else
+#error "Not supported audio type"
+#endif
+ };
+
+ AudioConfig(const ChannelLayout& aChannelLayout, uint32_t aRate,
+ AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
+ bool aInterleaved = true);
+ // Will create a channel configuration from default SMPTE ordering.
+ AudioConfig(uint32_t aChannels, uint32_t aRate,
+ AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
+ bool aInterleaved = true);
+
+ const ChannelLayout& Layout() const
+ {
+ return mChannelLayout;
+ }
+ uint32_t Channels() const
+ {
+ if (!mChannelLayout.IsValid()) {
+ return mChannels;
+ }
+ return mChannelLayout.Count();
+ }
+ uint32_t Rate() const
+ {
+ return mRate;
+ }
+ SampleFormat Format() const
+ {
+ return mFormat;
+ }
+ bool Interleaved() const
+ {
+ return mInterleaved;
+ }
+ bool operator==(const AudioConfig& aOther) const
+ {
+ return mChannelLayout == aOther.mChannelLayout &&
+ mRate == aOther.mRate && mFormat == aOther.mFormat &&
+ mInterleaved == aOther.mInterleaved;
+ }
+ bool operator!=(const AudioConfig& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ bool IsValid() const
+ {
+ return mChannelLayout.IsValid() && Format() != FORMAT_NONE && Rate() > 0;
+ }
+
+ static const char* FormatToString(SampleFormat aFormat);
+ static uint32_t SampleSize(SampleFormat aFormat);
+ static uint32_t FormatToBits(SampleFormat aFormat);
+
+private:
+ // Channels configuration.
+ ChannelLayout mChannelLayout;
+
+ // Channel count.
+ uint32_t mChannels;
+
+ // Sample rate.
+ uint32_t mRate;
+
+ // Sample format.
+ SampleFormat mFormat;
+
+ bool mInterleaved;
+};
+
+
namespace layers {
class Image;
class ImageContainer;
@@ -375,9 +645,11 @@ public:
uint32_t aFrames,
AlignedAudioBuffer&& aData,
uint32_t aChannels,
- uint32_t aRate)
+ uint32_t aRate,
+ uint32_t aChannelMap = AudioConfig::ChannelLayout::UNKNOWN_MAP)
: MediaData(sType, aOffset, aTime, aDuration, aFrames)
, mChannels(aChannels)
+ , mChannelMap(aChannelMap)
, mRate(aRate)
, mAudioData(Move(aData)) {}
@@ -403,6 +675,11 @@ public:
bool IsAudible() const;
const uint32_t mChannels;
+ // The AudioConfig::ChannelLayout map. Channels are ordered as per SMPTE
+ // definition. A value of UNKNOWN_MAP indicates unknown layout.
+ // ChannelMap is an unsigned bitmap compatible with Windows' WAVE and FFmpeg
+ // channel map.
+ const uint32_t mChannelMap;
const uint32_t mRate;
// At least one of mAudioBuffer/mAudioData must be non-null.
// mChannels channels, each with mFrames frames
diff --git a/dom/media/MediaInfo.cpp b/dom/media/MediaInfo.cpp
index 568183e68..28b9e7782 100644
--- a/dom/media/MediaInfo.cpp
+++ b/dom/media/MediaInfo.cpp
@@ -35,69 +35,235 @@ typedef AudioConfig::ChannelLayout ChannelLayout;
3F4-LFE L R C LFE Rls Rrs LS RS
*/
+ChannelLayout ChannelLayout::LMONO{ AudioConfig::CHANNEL_FRONT_CENTER };
+ChannelLayout ChannelLayout::LMONO_LFE{ AudioConfig::CHANNEL_FRONT_CENTER, AudioConfig::CHANNEL_LFE };
+ChannelLayout ChannelLayout::LSTEREO{ AudioConfig::CHANNEL_FRONT_LEFT, AudioConfig::CHANNEL_FRONT_RIGHT };
+ChannelLayout ChannelLayout::LSTEREO_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_LFE };
+ChannelLayout ChannelLayout::L3F{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER };
+ChannelLayout ChannelLayout::L3F_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_LFE };
+ChannelLayout ChannelLayout::L2F1{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_BACK_CENTER };
+ChannelLayout ChannelLayout::L2F1_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_BACK_CENTER };
+ChannelLayout ChannelLayout::L3F1{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_BACK_CENTER };
+ChannelLayout ChannelLayout::LSURROUND = ChannelLayout::L3F1;
+ChannelLayout ChannelLayout::L3F1_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_BACK_CENTER };
+ChannelLayout ChannelLayout::L2F2{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::L2F2_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::LQUAD{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT };
+ChannelLayout ChannelLayout::LQUAD_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT };
+ChannelLayout ChannelLayout::L3F2{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::L3F2_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::L5POINT1_SURROUND = ChannelLayout::L3F2_LFE;
+
+ChannelLayout ChannelLayout::L3F3R_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_BACK_CENTER,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::L3F4_LFE{ AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_LFE,
+ AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT,
+ AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT };
+ChannelLayout ChannelLayout::L7POINT1_SURROUND = ChannelLayout::L3F4_LFE;
+
void
AudioConfig::ChannelLayout::UpdateChannelMap()
{
- mChannelMap = 0;
mValid = mChannels.Length() <= MAX_AUDIO_CHANNELS;
+ mChannelMap = 0;
+ if (mValid) {
+ mChannelMap = Map();
+ mValid = mChannelMap > 0;
+ }
+}
+
+uint32_t
+AudioConfig::ChannelLayout::Map() const
+{
+ if (mChannelMap) {
+ return mChannelMap;
+ }
+ uint32_t map = 0;
for (size_t i = 0; i < mChannels.Length() && i <= MAX_AUDIO_CHANNELS; i++) {
uint32_t mask = 1 << mChannels[i];
if (mChannels[i] == CHANNEL_INVALID || (mChannelMap & mask)) {
- mValid = false;
+ // Invalid configuration.
+ return 0;
}
- mChannelMap |= mask;
+ map |= mask;
}
+ return map;
}
-/* static */ const AudioConfig::Channel*
-AudioConfig::ChannelLayout::SMPTEDefault(uint32_t aChannels) const
+const AudioConfig::Channel*
+AudioConfig::ChannelLayout::DefaultLayoutForChannels(uint32_t aChannels) const
{
switch (aChannels) {
case 1: // MONO
{
- static const Channel config[] = { CHANNEL_MONO };
+ static const Channel config[] = { CHANNEL_FRONT_CENTER };
return config;
}
case 2: // STEREO
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT };
return config;
}
case 3: // 3F
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER };
return config;
}
- case 4: // 2F2
+ case 4: // QUAD
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT };
return config;
}
case 5: // 3F2
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LS, CHANNEL_RS };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
return config;
}
case 6: // 3F2-LFE
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
return config;
}
case 7: // 3F3R-LFE
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER, CHANNEL_LS, CHANNEL_RS };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_BACK_CENTER, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
return config;
}
case 8: // 3F4-LFE
{
- static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RLS, CHANNEL_RRS, CHANNEL_LS, CHANNEL_RS };
+ static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
return config;
}
default:
return nullptr;
}
+
}
+/* static */ AudioConfig::ChannelLayout
+AudioConfig::ChannelLayout::SMPTEDefault(
+ const ChannelLayout& aChannelLayout)
+{
+ if (!aChannelLayout.IsValid()) {
+ return aChannelLayout;
+ }
+ return SMPTEDefault(aChannelLayout.Map());
+}
+
+/* static */ ChannelLayout
+AudioConfig::ChannelLayout::SMPTEDefault(uint32_t aMap)
+{
+ MOZ_ASSERT(LMONO_MAP == LMONO.Map());
+ MOZ_ASSERT(LMONO_LFE_MAP == LMONO_LFE.Map());
+ MOZ_ASSERT(LSTEREO_MAP == LSTEREO.Map());
+ MOZ_ASSERT(LSTEREO_LFE_MAP == LSTEREO_LFE.Map());
+ MOZ_ASSERT(L3F_MAP == L3F.Map());
+ MOZ_ASSERT(L3F_LFE_MAP == L3F_LFE.Map());
+ MOZ_ASSERT(L2F1_MAP == L2F1.Map());
+ MOZ_ASSERT(L2F1_LFE_MAP == L2F1_LFE.Map());
+ MOZ_ASSERT(L3F1_MAP == L3F1.Map());
+ MOZ_ASSERT(L3F1_LFE_MAP == L3F1_LFE.Map());
+ MOZ_ASSERT(L2F2_MAP == L2F2.Map());
+ MOZ_ASSERT(L2F2_LFE_MAP == L2F2_LFE.Map());
+ MOZ_ASSERT(LQUAD_MAP == LQUAD.Map());
+ MOZ_ASSERT(L3F2_MAP == L3F2.Map());
+ MOZ_ASSERT(L3F2_LFE_MAP == L3F2_LFE.Map());
+ MOZ_ASSERT(L3F3R_LFE_MAP == L3F3R_LFE.Map());
+ MOZ_ASSERT(L3F4_LFE_MAP == L3F4_LFE.Map());
+
+ // First handle the most common cases.
+ switch (aMap) {
+ case LMONO_MAP: return LMONO;
+ case LMONO_LFE_MAP: return LMONO_LFE;
+ case LSTEREO_MAP: return LSTEREO;
+ case LSTEREO_LFE_MAP : return LSTEREO_LFE;
+ case L3F_MAP: return L3F;
+ case L3F_LFE_MAP: return L3F_LFE;
+ case L2F1_MAP: return L2F1;
+ case L2F1_LFE_MAP: return L2F1_LFE;
+ case L3F1_MAP: return L3F1;
+ case L3F1_LFE_MAP: return L3F1_LFE;
+ case L2F2_MAP: return L2F2;
+ case L2F2_LFE_MAP: return L2F2_LFE;
+ case LQUAD_MAP: return LQUAD;
+ case L3F2_MAP: return L3F2;
+ case L3F2_LFE_MAP: return L3F2_LFE;
+ case L3F3R_LFE_MAP: return L3F3R_LFE;
+ case L3F4_LFE_MAP: return L3F4_LFE;
+ default:
+ break;
+ }
+ AutoTArray<Channel, MAX_AUDIO_CHANNELS> layout;
+ uint32_t channels = 0;
+
+ uint32_t i = 0;
+ while (aMap) {
+ if (aMap & 1) {
+ layout.AppendElement(static_cast<Channel>(i));
+ channels++;
+ if (channels > MAX_AUDIO_CHANNELS) {
+ return ChannelLayout();
+ }
+ }
+ aMap >>= 1;
+ i++;
+ }
+ return ChannelLayout(channels, layout.Elements());
+}
+
+
bool
AudioConfig::ChannelLayout::MappingTable(const ChannelLayout& aOther,
uint8_t* aMap) const
diff --git a/dom/media/MediaInfo.h b/dom/media/MediaInfo.h
index d54cf99b2..29dcf2cc9 100644
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -33,9 +33,6 @@ public:
nsCString mValue;
};
- // Maximum channel number we can currently handle (7.1)
-#define MAX_AUDIO_CHANNELS 8
-
class TrackInfo {
public:
enum TrackType {
@@ -317,6 +314,7 @@ public:
EmptyString(), EmptyString(), true, 1)
, mRate(0)
, mChannels(0)
+ , mChannelMap(AudioConfig::ChannelLayout::UNKNOWN_MAP)
, mBitDepth(0)
, mProfile(0)
, mExtendedProfile(0)
@@ -329,6 +327,7 @@ public:
: TrackInfo(aOther)
, mRate(aOther.mRate)
, mChannels(aOther.mChannels)
+ , mChannelMap(aOther.mChannelMap)
, mBitDepth(aOther.mBitDepth)
, mProfile(aOther.mProfile)
, mExtendedProfile(aOther.mExtendedProfile)
@@ -365,6 +364,11 @@ public:
// Number of audio channels.
uint32_t mChannels;
+ // The AudioConfig::ChannelLayout map. Channels are ordered as per SMPTE
+ // definition. A value of UNKNOWN_MAP indicates unknown layout.
+ // ChannelMap is an unsigned bitmap compatible with Windows' WAVE and FFmpeg
+ // channel map.
+ uint32_t mChannelMap;
// Bits per sample.
uint32_t mBitDepth;
@@ -555,170 +559,6 @@ public:
const nsCString& mMimeType;
};
-class AudioConfig {
-public:
- enum Channel {
- CHANNEL_INVALID = -1,
- CHANNEL_MONO = 0,
- CHANNEL_LEFT,
- CHANNEL_RIGHT,
- CHANNEL_CENTER,
- CHANNEL_LS,
- CHANNEL_RS,
- CHANNEL_RLS,
- CHANNEL_RCENTER,
- CHANNEL_RRS,
- CHANNEL_LFE,
- };
-
- class ChannelLayout {
- public:
- ChannelLayout()
- : mChannelMap(0)
- , mValid(false)
- {}
- explicit ChannelLayout(uint32_t aChannels)
- : ChannelLayout(aChannels, SMPTEDefault(aChannels))
- {}
- ChannelLayout(uint32_t aChannels, const Channel* aConfig)
- : ChannelLayout()
- {
- if (!aConfig) {
- mValid = false;
- return;
- }
- mChannels.AppendElements(aConfig, aChannels);
- UpdateChannelMap();
- }
- bool operator==(const ChannelLayout& aOther) const
- {
- return mChannels == aOther.mChannels;
- }
- bool operator!=(const ChannelLayout& aOther) const
- {
- return mChannels != aOther.mChannels;
- }
- const Channel& operator[](uint32_t aIndex) const
- {
- return mChannels[aIndex];
- }
- uint32_t Count() const
- {
- return mChannels.Length();
- }
- uint32_t Map() const
- {
- return mChannelMap;
- }
- // Calculate the mapping table from the current layout to aOther such that
- // one can easily go from one layout to the other by doing:
- // out[channel] = in[map[channel]].
- // Returns true if the reordering is possible or false otherwise.
- // If true, then aMap, if set, will be updated to contain the mapping table
- // allowing conversion from the current layout to aOther.
- // If aMap is nullptr, then MappingTable can be used to simply determine if
- // the current layout can be easily reordered to aOther.
- // aMap must be an array of size MAX_AUDIO_CHANNELS.
- bool MappingTable(const ChannelLayout& aOther, uint8_t* aMap = nullptr) const;
- bool IsValid() const {
- return mValid;
- }
- bool HasChannel(Channel aChannel) const
- {
- return mChannelMap & (1 << aChannel);
- }
- private:
- void UpdateChannelMap();
- const Channel* SMPTEDefault(uint32_t aChannels) const;
- AutoTArray<Channel, MAX_AUDIO_CHANNELS> mChannels;
- uint32_t mChannelMap;
- bool mValid;
- };
-
- enum SampleFormat {
- FORMAT_NONE = 0,
- FORMAT_U8,
- FORMAT_S16,
- FORMAT_S24LSB,
- FORMAT_S24,
- FORMAT_S32,
- FORMAT_FLT,
-#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
- FORMAT_DEFAULT = FORMAT_FLT
-#elif defined(MOZ_SAMPLE_TYPE_S16)
- FORMAT_DEFAULT = FORMAT_S16
-#else
-#error "Not supported audio type"
-#endif
- };
-
- AudioConfig(const ChannelLayout& aChannelLayout, uint32_t aRate,
- AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
- bool aInterleaved = true);
- // Will create a channel configuration from default SMPTE ordering.
- AudioConfig(uint32_t aChannels, uint32_t aRate,
- AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
- bool aInterleaved = true);
-
- const ChannelLayout& Layout() const
- {
- return mChannelLayout;
- }
- uint32_t Channels() const
- {
- if (!mChannelLayout.IsValid()) {
- return mChannels;
- }
- return mChannelLayout.Count();
- }
- uint32_t Rate() const
- {
- return mRate;
- }
- SampleFormat Format() const
- {
- return mFormat;
- }
- bool Interleaved() const
- {
- return mInterleaved;
- }
- bool operator==(const AudioConfig& aOther) const
- {
- return mChannelLayout == aOther.mChannelLayout &&
- mRate == aOther.mRate && mFormat == aOther.mFormat &&
- mInterleaved == aOther.mInterleaved;
- }
- bool operator!=(const AudioConfig& aOther) const
- {
- return !(*this == aOther);
- }
-
- bool IsValid() const
- {
- return mChannelLayout.IsValid() && Format() != FORMAT_NONE && Rate() > 0;
- }
-
- static const char* FormatToString(SampleFormat aFormat);
- static uint32_t SampleSize(SampleFormat aFormat);
- static uint32_t FormatToBits(SampleFormat aFormat);
-
-private:
- // Channels configuration.
- ChannelLayout mChannelLayout;
-
- // Channel count.
- uint32_t mChannels;
-
- // Sample rate.
- uint32_t mRate;
-
- // Sample format.
- SampleFormat mFormat;
-
- bool mInterleaved;
-};
-
} // namespace mozilla
#endif // MediaInfo_h
diff --git a/dom/media/MediaPrefs.h b/dom/media/MediaPrefs.h
index c1e66a3ce..84163353a 100644
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -86,8 +86,12 @@ private:
DECL_MEDIA_PREF("accessibility.monoaudio.enable", MonoAudio, bool, false);
DECL_MEDIA_PREF("media.resampling.enabled", AudioSinkResampling, bool, false);
DECL_MEDIA_PREF("media.resampling.rate", AudioSinkResampleRate, uint32_t, 48000);
+#ifdef XP_WIN
+ // Enable multichannel support on Windows.
+ DECL_MEDIA_PREF("media.forcestereo.enabled", AudioSinkForceStereo, bool, false);
+#else
DECL_MEDIA_PREF("media.forcestereo.enabled", AudioSinkForceStereo, bool, true);
-
+#endif
// VideoSink
DECL_MEDIA_PREF("media.ruin-av-sync.enabled", RuinAvSync, bool, false);
diff --git a/dom/media/mediasink/DecodedAudioDataSink.cpp b/dom/media/mediasink/DecodedAudioDataSink.cpp
index e7fcffe4f..d20eac8ac 100644
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -195,7 +195,15 @@ nsresult
DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams)
{
mAudioStream = new AudioStream(*this);
- nsresult rv = mAudioStream->Init(mOutputChannels, mOutputRate, mChannel);
+ // When AudioQueue is empty, there is no way to know the channel layout of
+ // the coming audio data, so we use the predefined channel map instead.
+ uint32_t channelMap = mConverter
+ ? mConverter->OutputConfig().Layout().Map()
+ : AudioConfig::ChannelLayout(mOutputChannels).Map();
+ // This layout map is already processed by mConverter with mOutputChannels
+ // into SMPTE format, so there is no need to worry whether
+ // MediaPrefs::MonoAudio() or MediaPrefs::AudioSinkForceStereo() is applied.
+ nsresult rv = mAudioStream->Init(mOutputChannels, channelMap, mOutputRate, mChannel);
if (NS_FAILED(rv)) {
mAudioStream->Shutdown();
mAudioStream = nullptr;
@@ -410,10 +418,18 @@ DecodedAudioDataSink::NotifyAudioNeeded()
mFramesParsed = result.value();
}
+ const AudioConfig::ChannelLayout inputLayout =
+ data->mChannelMap
+ ? AudioConfig::ChannelLayout::SMPTEDefault(data->mChannelMap)
+ : AudioConfig::ChannelLayout(data->mChannels);
+ const AudioConfig::ChannelLayout outputLayout =
+ mOutputChannels == data->mChannels
+ ? inputLayout
+ : AudioConfig::ChannelLayout(mOutputChannels);
mConverter =
MakeUnique<AudioConverter>(
- AudioConfig(data->mChannels, data->mRate),
- AudioConfig(mOutputChannels, mOutputRate));
+ AudioConfig(inputLayout, data->mRate),
+ AudioConfig(outputLayout, mOutputRate));
}
// See if there's a gap in the audio. If there is, push silence into the
diff --git a/dom/media/moz.build b/dom/media/moz.build
index 40d90b599..4c22fdb64 100644
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -65,6 +65,7 @@ if CONFIG['MOZ_WEBRTC']:
WEBRTC_SIGNALLING_TEST_MANIFESTS += ['tests/mochitest/steeplechase_long/steeplechase_long.ini']
XPIDL_SOURCES += [
+ 'nsIAudioDeviceInfo.idl',
'nsIDOMNavigatorUserMedia.idl',
'nsIMediaManager.idl',
]
@@ -163,6 +164,7 @@ IPDL_SOURCES += [
]
EXPORTS.mozilla.dom += [
+ 'AudioDeviceInfo.h',
'AudioStreamTrack.h',
'AudioTrack.h',
'AudioTrackList.h',
@@ -191,6 +193,7 @@ SOURCES += [
'AudioChannelFormat.cpp',
'AudioCompactor.cpp',
'AudioConverter.cpp',
+ 'AudioDeviceInfo.cpp',
'AudioSegment.cpp',
'AudioStream.cpp',
'AudioStreamTrack.cpp',
diff --git a/dom/media/nsIAudioDeviceInfo.idl b/dom/media/nsIAudioDeviceInfo.idl
new file mode 100644
index 000000000..507c4d39c
--- /dev/null
+++ b/dom/media/nsIAudioDeviceInfo.idl
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(feb979a8-f8cc-4522-9dff-6c055ca50762)]
+interface nsIAudioDeviceInfo : nsISupports
+{
+ readonly attribute DOMString name;
+
+ readonly attribute DOMString groupId;
+
+ readonly attribute DOMString vendor;
+
+ // type: Unknown/Input/Output
+ const unsigned short TYPE_UNKNOWN = 0;
+ const unsigned short TYPE_INPUT = 1;
+ const unsigned short TYPE_OUTPUT = 2;
+ readonly attribute unsigned short type;
+
+ // state: Disabled/Unplugged/Enabled
+ const unsigned short STATE_DISABLED = 0;
+ const unsigned short STATE_UNPLUGGED = 1;
+ const unsigned short STATE_ENABLED = 2;
+ readonly attribute unsigned short state;
+
+ // preferred: None/Multimedia/Voice/Notification/All
+ const unsigned short PREF_NONE = 0x00;
+ const unsigned short PREF_MULTIMEDIA = 0x01;
+ const unsigned short PREF_VOICE = 0x02;
+ const unsigned short PREF_NOTIFICATION = 0x04;
+ const unsigned short PREF_ALL = 0x0F;
+ readonly attribute unsigned short preferred;
+
+ // supported format, default format: S16LE/S16BE/F32LE/F32BE
+ const unsigned short FMT_S16LE = 0x0010;
+ const unsigned short FMT_S16BE = 0x0020;
+ const unsigned short FMT_F32LE = 0x1000;
+ const unsigned short FMT_F32BE = 0x2000;
+ readonly attribute unsigned short supportedFormat;
+ readonly attribute unsigned short defaultFormat;
+
+ // Max number of channels: [1, 255]
+ readonly attribute unsigned long maxChannels;
+
+ readonly attribute unsigned long defaultRate;
+ readonly attribute unsigned long maxRate;
+ readonly attribute unsigned long minRate;
+
+ readonly attribute unsigned long maxLatency;
+ readonly attribute unsigned long minLatency;
+};
diff --git a/dom/media/platforms/agnostic/VorbisDecoder.cpp b/dom/media/platforms/agnostic/VorbisDecoder.cpp
index ed8b90dbd..f842e6490 100644
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -302,47 +302,78 @@ VorbisDataDecoder::VorbisLayout(uint32_t aChannels)
switch (aChannels) {
case 1: // the stream is monophonic
{
- static const Channel config[] = { AudioConfig::CHANNEL_MONO };
+ static const Channel config[] = { AudioConfig::CHANNEL_FRONT_CENTER };
return config;
}
case 2: // the stream is stereo. channel order: left, right
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_RIGHT };
+ static const Channel config[] = { AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT };
return config;
}
- case 3: // the stream is a 1d-surround encoding. channel order: left, center, right
+ case 3: // the stream is a 1d-surround encoding. channel order: left,
+ // center, right
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT };
+ static const Channel config[] = { AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_FRONT_RIGHT };
return config;
}
- case 4: // the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
+ case 4: // the stream is quadraphonic surround. channel order: front left,
+ // front right, rear left, rear right
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS };
+ static const Channel config[] = { AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT };
return config;
}
- case 5: // the stream is five-channel surround. channel order: front left, center, front right, rear left, rear right
+ case 5: // the stream is five-channel surround. channel order: front left,
+ // center, front right, rear left, rear right
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS };
+ static const Channel config[] = { AudioConfig::CHANNEL_FRONT_LEFT,
+ AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_FRONT_RIGHT,
+ AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT };
return config;
}
- case 6: // the stream is 5.1 surround. channel order: front left, center, front right, rear left, rear right, LFE
+ case 6: // the stream is 5.1 surround. channel order: front left, center,
+ // front right, rear left, rear right, LFE
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_LFE };
+ static const Channel config[] = {
+ AudioConfig::CHANNEL_FRONT_LEFT, AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_FRONT_RIGHT, AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT, AudioConfig::CHANNEL_LFE
+ };
return config;
}
- case 7: // surround. channel order: front left, center, front right, side left, side right, rear center, LFE
+ case 7: // surround. channel order: front left, center, front right, side
+ // left, side right, rear center, LFE
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_RCENTER, AudioConfig::CHANNEL_LFE };
+ static const Channel config[] = {
+ AudioConfig::CHANNEL_FRONT_LEFT, AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_FRONT_RIGHT, AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT, AudioConfig::CHANNEL_BACK_CENTER,
+ AudioConfig::CHANNEL_LFE
+ };
return config;
}
- case 8: // the stream is 7.1 surround. channel order: front left, center, front right, side left, side right, rear left, rear right, LFE
+ case 8: // the stream is 7.1 surround. channel order: front left, center,
+ // front right, side left, side right, rear left, rear right, LFE
{
- static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_RLS, AudioConfig::CHANNEL_RRS, AudioConfig::CHANNEL_LFE };
+ static const Channel config[] = {
+ AudioConfig::CHANNEL_FRONT_LEFT, AudioConfig::CHANNEL_FRONT_CENTER,
+ AudioConfig::CHANNEL_FRONT_RIGHT, AudioConfig::CHANNEL_SIDE_LEFT,
+ AudioConfig::CHANNEL_SIDE_RIGHT, AudioConfig::CHANNEL_BACK_LEFT,
+ AudioConfig::CHANNEL_BACK_RIGHT, AudioConfig::CHANNEL_LFE
+ };
return config;
}
default:
return nullptr;
}
+
}
} // namespace mozilla
diff --git a/dom/media/platforms/apple/AppleATDecoder.cpp b/dom/media/platforms/apple/AppleATDecoder.cpp
index 65794d82c..9466a4e8b 100644
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -323,8 +323,9 @@ AppleATDecoder::DecodeSample(MediaRawData* aSample)
return NS_ERROR_OUT_OF_MEMORY;
}
if (mChannelLayout && !mAudioConverter) {
- AudioConfig in(*mChannelLayout.get(), rate);
- AudioConfig out(channels, rate);
+ AudioConfig in(*mChannelLayout, rate);
+ AudioConfig out(AudioConfig::ChannelLayout::SMPTEDefault(*mChannelLayout),
+ rate);
if (!in.IsValid() || !out.IsValid()) {
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
RESULT_DETAIL("Invalid audio config"));
@@ -342,7 +343,9 @@ AppleATDecoder::DecodeSample(MediaRawData* aSample)
numFrames,
data.Forget(),
channels,
- rate);
+ rate,
+ mChannelLayout ? mChannelLayout->Map()
+ : AudioConfig::ChannelLayout::UNKNOWN_MAP);
mCallback->Output(audio);
return NS_OK;
}
@@ -428,26 +431,25 @@ AudioConfig::Channel
ConvertChannelLabel(AudioChannelLabel id)
{
switch (id) {
- case kAudioChannelLabel_Mono:
- return AudioConfig::CHANNEL_MONO;
case kAudioChannelLabel_Left:
- return AudioConfig::CHANNEL_LEFT;
+ return AudioConfig::CHANNEL_FRONT_LEFT;
case kAudioChannelLabel_Right:
- return AudioConfig::CHANNEL_RIGHT;
+ return AudioConfig::CHANNEL_FRONT_RIGHT;
+ case kAudioChannelLabel_Mono:
case kAudioChannelLabel_Center:
- return AudioConfig::CHANNEL_CENTER;
+ return AudioConfig::CHANNEL_FRONT_CENTER;
case kAudioChannelLabel_LFEScreen:
return AudioConfig::CHANNEL_LFE;
case kAudioChannelLabel_LeftSurround:
- return AudioConfig::CHANNEL_LS;
+ return AudioConfig::CHANNEL_SIDE_LEFT;
case kAudioChannelLabel_RightSurround:
- return AudioConfig::CHANNEL_RS;
+ return AudioConfig::CHANNEL_SIDE_RIGHT;
case kAudioChannelLabel_CenterSurround:
- return AudioConfig::CHANNEL_RCENTER;
+ return AudioConfig::CHANNEL_BACK_CENTER;
case kAudioChannelLabel_RearSurroundLeft:
- return AudioConfig::CHANNEL_RLS;
+ return AudioConfig::CHANNEL_BACK_LEFT;
case kAudioChannelLabel_RearSurroundRight:
- return AudioConfig::CHANNEL_RRS;
+ return AudioConfig::CHANNEL_BACK_RIGHT;
default:
return AudioConfig::CHANNEL_INVALID;
}
diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
index f867ec494..7e9198725 100644
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -117,6 +117,8 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
return audio;
}
+typedef AudioConfig::ChannelLayout ChannelLayout;
+
MediaResult
FFmpegAudioDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
{
@@ -188,7 +190,8 @@ FFmpegAudioDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample)
mFrame->nb_samples,
Move(audio),
numChannels,
- samplingRate);
+ samplingRate,
+ mCodecContext->channel_layout);
mCallback->Output(data);
pts += duration;
if (!pts.IsValid()) {
diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
index 3dacdf0aa..cf1ebb1cf 100644
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -84,6 +84,7 @@ AACAudioSpecificConfigToUserData(uint8_t aAACProfileLevelIndication,
WMFAudioMFTManager::WMFAudioMFTManager(
const AudioInfo& aConfig)
: mAudioChannels(aConfig.mChannels)
+ , mChannelsMap(AudioConfig::ChannelLayout::UNKNOWN_MAP)
, mAudioRate(aConfig.mRate)
, mAudioFrameSum(0)
, mMustRecaptureAudioPosition(true)
@@ -212,6 +213,15 @@ WMFAudioMFTManager::UpdateOutputType()
hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mAudioChannels);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+ uint32_t channelsMap;
+ hr = type->GetUINT32(MF_MT_AUDIO_CHANNEL_MASK, &channelsMap);
+ if (SUCCEEDED(hr)) {
+ mChannelsMap = channelsMap;
+ } else {
+ LOG("Unable to retrieve channel layout. Ignoring");
+ mChannelsMap = AudioConfig::ChannelLayout::UNKNOWN_MAP;
+ }
+
AudioConfig::ChannelLayout layout(mAudioChannels);
if (!layout.IsValid()) {
return E_FAIL;
@@ -338,7 +348,8 @@ WMFAudioMFTManager::Output(int64_t aStreamOffset,
numFrames,
Move(audioData),
mAudioChannels,
- mAudioRate);
+ mAudioRate,
+ mChannelsMap);
#ifdef LOG_SAMPLE_DECODE
LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.h b/dom/media/platforms/wmf/WMFAudioMFTManager.h
index 5bbbc6108..1f803a0e9 100644
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.h
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.h
@@ -47,6 +47,7 @@ private:
HRESULT UpdateOutputType();
uint32_t mAudioChannels;
+ uint32_t mChannelsMap;
uint32_t mAudioRate;
nsTArray<BYTE> mUserData;
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index e29f4cb56..01309308b 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -353,6 +353,9 @@ pref("media.dormant-on-pause-timeout-ms", 5000);
pref("media.dormant-on-pause-timeout-ms", -1);
#endif
+// Audio backend override
+pref("media.cubeb.backend", "");
+
// Media cache size in kilobytes
pref("media.cache_size", 512000);
// When a network connection is suspended, don't resume it until the
diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js
index f9a0abcb0..86f1fa35b 100644
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -310,7 +310,6 @@ var snapshotFormatters = {
addRowFromKey("features", "webgl2DriverExtensions");
addRowFromKey("features", "webgl2Extensions");
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
- addRowFromKey("features", "currentAudioBackend", "audioBackend");
addRowFromKey("features", "direct2DEnabled", "#Direct2D");
if ("directWriteEnabled" in data) {
@@ -462,6 +461,111 @@ var snapshotFormatters = {
}
},
+ media: function media(data) {
+ let strings = stringBundle();
+
+ function insertBasicInfo(key, value) {
+ function createRow(key, value) {
+ let th = $.new("th", strings.GetStringFromName(key), "column");
+ let td = $.new("td", value);
+ td.style["white-space"] = "pre-wrap";
+ return $.new("tr", [th, td]);
+ }
+ $.append($("media-info-tbody"), [createRow(key, value)]);
+ }
+
+ function createDeviceInfoRow(device) {
+ let deviceInfo = Ci.nsIAudioDeviceInfo;
+
+ let states = {};
+ states[deviceInfo.STATE_DISABLED] = "Disabled";
+ states[deviceInfo.STATE_UNPLUGGED] = "Unplugged";
+ states[deviceInfo.STATE_ENABLED] = "Enabled";
+
+ let preferreds = {};
+ preferreds[deviceInfo.PREF_NONE] = "None";
+ preferreds[deviceInfo.PREF_MULTIMEDIA] = "Multimedia";
+ preferreds[deviceInfo.PREF_VOICE] = "Voice";
+ preferreds[deviceInfo.PREF_NOTIFICATION] = "Notification";
+ preferreds[deviceInfo.PREF_ALL] = "All";
+
+ let formats = {};
+ formats[deviceInfo.FMT_S16LE] = "S16LE";
+ formats[deviceInfo.FMT_S16BE] = "S16BE";
+ formats[deviceInfo.FMT_F32LE] = "F32LE";
+ formats[deviceInfo.FMT_F32BE] = "F32BE";
+
+ function toPreferredString(preferred) {
+ if (preferred == deviceInfo.PREF_NONE) {
+ return preferreds[deviceInfo.PREF_NONE];
+ } else if (preferred & deviceInfo.PREF_ALL) {
+ return preferreds[deviceInfo.PREF_ALL];
+ }
+ let str = "";
+ for (let pref of [deviceInfo.PREF_MULTIMEDIA,
+ deviceInfo.PREF_VOICE,
+ deviceInfo.PREF_NOTIFICATION]) {
+ if (preferred & pref) {
+ str += " " + preferreds[pref];
+ }
+ }
+ return str;
+ }
+
+ function toFromatString(dev) {
+ let str = "default: " + formats[dev.defaultFormat] + ", support:";
+ for (let fmt of [deviceInfo.FMT_S16LE,
+ deviceInfo.FMT_S16BE,
+ deviceInfo.FMT_F32LE,
+ deviceInfo.FMT_F32BE]) {
+ if (dev.supportedFormat & fmt) {
+ str += " " + formats[fmt];
+ }
+ }
+ return str;
+ }
+
+ function toRateString(dev) {
+ return "default: " + dev.defaultRate +
+ ", support: " + dev.minRate + " - " + dev.maxRate;
+ }
+
+ function toLatencyString(dev) {
+ return dev.minLatency + " - " + dev.maxLatency;
+ }
+
+ return $.new("tr", [$.new("td", device.name),
+ $.new("td", device.groupId),
+ $.new("td", device.vendor),
+ $.new("td", states[device.state]),
+ $.new("td", toPreferredString(device.preferred)),
+ $.new("td", toFromatString(device)),
+ $.new("td", device.maxChannels),
+ $.new("td", toRateString(device)),
+ $.new("td", toLatencyString(device))]);
+ }
+
+ function insertDeviceInfo(side, devices) {
+ let rows = [];
+ for (let dev of devices) {
+ rows.push(createDeviceInfoRow(dev));
+ }
+ $.append($("media-" + side + "-devices-tbody"), rows);
+ }
+
+ // Basic information
+ insertBasicInfo("audioBackend", data.currentAudioBackend);
+ insertBasicInfo("maxAudioChannels", data.currentMaxAudioChannels);
+ insertBasicInfo("sampleRate", data.currentPreferredSampleRate);
+
+ // Output devices information
+ insertDeviceInfo("output", data.audioOutputDevices);
+
+ // Input devices information
+ insertDeviceInfo("input", data.audioInputDevices);
+ },
+
+
javaScript: function javaScript(data) {
$("javascript-incremental-gc").textContent = data.incrementalGCEnabled;
},
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index 4ae992739..7772f6497 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -346,6 +346,91 @@
<!-- - - - - - - - - - - - - - - - - - - - - -->
<h2 class="major-section">
+ &aboutSupport.mediaTitle;
+ </h2>
+ <table>
+ <tbody id="media-info-tbody">
+ </tbody>
+
+ <tbody id="media-output-devices-tbody">
+ <tr>
+ <th colspan="10" class="title-column">
+ &aboutSupport.mediaOutputDevicesTitle;
+ </th>
+ </tr>
+ <tr>
+ <th>
+ &aboutSupport.mediaDeviceName;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceGroup;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceVendor;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceState;
+ </th>
+ <th>
+ &aboutSupport.mediaDevicePreferred;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceFormat;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceChannels;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceRate;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceLatency;
+ </th>
+ </tr>
+ </tbody>
+
+ <tbody id="media-input-devices-tbody">
+ <tr>
+ <th colspan="10" class="title-column">
+ &aboutSupport.mediaInputDevicesTitle;
+ </th>
+ </tr>
+ <tr>
+ <th>
+ &aboutSupport.mediaDeviceName;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceGroup;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceVendor;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceState;
+ </th>
+ <th>
+ &aboutSupport.mediaDevicePreferred;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceFormat;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceChannels;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceRate;
+ </th>
+ <th>
+ &aboutSupport.mediaDeviceLatency;
+ </th>
+ </tr>
+ </tbody>
+
+ </table>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - -->
+
+ <h2 class="major-section">
&aboutSupport.modifiedKeyPrefsTitle;
</h2>
diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
index 02eb34664..b4da6bc77 100644
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd
@@ -121,3 +121,16 @@ variant of aboutSupport.showDir.label. -->
<!ENTITY aboutSupport.graphicsDecisionLogTitle "Decision Log">
<!ENTITY aboutSupport.graphicsCrashGuardsTitle "Crash Guard Disabled Features">
<!ENTITY aboutSupport.graphicsWorkaroundsTitle "Workarounds">
+
+<!ENTITY aboutSupport.mediaTitle "Media">
+<!ENTITY aboutSupport.mediaOutputDevicesTitle "Output Devices">
+<!ENTITY aboutSupport.mediaInputDevicesTitle "Input Devices">
+<!ENTITY aboutSupport.mediaDeviceName "Name">
+<!ENTITY aboutSupport.mediaDeviceGroup "Group">
+<!ENTITY aboutSupport.mediaDeviceVendor "Vendor">
+<!ENTITY aboutSupport.mediaDeviceState "State">
+<!ENTITY aboutSupport.mediaDevicePreferred "Preferred">
+<!ENTITY aboutSupport.mediaDeviceFormat "Format">
+<!ENTITY aboutSupport.mediaDeviceChannels "Channels">
+<!ENTITY aboutSupport.mediaDeviceRate "Rate">
+<!ENTITY aboutSupport.mediaDeviceLatency "Latency">
diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.properties b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
index 46e445f66..751a3f096 100644
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties
@@ -59,7 +59,6 @@ clearTypeParameters = ClearType Parameters
compositing = Compositing
hardwareH264 = Hardware H264 Decoding
-audioBackend = Audio Backend
mainThreadNoOMTC = main thread, no OMTC
yes = Yes
no = No
@@ -98,6 +97,11 @@ glcontextCrashGuard = OpenGL
resetOnNextRestart = Reset on Next Restart
gpuProcessKillButton = Terminate GPU Process
+audioBackend = Audio Backend
+maxAudioChannels = Max Channels
+channelLayout = Preferred Channel Layout
+sampleRate = Preferred Sample Rate
+
minLibVersions = Expected minimum version
loadedLibVersions = Version in use
diff --git a/toolkit/modules/Troubleshoot.jsm b/toolkit/modules/Troubleshoot.jsm
index 8ff6be30b..d8f485d2c 100644
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -332,9 +332,6 @@ var dataProviders = {
data.numAcceleratedWindows++;
}
- let winUtils = Services.wm.getMostRecentWindow("").
- QueryInterface(Ci.nsIInterfaceRequestor).
- getInterface(Ci.nsIDOMWindowUtils)
data.supportsHardwareH264 = "Unknown";
try {
// After restart - data may not be available
@@ -345,7 +342,6 @@ var dataProviders = {
promises.push(promise);
} catch (e) {}
- data.currentAudioBackend = winUtils.currentAudioBackend;
if (!data.numAcceleratedWindows && gfxInfo) {
#ifdef XP_WIN
@@ -492,6 +488,48 @@ var dataProviders = {
completed();
},
+ media: function media(done) {
+ function convertDevices(devices) {
+ if (!devices) {
+ return undefined;
+ }
+ let infos = [];
+ for (let i = 0; i < devices.length; ++i) {
+ let device = devices.queryElementAt(i, Ci.nsIAudioDeviceInfo);
+ infos.push({
+ name: device.name,
+ groupId: device.groupId,
+ vendor: device.vendor,
+ type: device.type,
+ state: device.state,
+ preferred: device.preferred,
+ supportedFormat: device.supportedFormat,
+ defaultFormat: device.defaultFormat,
+ maxChannels: device.maxChannels,
+ defaultRate: device.defaultRate,
+ maxRate: device.maxRate,
+ minRate: device.minRate,
+ maxLatency: device.maxLatency,
+ minLatency: device.minLatency
+ });
+ }
+ return infos;
+ }
+
+ let data = {};
+ let winUtils = Services.wm.getMostRecentWindow("").
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils);
+ data.currentAudioBackend = winUtils.currentAudioBackend;
+ data.currentMaxAudioChannels = winUtils.currentMaxAudioChannels;
+ data.currentPreferredSampleRate = winUtils.currentPreferredSampleRate;
+ data.audioOutputDevices = convertDevices(winUtils.audioDevices(Ci.nsIDOMWindowUtils.AUDIO_OUTPUT).
+ QueryInterface(Ci.nsIArray));
+ data.audioInputDevices = convertDevices(winUtils.audioDevices(Ci.nsIDOMWindowUtils.AUDIO_INPUT).
+ QueryInterface(Ci.nsIArray));
+ done(data);
+ },
+
javaScript: function javaScript(done) {
let data = {};
let winEnumer = Services.ww.getWindowEnumerator();