diff options
Diffstat (limited to 'dom')
24 files changed, 1052 insertions, 242 deletions
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index d05f3b71fc..eb09a6f14f 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 70ec7e0ae6..200bd3caf6 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 30eeb59049..88ac673860 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 ab9b11543d..a77ea57f4a 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 0000000000..6f27218737 --- /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 0000000000..15930810de --- /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 77ed512eee..dc1daa2e07 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 199314d4b6..e138ab991a 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 86879198ad..a9315d8ab8 100644 --- a/dom/media/CubebUtils.cpp +++ b/dom/media/CubebUtils.cpp @@ -20,6 +20,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" @@ -48,13 +49,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"; @@ -131,9 +133,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; @@ -225,7 +239,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; @@ -256,14 +270,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() @@ -272,6 +295,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)); @@ -280,6 +305,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); @@ -289,6 +315,7 @@ void ShutdownLibrary() sCubebContext = nullptr; } sBrandName = nullptr; + sCubebBackendName = nullptr; // This will ensure we don't try to re-create a context. sCubebState = CubebState::Shutdown; } @@ -319,5 +346,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 f434923747..8449462442 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 90680d8c69..70b59c61f1 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 fb3eff7b51..5b68200eff 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 bec9c6076b..dd1d0243e0 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 568183e688..28b9e77828 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 d54cf99b2d..29dcf2cc97 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 c1e66a3cee..84163353a5 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 e7fcffe4f3..d20eac8acb 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 54920359c9..cbe91c255f 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 0000000000..507c4d39c4 --- /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 ed8b90dbd4..f842e64905 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 65794d82c8..9466a4e8be 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 f867ec4942..7e91987251 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 4e0c3a8641..ef83dbb05d 100644 --- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp @@ -82,6 +82,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) @@ -210,6 +211,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; @@ -332,7 +342,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 5bbbc6108a..1f803a0e98 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; |