diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/media/webrtc/CubebDeviceEnumerator.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/webrtc/CubebDeviceEnumerator.cpp')
-rw-r--r-- | dom/media/webrtc/CubebDeviceEnumerator.cpp | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/dom/media/webrtc/CubebDeviceEnumerator.cpp b/dom/media/webrtc/CubebDeviceEnumerator.cpp new file mode 100644 index 0000000000..f7f8ec46d8 --- /dev/null +++ b/dom/media/webrtc/CubebDeviceEnumerator.cpp @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CubebDeviceEnumerator.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" +#include "nsThreadUtils.h" +#ifdef XP_WIN +# include "mozilla/mscom/EnsureMTA.h" +#endif + +namespace mozilla { + +using namespace CubebUtils; + +/* static */ +static StaticRefPtr<CubebDeviceEnumerator> sInstance; +static StaticMutex sInstanceMutex; + +/* static */ +CubebDeviceEnumerator* CubebDeviceEnumerator::GetInstance() { + StaticMutexAutoLock lock(sInstanceMutex); + if (!sInstance) { + sInstance = new CubebDeviceEnumerator(); + static bool clearOnShutdownSetup = []() -> bool { + auto setClearOnShutdown = []() -> void { + ClearOnShutdown(&sInstance, ShutdownPhase::ShutdownThreads); + }; + if (NS_IsMainThread()) { + setClearOnShutdown(); + } else { + SchedulerGroup::Dispatch( + TaskCategory::Other, + NS_NewRunnableFunction("CubebDeviceEnumerator::::GetInstance()", + std::move(setClearOnShutdown))); + } + return true; + }(); + Unused << clearOnShutdownSetup; + } + return sInstance.get(); +} + +CubebDeviceEnumerator::CubebDeviceEnumerator() + : mMutex("CubebDeviceListMutex"), + mManualInputInvalidation(false), + mManualOutputInvalidation(false) { +#ifdef XP_WIN + // Ensure the MTA thread exists and gets instantiated before the + // CubebDeviceEnumerator so that this instance will always gets destructed + // before the MTA thread gets shutdown. + mozilla::mscom::EnsureMTA(); + mozilla::mscom::EnsureMTA([&]() -> void { +#endif + int rv = cubeb_register_device_collection_changed( + GetCubebContext(), CUBEB_DEVICE_TYPE_OUTPUT, + &OutputAudioDeviceListChanged_s, this); + if (rv != CUBEB_OK) { + NS_WARNING( + "Could not register the audio output" + " device collection changed callback."); + mManualOutputInvalidation = true; + } + rv = cubeb_register_device_collection_changed( + GetCubebContext(), CUBEB_DEVICE_TYPE_INPUT, + &InputAudioDeviceListChanged_s, this); + if (rv != CUBEB_OK) { + NS_WARNING( + "Could not register the audio input" + " device collection changed callback."); + mManualInputInvalidation = true; + } +#ifdef XP_WIN + }); +#endif +} + +/* static */ +void CubebDeviceEnumerator::Shutdown() { + StaticMutexAutoLock lock(sInstanceMutex); + if (sInstance) { + sInstance = nullptr; + } +} + +CubebDeviceEnumerator::~CubebDeviceEnumerator() { +#ifdef XP_WIN + mozilla::mscom::EnsureMTA([&]() -> void { +#endif + int rv = cubeb_register_device_collection_changed( + GetCubebContext(), CUBEB_DEVICE_TYPE_OUTPUT, nullptr, this); + if (rv != CUBEB_OK) { + NS_WARNING( + "Could not unregister the audio output" + " device collection changed callback."); + } + rv = cubeb_register_device_collection_changed( + GetCubebContext(), CUBEB_DEVICE_TYPE_INPUT, nullptr, this); + if (rv != CUBEB_OK) { + NS_WARNING( + "Could not unregister the audio input" + " device collection changed callback."); + } +#ifdef XP_WIN + }); +#endif +} + +void CubebDeviceEnumerator::EnumerateAudioInputDevices( + nsTArray<RefPtr<AudioDeviceInfo>>& aOutDevices) { + MutexAutoLock lock(mMutex); + aOutDevices.Clear(); + EnumerateAudioDevices(Side::INPUT); + aOutDevices.AppendElements(mInputDevices); +} + +void CubebDeviceEnumerator::EnumerateAudioOutputDevices( + nsTArray<RefPtr<AudioDeviceInfo>>& aOutDevices) { + MutexAutoLock lock(mMutex); + aOutDevices.Clear(); + EnumerateAudioDevices(Side::OUTPUT); + aOutDevices.AppendElements(mOutputDevices); +} + +#ifndef ANDROID +static 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]; +} + +static 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]; +} + +static uint16_t ConvertCubebPreferred(cubeb_device_pref aPreferred) { + if (aPreferred == CUBEB_DEVICE_PREF_NONE) { + return nsIAudioDeviceInfo::PREF_NONE; + } + 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; +} + +static 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; +} + +static void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos, + Side aSide) { + cubeb* context = GetCubebContext(); + if (context) { + cubeb_device_collection collection = {nullptr, 0}; +# ifdef XP_WIN + mozilla::mscom::EnsureMTA([&]() -> void { +# endif + 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( + device.devid, NS_ConvertUTF8toUTF16(device.friendly_name), + NS_ConvertUTF8toUTF16(device.group_id), + NS_ConvertUTF8toUTF16(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); +# ifdef XP_WIN + }); +# endif + } +} +#endif // non ANDROID + +void CubebDeviceEnumerator::EnumerateAudioDevices( + CubebDeviceEnumerator::Side aSide) { + mMutex.AssertCurrentThreadOwns(); + MOZ_ASSERT(aSide == Side::INPUT || aSide == Side::OUTPUT); + + nsTArray<RefPtr<AudioDeviceInfo>> devices; + bool manualInvalidation = true; + + if (aSide == Side::INPUT) { + devices = std::move(mInputDevices); + manualInvalidation = mManualInputInvalidation; + } else { + MOZ_ASSERT(aSide == Side::OUTPUT); + devices = std::move(mOutputDevices); + manualInvalidation = mManualOutputInvalidation; + } + + cubeb* context = GetCubebContext(); + if (!context) { + return; + } + +#ifdef ANDROID + cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN; + uint32_t channels = 0; + nsAutoString name; + if (aSide == Side::INPUT) { + type = CUBEB_DEVICE_TYPE_INPUT; + channels = 1; + name = u"Default audio input device"_ns; + } else { + MOZ_ASSERT(aSide == Side::OUTPUT); + type = CUBEB_DEVICE_TYPE_OUTPUT; + channels = 2; + name = u"Default audio output device"_ns; + } + + if (devices.IsEmpty()) { + // Bug 1473346: enumerating devices is not supported on Android in cubeb, + // simply state that there is a single sink, that it is the default, and has + // a single channel. All the other values are made up and are not to be + // used. + // Bug 1660391: we can't use fluent here yet to get localized strings, so + // those are hard-coded en_US strings for now. + RefPtr<AudioDeviceInfo> info = new AudioDeviceInfo( + nullptr, name, u""_ns, u""_ns, type, CUBEB_DEVICE_STATE_ENABLED, + CUBEB_DEVICE_PREF_ALL, CUBEB_DEVICE_FMT_ALL, CUBEB_DEVICE_FMT_S16NE, + channels, 44100, 44100, 44100, 441, 128); + devices.AppendElement(info); + } +#else + if (devices.IsEmpty() || manualInvalidation) { + devices.Clear(); + + MutexAutoUnlock unlock(mMutex); + GetDeviceCollection(devices, (aSide == Side::INPUT) ? CubebUtils::Input + : CubebUtils::Output); + } +#endif + + if (aSide == Side::INPUT) { + mInputDevices.AppendElements(devices); + } else { + mOutputDevices.AppendElements(devices); + } +} + +already_AddRefed<AudioDeviceInfo> CubebDeviceEnumerator::DeviceInfoFromID( + CubebUtils::AudioDeviceID aID) { + MutexAutoLock lock(mMutex); + + if (mInputDevices.IsEmpty() || mManualInputInvalidation) { + EnumerateAudioDevices(Side::INPUT); + } + for (RefPtr<AudioDeviceInfo>& device : mInputDevices) { + if (device->DeviceID() == aID) { + RefPtr<AudioDeviceInfo> other = device; + return other.forget(); + } + } + + if (mOutputDevices.IsEmpty() || mManualOutputInvalidation) { + EnumerateAudioDevices(Side::OUTPUT); + } + for (RefPtr<AudioDeviceInfo>& device : mOutputDevices) { + if (device->DeviceID() == aID) { + RefPtr<AudioDeviceInfo> other = device; + return other.forget(); + } + } + return nullptr; +} + +already_AddRefed<AudioDeviceInfo> CubebDeviceEnumerator::DeviceInfoFromName( + const nsString& aName) { + RefPtr<AudioDeviceInfo> other = DeviceInfoFromName(aName, Side::INPUT); + if (other) { + return other.forget(); + } + return DeviceInfoFromName(aName, Side::OUTPUT); +} + +already_AddRefed<AudioDeviceInfo> CubebDeviceEnumerator::DeviceInfoFromName( + const nsString& aName, Side aSide) { + MutexAutoLock lock(mMutex); + + nsTArray<RefPtr<AudioDeviceInfo>>& devices = + (aSide == Side::INPUT) ? mInputDevices : mOutputDevices; + bool manualInvalidation = (aSide == Side::INPUT) ? mManualInputInvalidation + : mManualOutputInvalidation; + + if (devices.IsEmpty() || manualInvalidation) { + EnumerateAudioDevices(aSide); + } + for (RefPtr<AudioDeviceInfo>& device : devices) { + if (device->Name().Equals(aName)) { + RefPtr<AudioDeviceInfo> other = device; + return other.forget(); + } + } + + return nullptr; +} + +RefPtr<AudioDeviceInfo> CubebDeviceEnumerator::DefaultDevice(Side aSide) { + MutexAutoLock lock(mMutex); + + nsTArray<RefPtr<AudioDeviceInfo>>& devices = + (aSide == Side::INPUT) ? mInputDevices : mOutputDevices; + bool manualInvalidation = (aSide == Side::INPUT) ? mManualInputInvalidation + : mManualOutputInvalidation; + + if (devices.IsEmpty() || manualInvalidation) { + EnumerateAudioDevices(aSide); + } + for (RefPtr<AudioDeviceInfo>& device : devices) { + if (device->Preferred()) { + RefPtr<AudioDeviceInfo> other = device; + return other.forget(); + } + } + + return nullptr; +} + +void CubebDeviceEnumerator::InputAudioDeviceListChanged_s(cubeb* aContext, + void* aUser) { + CubebDeviceEnumerator* self = reinterpret_cast<CubebDeviceEnumerator*>(aUser); + self->AudioDeviceListChanged(CubebDeviceEnumerator::Side::INPUT); +} + +void CubebDeviceEnumerator::OutputAudioDeviceListChanged_s(cubeb* aContext, + void* aUser) { + CubebDeviceEnumerator* self = reinterpret_cast<CubebDeviceEnumerator*>(aUser); + self->AudioDeviceListChanged(CubebDeviceEnumerator::Side::OUTPUT); +} + +void CubebDeviceEnumerator::AudioDeviceListChanged(Side aSide) { + MutexAutoLock lock(mMutex); + if (aSide == Side::INPUT) { + mInputDevices.Clear(); + mOnInputDeviceListChange.Notify(); + } else { + MOZ_ASSERT(aSide == Side::OUTPUT); + mOutputDevices.Clear(); + mOnOutputDeviceListChange.Notify(); + } +} + +} // namespace mozilla |