6#include <QtMultimedia/qmediadevices.h>
7#include <QtMultimedia/private/qaudiodevice_p.h>
8#include <QtMultimedia/private/qcoreaudiodevice_p.h>
9#include <QtMultimedia/private/qcoreaaudiosink_p.h>
10#include <QtMultimedia/private/qcoreaaudiosource_p.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qdebug.h>
16#if defined(QT_PLATFORM_UIKIT)
17# import <AVFoundation/AVFoundation.h>
19# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
26#if defined(Q_OS_MACOS)
32template <
typename... Args>
33static QAudioDevice createAudioDevice(
bool isDefault, Args &&...args)
35 auto dev = std::make_unique<QCoreAudioDeviceInfo>(std::forward<Args>(args)...);
36 dev->isDefault = isDefault;
37 return QAudioDevicePrivate::createQAudioDevice(std::move(dev));
40#if defined(Q_OS_MACOS)
42static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
44 const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output)
45 ? kAudioHardwarePropertyDefaultOutputDevice
46 : kAudioHardwarePropertyDefaultInputDevice;
47 const AudioObjectPropertyAddress propertyAddress = {
49 kAudioObjectPropertyScopeGlobal,
50 kAudioObjectPropertyElementMain,
53 if (
auto audioDevice = QCoreAudioUtils::getAudioProperty<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress)) {
60static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
62 using namespace QCoreAudioUtils;
64 QList<QAudioDevice> devices;
66 AudioDeviceID defaultDevice = defaultAudioDevice(mode);
67 if (defaultDevice != 0)
68 devices << createAudioDevice(
71 QCoreAudioUtils::readPersistentDeviceId(defaultDevice, mode),
74 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
75 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
76 kAudioObjectPropertyElementMain
79 if (
auto audioDevices = getAudioPropertyList<AudioDeviceID>(
80 kAudioObjectSystemObject, audioDevicesPropertyAddress)) {
81 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
82 makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
84 for (
const auto &device : *audioDevices) {
85 if (device == defaultDevice)
88 if (getAudioProperty<AudioStreamBasicDescription>(device,
89 audioDeviceStreamFormatPropertyAddress,
91 devices << createAudioDevice(
false,
93 QCoreAudioUtils::readPersistentDeviceId(device, mode),
102#elif defined(QT_PLATFORM_UIKIT)
104static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
106 QList<QAudioDevice> devices;
108 if (mode == QAudioDevice::Output) {
109 devices.append(createAudioDevice(
true,
"default", QAudioDevice::Output));
111#if !defined(Q_OS_VISIONOS)
112 AVCaptureDevice *defaultDevice =
113 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
116 AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
117 [AVCaptureDeviceDiscoverySession
118 discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeMicrophone ]
119 mediaType:AVMediaTypeAudio
120 position:AVCaptureDevicePositionUnspecified];
122 NSArray *captureDevices = [captureDeviceDiscoverySession devices];
123 for (AVCaptureDevice *device in captureDevices) {
124 const bool isDefault =
125 defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
126 devices.append(createAudioDevice(isDefault,
127 QString::fromNSString(device.uniqueID).toUtf8(),
128 QAudioDevice::Input));
140static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
141 { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
142 kAudioObjectPropertyElementMain },
143 { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
144 kAudioObjectPropertyElementMain },
145 { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
146 kAudioObjectPropertyElementMain }
153template <
typename Block>
154auto makeOwningBlock(Block block)
157 return std::make_shared<Block>(block);
161 void operator()(Block *b)
const noexcept
170 return std::shared_ptr<Block>(
new Block(Block_copy(block)), BlockDeleter{});
176QCoreAudioDevices::QCoreAudioDevices()
178 if (!QThread::isMainThread())
179 moveToThread(qApp->thread());
182 m_listenerQueue.reset(
183 dispatch_queue_create(
"QtAudioDevicesListener",
nullptr));
184 dispatch_set_target_queue(m_listenerQueue.get(), dispatch_get_main_queue());
186 std::shared_ptr destroyedFlag = m_destroyed;
187 m_deviceListenerBlock = makeOwningBlock<AudioObjectPropertyListenerBlock>(
188 [
this, destroyedFlag](UInt32 numberOfProps,
const AudioObjectPropertyAddress *props) {
195 Q_ASSERT(QThread::isMainThread());
197 auto properties = QSpan{ props, numberOfProps };
199 bool updateInputs =
false;
200 bool updateOutputs =
false;
202 for (
const AudioObjectPropertyAddress &address : properties) {
203 qCDebug(qLcDarwinMediaDevices)
204 <<
"audioDeviceChangeListenerBlock: address: " << address.mSelector
205 << address.mScope << address.mElement;
207 switch (address.mSelector) {
208 case kAudioHardwarePropertyDefaultInputDevice:
211 case kAudioHardwarePropertyDefaultOutputDevice:
212 updateOutputs =
true;
216 updateOutputs =
true;
222 updateAudioInputsCache();
224 updateAudioOutputsCache();
227 for (
const auto &address : listenerAddresses) {
228 const auto err = AudioObjectAddPropertyListenerBlock(
229 kAudioObjectSystemObject, &address, m_listenerQueue.get(), *m_deviceListenerBlock);
231 qWarning() <<
"Fail to add listener. mSelector:" << address.mSelector
232 <<
"mScope:" << address.mScope <<
"mElement:" << address.mElement
236 updateAudioInputsCache();
237 updateAudioOutputsCache();
241QCoreAudioDevices::~QCoreAudioDevices()
245 for (
const auto &address : listenerAddresses) {
246 Q_ASSERT(m_deviceListenerBlock);
247 AudioObjectRemovePropertyListenerBlock(kAudioObjectSystemObject, &address,
248 m_listenerQueue.get(), *m_deviceListenerBlock);
250 m_listenerQueue = {};
254QList<QAudioDevice> QCoreAudioDevices::findAudioInputs()
const
256 return availableAudioDevices(QAudioDevice::Input);
259QList<QAudioDevice> QCoreAudioDevices::findAudioOutputs()
const
261 return availableAudioDevices(QAudioDevice::Output);
264QPlatformAudioSource *QCoreAudioDevices::createAudioSource(
const QAudioDevice &info,
265 const QAudioFormat &fmt,
268 return new QCoreAudioSource(info, fmt, parent);
271QPlatformAudioSink *QCoreAudioDevices::createAudioSink(
const QAudioDevice &info,
272 const QAudioFormat &fmt, QObject *parent)
274 return new QCoreAudioSink(info, fmt, parent);
337 qWarning() <<
"QAudioOutput: Failed to add property listener";
Combined button and popup list for selecting options.