6#include <QtMultimedia/qmediadevices.h>
7#include <QtMultimedia/private/qaudiodevice_p.h>
8#include <QtMultimedia/private/qdarwinaudiodevice_p.h>
9#include <QtMultimedia/private/qdarwinaudiosink_p.h>
10#include <QtMultimedia/private/qdarwinaudiosource_p.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qdebug.h>
16#if defined(QT_PLATFORM_UIKIT)
17# include <QtMultimedia/private/qcoreaudiosessionmanager_p.h>
18# import <AVFoundation/AVFoundation.h>
21# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
24#if defined(Q_OS_MACOS)
30template <
typename... Args>
31static QAudioDevice createAudioDevice(
bool isDefault, Args &&...args)
33 auto dev = std::make_unique<QCoreAudioDeviceInfo>(std::forward<Args>(args)...);
34 dev->isDefault = isDefault;
35 return QAudioDevicePrivate::createQAudioDevice(std::move(dev));
38#if defined(Q_OS_MACOS)
40static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
42 const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output)
43 ? kAudioHardwarePropertyDefaultOutputDevice
44 : kAudioHardwarePropertyDefaultInputDevice;
45 const AudioObjectPropertyAddress propertyAddress = {
47 kAudioObjectPropertyScopeGlobal,
48 kAudioObjectPropertyElementMain,
51 if (
auto audioDevice = QCoreAudioUtils::getAudioProperty<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress)) {
58static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
60 using namespace QCoreAudioUtils;
62 QList<QAudioDevice> devices;
64 AudioDeviceID defaultDevice = defaultAudioDevice(mode);
65 if (defaultDevice != 0)
66 devices << createAudioDevice(
69 QCoreAudioUtils::readPersistentDeviceId(defaultDevice, mode),
72 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
73 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
74 kAudioObjectPropertyElementMain
77 if (
auto audioDevices = getAudioPropertyList<AudioDeviceID>(
78 kAudioObjectSystemObject, audioDevicesPropertyAddress)) {
79 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
80 makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
82 for (
const auto &device : *audioDevices) {
83 if (device == defaultDevice)
86 if (getAudioProperty<AudioStreamBasicDescription>(device,
87 audioDeviceStreamFormatPropertyAddress,
89 devices << createAudioDevice(
false,
91 QCoreAudioUtils::readPersistentDeviceId(device, mode),
100#elif defined(QT_PLATFORM_UIKIT)
102static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
104 QList<QAudioDevice> devices;
106 if (mode == QAudioDevice::Output) {
107 devices.append(createAudioDevice(
true,
"default", QAudioDevice::Output));
109#if !defined(Q_OS_VISIONOS)
110 AVCaptureDevice *defaultDevice =
111 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
114 AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
115 [AVCaptureDeviceDiscoverySession
116 discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeMicrophone ]
117 mediaType:AVMediaTypeAudio
118 position:AVCaptureDevicePositionUnspecified];
120 NSArray *captureDevices = [captureDeviceDiscoverySession devices];
121 for (AVCaptureDevice *device in captureDevices) {
122 const bool isDefault =
123 defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
124 devices.append(createAudioDevice(isDefault,
125 QString::fromNSString(device.uniqueID).toUtf8(),
126 QAudioDevice::Input));
138static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
139 { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
140 kAudioObjectPropertyElementMain },
141 { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
142 kAudioObjectPropertyElementMain },
143 { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
144 kAudioObjectPropertyElementMain }
149QDarwinAudioDevices::QDarwinAudioDevices()
151 if (!QThread::isMainThread())
152 moveToThread(qApp->thread());
155 m_listenerQueue.reset(
156 dispatch_queue_create(
"QtAudioDevicesListener",
nullptr));
157 dispatch_set_target_queue(m_listenerQueue.get(), dispatch_get_main_queue());
159 std::shared_ptr destroyedFlag = m_destroyed;
160 m_deviceListenerBlock = std::make_unique<AudioObjectPropertyListenerBlock>(
161 [
this, destroyedFlag](UInt32 numberOfProps,
const AudioObjectPropertyAddress *props) {
168 Q_ASSERT(QThread::isMainThread());
170 auto properties = QSpan{ props, numberOfProps };
172 bool updateInputs =
false;
173 bool updateOutputs =
false;
175 for (
const AudioObjectPropertyAddress &address : properties) {
176 qCDebug(qLcDarwinMediaDevices)
177 <<
"audioDeviceChangeListenerBlock: address: " << address.mSelector
178 << address.mScope << address.mElement;
180 switch (address.mSelector) {
181 case kAudioHardwarePropertyDefaultInputDevice:
184 case kAudioHardwarePropertyDefaultOutputDevice:
185 updateOutputs =
true;
189 updateOutputs =
true;
195 updateAudioInputsCache();
197 updateAudioOutputsCache();
200 for (
const auto &address : listenerAddresses) {
201 const auto err = AudioObjectAddPropertyListenerBlock(
202 kAudioObjectSystemObject, &address, m_listenerQueue.get(), *m_deviceListenerBlock);
204 qWarning() <<
"Fail to add listener. mSelector:" << address.mSelector
205 <<
"mScope:" << address.mScope <<
"mElement:" << address.mElement
209 updateAudioInputsCache();
210 updateAudioOutputsCache();
214QDarwinAudioDevices::~QDarwinAudioDevices()
218 for (
const auto &address : listenerAddresses) {
219 Q_ASSERT(m_deviceListenerBlock);
220 AudioObjectRemovePropertyListenerBlock(kAudioObjectSystemObject, &address,
221 m_listenerQueue.get(), *m_deviceListenerBlock);
223 m_listenerQueue = {};
227QList<QAudioDevice> QDarwinAudioDevices::findAudioInputs()
const
229 return availableAudioDevices(QAudioDevice::Input);
232QList<QAudioDevice> QDarwinAudioDevices::findAudioOutputs()
const
234 return availableAudioDevices(QAudioDevice::Output);
237QPlatformAudioSource *QDarwinAudioDevices::createAudioSource(
const QAudioDevice &info,
238 const QAudioFormat &fmt,
241 return new QDarwinAudioSource(info, fmt, parent);
244QPlatformAudioSink *QDarwinAudioDevices::createAudioSink(
const QAudioDevice &info,
245 const QAudioFormat &fmt, QObject *parent)
247 return new QDarwinAudioSink(info, fmt, parent);
292 qWarning() <<
"QAudioOutput: Failed to add property listener";
Combined button and popup list for selecting options.