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>
22#if defined(Q_OS_MACOS)
28template <
typename... Args>
29static QAudioDevice createAudioDevice(
bool isDefault, Args &&...args)
31 auto dev = std::make_unique<QCoreAudioDeviceInfo>(std::forward<Args>(args)...);
32 dev->isDefault = isDefault;
33 return QAudioDevicePrivate::createQAudioDevice(std::move(dev));
36#if defined(Q_OS_MACOS)
38static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
40 const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output)
41 ? kAudioHardwarePropertyDefaultOutputDevice
42 : kAudioHardwarePropertyDefaultInputDevice;
43 const AudioObjectPropertyAddress propertyAddress = {
45 kAudioObjectPropertyScopeGlobal,
46 kAudioObjectPropertyElementMain,
49 if (
auto audioDevice = QCoreAudioUtils::getAudioProperty<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress)) {
56static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
58 using namespace QCoreAudioUtils;
60 QList<QAudioDevice> devices;
62 AudioDeviceID defaultDevice = defaultAudioDevice(mode);
63 if (defaultDevice != 0)
64 devices << createAudioDevice(
67 QCoreAudioUtils::readPersistentDeviceId(defaultDevice, mode),
70 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
71 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
72 kAudioObjectPropertyElementMain
75 if (
auto audioDevices = getAudioPropertyList<AudioDeviceID>(
76 kAudioObjectSystemObject, audioDevicesPropertyAddress)) {
77 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
78 makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
80 for (
const auto &device : *audioDevices) {
81 if (device == defaultDevice)
84 if (getAudioProperty<AudioStreamBasicDescription>(device,
85 audioDeviceStreamFormatPropertyAddress,
87 devices << createAudioDevice(
false,
89 QCoreAudioUtils::readPersistentDeviceId(device, mode),
98#elif defined(QT_PLATFORM_UIKIT)
100static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
102 QList<QAudioDevice> devices;
104 if (mode == QAudioDevice::Output) {
105 devices.append(createAudioDevice(
true,
"default", QAudioDevice::Output));
107#if !defined(Q_OS_VISIONOS)
108 AVCaptureDevice *defaultDevice =
109 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
112 AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
113 [AVCaptureDeviceDiscoverySession
114 discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeMicrophone ]
115 mediaType:AVMediaTypeAudio
116 position:AVCaptureDevicePositionUnspecified];
118 NSArray *captureDevices = [captureDeviceDiscoverySession devices];
119 for (AVCaptureDevice *device in captureDevices) {
120 const bool isDefault =
121 defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
122 devices.append(createAudioDevice(isDefault,
123 QString::fromNSString(device.uniqueID).toUtf8(),
124 QAudioDevice::Input));
136static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
137 { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
138 kAudioObjectPropertyElementMain },
139 { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
140 kAudioObjectPropertyElementMain },
141 { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
142 kAudioObjectPropertyElementMain }
147QCoreAudioDevices::QCoreAudioDevices()
149 if (!QThread::isMainThread())
150 moveToThread(qApp->thread());
153 m_listenerQueue.reset(
154 dispatch_queue_create(
"QtAudioDevicesListener",
nullptr));
155 dispatch_set_target_queue(m_listenerQueue.get(), dispatch_get_main_queue());
157 std::shared_ptr destroyedFlag = m_destroyed;
158 m_deviceListenerBlock = std::make_unique<AudioObjectPropertyListenerBlock>(
159 [
this, destroyedFlag](UInt32 numberOfProps,
const AudioObjectPropertyAddress *props) {
166 Q_ASSERT(QThread::isMainThread());
168 auto properties = QSpan{ props, numberOfProps };
170 bool updateInputs =
false;
171 bool updateOutputs =
false;
173 for (
const AudioObjectPropertyAddress &address : properties) {
174 qCDebug(qLcDarwinMediaDevices)
175 <<
"audioDeviceChangeListenerBlock: address: " << address.mSelector
176 << address.mScope << address.mElement;
178 switch (address.mSelector) {
179 case kAudioHardwarePropertyDefaultInputDevice:
182 case kAudioHardwarePropertyDefaultOutputDevice:
183 updateOutputs =
true;
187 updateOutputs =
true;
193 updateAudioInputsCache();
195 updateAudioOutputsCache();
198 for (
const auto &address : listenerAddresses) {
199 const auto err = AudioObjectAddPropertyListenerBlock(
200 kAudioObjectSystemObject, &address, m_listenerQueue.get(), *m_deviceListenerBlock);
202 qWarning() <<
"Fail to add listener. mSelector:" << address.mSelector
203 <<
"mScope:" << address.mScope <<
"mElement:" << address.mElement
207 updateAudioInputsCache();
208 updateAudioOutputsCache();
212QCoreAudioDevices::~QCoreAudioDevices()
216 for (
const auto &address : listenerAddresses) {
217 Q_ASSERT(m_deviceListenerBlock);
218 AudioObjectRemovePropertyListenerBlock(kAudioObjectSystemObject, &address,
219 m_listenerQueue.get(), *m_deviceListenerBlock);
221 m_listenerQueue = {};
225QList<QAudioDevice> QCoreAudioDevices::findAudioInputs()
const
227 return availableAudioDevices(QAudioDevice::Input);
230QList<QAudioDevice> QCoreAudioDevices::findAudioOutputs()
const
232 return availableAudioDevices(QAudioDevice::Output);
235QPlatformAudioSource *QCoreAudioDevices::createAudioSource(
const QAudioDevice &info,
236 const QAudioFormat &fmt,
239 return new QCoreAudioSource(info, fmt, parent);
242QPlatformAudioSink *QCoreAudioDevices::createAudioSink(
const QAudioDevice &info,
243 const QAudioFormat &fmt, QObject *parent)
245 return new QCoreAudioSink(info, fmt, parent);
308 qWarning() <<
"QAudioOutput: Failed to add property listener";
Combined button and popup list for selecting options.