Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
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 = {
44 kAudioObjectPropertyScopeGlobal,
45 kAudioObjectPropertyElementMain,
46 };
47
48 if (auto audioDevice = getAudioObject<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress,
49 "Default Device")) {
50 return *audioDevice;
51 }
52
53 return 0;
54}
55
56static QByteArray uniqueId(AudioDeviceID device, QAudioDevice::Mode mode)
57{
58 const AudioObjectPropertyAddress propertyAddress =
59 makePropertyAddress(kAudioDevicePropertyDeviceUID, mode);
60
61 if (auto name = getAudioObject<CFStringRef>(device, propertyAddress, "Device UID")) {
62 QString s = QString::fromCFString(*name);
63 CFRelease(*name);
64 return s.toUtf8();
65 }
66
67 return QByteArray();
68}
69
70static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
71{
72 QList<QAudioDevice> devices;
73
74 AudioDeviceID defaultDevice = defaultAudioDevice(mode);
75 if (defaultDevice != 0)
76 devices << createAudioDevice(true, defaultDevice, uniqueId(defaultDevice, mode), mode);
77
78 const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
79 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
80 kAudioObjectPropertyElementMain
81 };
82
83 if (auto audioDevices = getAudioData<AudioDeviceID>(
84 kAudioObjectSystemObject, audioDevicesPropertyAddress, "Audio Devices")) {
85 const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
86 makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
87
88 for (const auto &device : *audioDevices) {
89 if (device == defaultDevice)
90 continue;
91
92 if (getAudioObject<AudioStreamBasicDescription>(device,
93 audioDeviceStreamFormatPropertyAddress,
94 nullptr /*don't print logs*/)) {
95 devices << createAudioDevice(false, device, uniqueId(device, mode), mode);
96 }
97 }
98 }
99
100 return devices;
101}
102
103static OSStatus audioDeviceChangeListener(AudioObjectID id, UInt32,
104 const AudioObjectPropertyAddress *address, void *ptr)
105{
107 Q_ASSERT(ptr);
108
109 QDarwinMediaDevices *instance = static_cast<QDarwinMediaDevices *>(ptr);
110
111 qCDebug(qLcDarwinMediaDevices)
112 << "audioDeviceChangeListener: id:" << id << "address: " << address->mSelector
113 << address->mScope << address->mElement;
114
115 switch (address->mSelector) {
116 case kAudioHardwarePropertyDefaultInputDevice:
117 instance->onInputsUpdated();
118 break;
119 case kAudioHardwarePropertyDefaultOutputDevice:
120 instance->onOutputsUpdated();
121 break;
122 default:
123 instance->onInputsUpdated();
124 instance->onOutputsUpdated();
125 break;
126 }
127
128 return 0;
129}
130
131static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
132 { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
133 kAudioObjectPropertyElementMain },
134 { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
135 kAudioObjectPropertyElementMain },
136 { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
137 kAudioObjectPropertyElementMain }
138};
139
140static void setAudioListeners(QDarwinMediaDevices &instance)
141{
142 for (const auto &address : listenerAddresses) {
143 const auto err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &address,
144 audioDeviceChangeListener, &instance);
145
146 if (err)
147 qWarning() << "Fail to add listener. mSelector:" << address.mSelector
148 << "mScope:" << address.mScope << "mElement:" << address.mElement
149 << "err:" << err;
150 }
151}
152
153static void removeAudioListeners(QDarwinMediaDevices &instance)
154{
155 for (const auto &address : listenerAddresses) {
156 const auto err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &address,
157 audioDeviceChangeListener, &instance);
158
159 if (err)
160 qWarning() << "Fail to remove listener. mSelector:" << address.mSelector
161 << "mScope:" << address.mScope << "mElement:" << address.mElement
162 << "err:" << err;
163 }
164}
165
166#elif defined(Q_OS_IOS)
167
168static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
169{
170 QList<QAudioDevice> devices;
171
172 if (mode == QAudioDevice::Output) {
173 devices.append(createAudioDevice(true, "default", QAudioDevice::Output));
174 } else {
175 AVCaptureDevice *defaultDevice =
176 [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
177
178 // TODO: Support Bluetooth and USB devices
179 AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
180 [AVCaptureDeviceDiscoverySession
181 discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInMicrophone ]
182 mediaType:AVMediaTypeAudio
183 position:AVCaptureDevicePositionUnspecified];
184
185 NSArray *captureDevices = [captureDeviceDiscoverySession devices];
186 for (AVCaptureDevice *device in captureDevices) {
187 const bool isDefault =
188 defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
189 devices.append(createAudioDevice(isDefault,
190 QString::fromNSString(device.uniqueID).toUtf8(),
192 }
193 }
194
195 return devices;
196}
197
198static void setAudioListeners(QDarwinMediaDevices &)
199{
200 // ### This should use the audio session manager
201}
202
203static void removeAudioListeners(QDarwinMediaDevices &)
204{
205 // ### This should use the audio session manager
206}
207
208#endif
209
210
213{
214#ifdef Q_OS_MACOS // TODO: implement setAudioListeners, removeAudioListeners for Q_OS_IOS, after
215 // that - remove or modify the define
216 m_cachedAudioInputs = availableAudioDevices(QAudioDevice::Input);
217 m_cachedAudioOutputs = availableAudioDevices(QAudioDevice::Output);
218#endif
219
220 setAudioListeners(*this);
221}
222
223
225{
226 removeAudioListeners(*this);
227}
228
229QList<QAudioDevice> QDarwinMediaDevices::audioInputs() const
230{
231 return availableAudioDevices(QAudioDevice::Input);
232}
233
234QList<QAudioDevice> QDarwinMediaDevices::audioOutputs() const
235{
236 return availableAudioDevices(QAudioDevice::Output);
237}
238
240{
241 auto inputs = availableAudioDevices(QAudioDevice::Input);
242 if (m_cachedAudioInputs != inputs) {
243 m_cachedAudioInputs = inputs;
245 }
246}
247
249{
250 auto outputs = availableAudioDevices(QAudioDevice::Output);
251 if (m_cachedAudioOutputs != outputs) {
252 m_cachedAudioOutputs = outputs;
254 }
255}
256
262
268
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
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE QAudioDevice createAudioDevice(bool isDefault, Args &&...args)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
static AudioObjectPropertyAddress makePropertyAddress(AudioObjectPropertySelector selector, QAudioDevice::Mode mode, AudioObjectPropertyElement element=kAudioObjectPropertyElementMain)
static ControlElement< T > * ptr(QWidget *widget)
GLenum mode
GLuint name
GLdouble s
[6]
Definition qopenglext.h:235
GLuint in
GLuint GLuint64EXT address
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QFileSelector selector
[1]
QHostInfo info
[0]
QJSValueList args