Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
qdarwinmediadevices.mm
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qmediadevices.h"
6#include "private/qaudiodevice_p.h"
10
11#include <qloggingcategory.h>
12
13#include <qdebug.h>
14
15#if defined(Q_OS_IOS)
17#import <AVFoundation/AVFoundation.h>
18#else
20#endif
21
22#if defined(Q_OS_MACOS)
23static Q_LOGGING_CATEGORY(qLcDarwinMediaDevices, "qt.multimedia.darwin.mediaDevices")
24#endif
25
27
28template<typename... Args>
29QAudioDevice createAudioDevice(bool isDefault, Args &&...args)
30{
31 auto *dev = new QCoreAudioDeviceInfo(std::forward<Args>(args)...);
32 dev->isDefault = isDefault;
33 return dev->create();
34}
35
36#if defined(Q_OS_MACOS)
37
38static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
39{
40 const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output) ? kAudioHardwarePropertyDefaultOutputDevice
41 : kAudioHardwarePropertyDefaultInputDevice;
42 const AudioObjectPropertyAddress propertyAddress = { selector, kAudioObjectPropertyScopeGlobal,
43 kAudioObjectPropertyElementMaster };
44
45 if (auto audioDevice = getAudioObject<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress,
46 "Default Device")) {
47 return *audioDevice;
48 }
49
50 return 0;
51}
52
53static QByteArray uniqueId(AudioDeviceID device, QAudioDevice::Mode mode)
54{
55 const AudioObjectPropertyAddress propertyAddress =
56 makePropertyAddress(kAudioDevicePropertyDeviceUID, mode);
57
58 if (auto name = getAudioObject<CFStringRef>(device, propertyAddress, "Device UID")) {
59 QString s = QString::fromCFString(*name);
60 CFRelease(*name);
61 return s.toUtf8();
62 }
63
64 return QByteArray();
65}
66
67static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
68{
70
71 AudioDeviceID defaultDevice = defaultAudioDevice(mode);
72 if (defaultDevice != 0)
73 devices << createAudioDevice(true, defaultDevice, uniqueId(defaultDevice, mode), mode);
74
75 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
76 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
77 kAudioObjectPropertyElementMaster
78 };
79
80 if (auto audioDevices = getAudioData<AudioDeviceID>(
81 kAudioObjectSystemObject, audioDevicesPropertyAddress, "Audio Devices")) {
82 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
83 makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
84
85 for (const auto &device : *audioDevices) {
86 if (device == defaultDevice)
87 continue;
88
89 if (getAudioObject<AudioStreamBasicDescription>(device,
90 audioDeviceStreamFormatPropertyAddress,
91 nullptr /*don't print logs*/)) {
92 devices << createAudioDevice(false, device, uniqueId(device, mode), mode);
93 }
94 }
95 }
96
97 return devices;
98}
99
100static OSStatus audioDeviceChangeListener(AudioObjectID id, UInt32,
101 const AudioObjectPropertyAddress *address, void *ptr)
102{
104 Q_ASSERT(ptr);
105
106 QDarwinMediaDevices *instance = static_cast<QDarwinMediaDevices *>(ptr);
107
108 qCDebug(qLcDarwinMediaDevices)
109 << "audioDeviceChangeListener: id:" << id << "address: " << address->mSelector
110 << address->mScope << address->mElement;
111
112 switch (address->mSelector) {
113 case kAudioHardwarePropertyDefaultInputDevice:
114 instance->onInputsUpdated();
115 break;
116 case kAudioHardwarePropertyDefaultOutputDevice:
117 instance->onOutputsUpdated();
118 break;
119 default:
120 instance->onInputsUpdated();
121 instance->onOutputsUpdated();
122 break;
123 }
124
125 return 0;
126}
127
128static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
129 { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
130 kAudioObjectPropertyElementMaster },
131 { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
132 kAudioObjectPropertyElementMaster },
133 { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
134 kAudioObjectPropertyElementMaster }
135};
136
137static void setAudioListeners(QDarwinMediaDevices &instance)
138{
139 for (const auto &address : listenerAddresses) {
140 const auto err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &address,
141 audioDeviceChangeListener, &instance);
142
143 if (err)
144 qWarning() << "Fail to add listener. mSelector:" << address.mSelector
145 << "mScope:" << address.mScope << "mElement:" << address.mElement
146 << "err:" << err;
147 }
148}
149
150static void removeAudioListeners(QDarwinMediaDevices &instance)
151{
152 for (const auto &address : listenerAddresses) {
153 const auto err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &address,
154 audioDeviceChangeListener, &instance);
155
156 if (err)
157 qWarning() << "Fail to remove listener. mSelector:" << address.mSelector
158 << "mScope:" << address.mScope << "mElement:" << address.mElement
159 << "err:" << err;
160 }
161}
162
163#elif defined(Q_OS_IOS)
164
165static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
166{
168
169 if (mode == QAudioDevice::Output) {
170 devices.append(createAudioDevice(true, "default", QAudioDevice::Output));
171 } else {
172 AVCaptureDevice *defaultDevice =
173 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
174
175 // TODO: Support Bluetooth and USB devices
176 AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
177 [AVCaptureDeviceDiscoverySession
178 discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInMicrophone ]
179 mediaType:AVMediaTypeAudio
180 position:AVCaptureDevicePositionUnspecified];
181
182 NSArray *captureDevices = [captureDeviceDiscoverySession devices];
183 for (AVCaptureDevice *device in captureDevices) {
184 const bool isDefault =
185 defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
186 devices.append(createAudioDevice(isDefault,
187 QString::fromNSString(device.uniqueID).toUtf8(),
189 }
190 }
191
192 return devices;
193}
194
195static void setAudioListeners(QDarwinMediaDevices &)
196{
197 // ### This should use the audio session manager
198}
199
200static void removeAudioListeners(QDarwinMediaDevices &)
201{
202 // ### This should use the audio session manager
203}
204
205#endif
206
207
210{
211#ifdef Q_OS_MACOS // TODO: implement setAudioListeners, removeAudioListeners for Q_OS_IOS, after
212 // that - remove or modify the define
213 m_cachedAudioInputs = availableAudioDevices(QAudioDevice::Input);
214 m_cachedAudioOutputs = availableAudioDevices(QAudioDevice::Output);
215#endif
216
217 setAudioListeners(*this);
218}
219
220
222{
223 removeAudioListeners(*this);
224}
225
227{
228 return availableAudioDevices(QAudioDevice::Input);
229}
230
232{
233 return availableAudioDevices(QAudioDevice::Output);
234}
235
237{
238 auto inputs = availableAudioDevices(QAudioDevice::Input);
239 if (m_cachedAudioInputs != inputs) {
240 m_cachedAudioInputs = inputs;
242 }
243}
244
246{
247 auto outputs = availableAudioDevices(QAudioDevice::Output);
248 if (m_cachedAudioOutputs != outputs) {
249 m_cachedAudioOutputs = outputs;
251 }
252}
253
256{
257 return new QDarwinAudioSource(info, parent);
258}
259
262{
263 return new QDarwinAudioSink(info, parent);
264}
265
IOBluetoothDevice * device
The QAudioDevice class provides an information about audio devices and their functionality.
Mode
Describes the mode of this device.
\inmodule QtCore
Definition qbytearray.h:57
QPlatformAudioSource * createAudioSource(const QAudioDevice &info, QObject *parent) override
QList< QAudioDevice > audioInputs() const override
QPlatformAudioSink * createAudioSink(const QAudioDevice &info, QObject *parent) override
QList< QAudioDevice > audioOutputs() const override
Definition qlist.h:74
\inmodule QtCore
Definition qobject.h:90
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE QAudioDevice createAudioDevice(bool isDefault, Args &&...args)
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
static AudioObjectPropertyAddress makePropertyAddress(AudioObjectPropertySelector selector, QAudioDevice::Mode mode, AudioObjectPropertyElement element=kAudioObjectPropertyElementMaster)
static ControlElement< T > * ptr(QWidget *widget)
GLenum mode
GLuint name
GLuint in
GLuint GLuint64EXT address
GLdouble s
[6]
Definition qopenglext.h:235
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QFileInfo info(fileName)
[8]
QFileSelector selector
[1]
QJSValueList args
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent