summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Dyomin <artem.dyomin@qt.io>2022-12-28 17:16:56 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-01-02 21:16:02 +0000
commit30ff855504a4cec725512a53c51894d723d0be27 (patch)
tree5cb79a0caf88bc734f84a4033d5342ebec632e60
parentc51430d1fbd553790de94891168ec0e00552f44e (diff)
Improve macos audio data warnings
Functions AudioObjectGetPropertyDataSize and AudioObjectGetPropertyData have been wrapped into util functions that can print warnings with info that might help us. Task-number: QTBUG-108176 Change-Id: I787d5a93ad395d2156df6b542c290949c61daaa3 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Doris Verria <doris.verria@qt.io> (cherry picked from commit 23e4f614b3ab22300b2ce58c50b1e9a6d4ab5011) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/multimedia/CMakeLists.txt5
-rw-r--r--src/multimedia/darwin/qdarwinaudiodevice.mm93
-rw-r--r--src/multimedia/darwin/qdarwinmediadevices.mm82
-rw-r--r--src/multimedia/darwin/qmacosaudiodatautils_p.h112
4 files changed, 174 insertions, 118 deletions
diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt
index 33fc6d868..14e6f69d4 100644
--- a/src/multimedia/CMakeLists.txt
+++ b/src/multimedia/CMakeLists.txt
@@ -170,6 +170,11 @@ qt_internal_extend_target(Multimedia CONDITION APPLE
${FWCoreFoundation}
)
+qt_internal_extend_target(Multimedia CONDITION MACOS
+ SOURCES
+ darwin/qmacosaudiodatautils_p.h
+)
+
qt_internal_extend_target(Multimedia CONDITION IOS OR TVOS
SOURCES
darwin/qcoreaudiosessionmanager.mm darwin/qcoreaudiosessionmanager_p.h
diff --git a/src/multimedia/darwin/qdarwinaudiodevice.mm b/src/multimedia/darwin/qdarwinaudiodevice.mm
index 5b0ca11f4..8374f4cea 100644
--- a/src/multimedia/darwin/qdarwinaudiodevice.mm
+++ b/src/multimedia/darwin/qdarwinaudiodevice.mm
@@ -5,8 +5,10 @@
#include "qcoreaudioutils_p.h"
#include <private/qcore_mac_p.h>
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# include "qcoreaudiosessionmanager_p.h"
+#if defined(Q_OS_IOS)
+#include "qcoreaudiosessionmanager_p.h"
+#else
+#include "qmacosaudiodatautils_p.h"
#endif
#include <QtCore/QDataStream>
@@ -36,50 +38,29 @@ QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevic
}
-
QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const
{
QAudioFormat format;
#if defined(Q_OS_MACOS)
- UInt32 propSize = 0;
- AudioObjectPropertyScope audioDevicePropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
- AudioObjectPropertyAddress audioDevicePropertyStreamsAddress = { kAudioDevicePropertyStreams,
- audioDevicePropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize) == noErr) {
-
- const int sc = propSize / sizeof(AudioStreamID);
-
- if (sc > 0) {
- AudioStreamID* streams = new AudioStreamID[sc];
-
- if (AudioObjectGetPropertyData(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize, streams) == noErr) {
-
- AudioObjectPropertyAddress audioDevicePhysicalFormatPropertyAddress = { kAudioStreamPropertyPhysicalFormat,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- for (int i = 0; i < sc; ++i) {
- if (AudioObjectGetPropertyDataSize(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize) == noErr) {
- AudioStreamBasicDescription sf;
-
- if (AudioObjectGetPropertyData(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize, &sf) == noErr) {
- format = CoreAudioUtils::toQAudioFormat(sf);
- break;
- } else {
- qWarning() << "QAudioDevice: Unable to find perferedFormat for stream";
- }
- } else {
- qWarning() << "QAudioDevice: Unable to find size of perferedFormat for stream";
- }
- }
+ const auto audioDevicePropertyStreamsAddress =
+ makePropertyAddress(kAudioDevicePropertyStreams, mode);
+
+ if (auto streamIDs = getAudioData<AudioStreamID>(m_deviceId, audioDevicePropertyStreamsAddress,
+ "propertyStreams")) {
+ const auto audioDevicePhysicalFormatPropertyAddress =
+ makePropertyAddress(kAudioStreamPropertyPhysicalFormat, mode);
+
+ for (auto streamID : *streamIDs) {
+ if (auto streamDescription = getAudioObject<AudioStreamBasicDescription>(
+ streamID, audioDevicePhysicalFormatPropertyAddress,
+ "prefferedPhysicalFormat")) {
+ format = CoreAudioUtils::toQAudioFormat(*streamDescription);
+ break;
}
-
- delete[] streams;
}
}
+
if (!format.isValid())
#endif
{
@@ -96,22 +77,14 @@ QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const
QString QCoreAudioDeviceInfo::getDescription() const
{
#ifdef Q_OS_MACOS
- CFStringRef name;
- UInt32 size = sizeof(CFStringRef);
- AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
-
- AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioObjectPropertyName,
- audioPropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
- qWarning() << "QAudioDevice: Unable to find device description";
- return QString();
+ const auto propertyAddress = makePropertyAddress(kAudioObjectPropertyName, mode);
+ if (auto name =
+ getAudioObject<CFStringRef>(m_deviceId, propertyAddress, "Device Description")) {
+ auto deleter = qScopeGuard([&name]() { CFRelease(*name); });
+ return QString::fromCFString(*name);
}
- QString s = QString::fromCFString(name);
- CFRelease(name);
- return s;
+ return {};
#else
return QString::fromUtf8(id);
#endif
@@ -120,16 +93,12 @@ QString QCoreAudioDeviceInfo::getDescription() const
void QCoreAudioDeviceInfo::getChannelLayout()
{
#ifdef Q_OS_MACOS
- AudioObjectPropertyAddress audioDeviceChannelLayoutPropertyAddress = { kAudioDevicePropertyPreferredChannelLayout,
- (mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
- kAudioObjectPropertyElementMaster };
- UInt32 propSize;
- if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize) == noErr) {
- AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propSize));
- if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize, layout) == noErr) {
- channelConfiguration = CoreAudioUtils::fromAudioChannelLayout(layout);
- }
- free(layout);
+ const auto propertyAddress =
+ makePropertyAddress(kAudioDevicePropertyPreferredChannelLayout, mode);
+ if (auto data = getAudioData<char>(m_deviceId, propertyAddress, "prefferedChannelLayout",
+ sizeof(AudioChannelLayout))) {
+ const auto *layout = reinterpret_cast<const AudioChannelLayout *>(data->data());
+ channelConfiguration = CoreAudioUtils::fromAudioChannelLayout(layout);
}
#else
channelConfiguration = (mode == QAudioDevice::Input) ? QAudioFormat::ChannelConfigMono : QAudioFormat::ChannelConfigStereo;
diff --git a/src/multimedia/darwin/qdarwinmediadevices.mm b/src/multimedia/darwin/qdarwinmediadevices.mm
index 46a91a850..9f4a23dc6 100644
--- a/src/multimedia/darwin/qdarwinmediadevices.mm
+++ b/src/multimedia/darwin/qdarwinmediadevices.mm
@@ -15,6 +15,8 @@
#if defined(Q_OS_IOS)
#include "qcoreaudiosessionmanager_p.h"
#import <AVFoundation/AVFoundation.h>
+#else
+#include "qmacosaudiodatautils_p.h"
#endif
Q_LOGGING_CATEGORY(qLcDarwinMediaDevices, "qt.multimedia.darwin.mediaDevices")
@@ -33,45 +35,31 @@ QAudioDevice createAudioDevice(bool isDefault, Args &&...args)
static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
{
- AudioDeviceID audioDevice;
- UInt32 size = sizeof(audioDevice);
const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output) ? kAudioHardwarePropertyDefaultOutputDevice
: kAudioHardwarePropertyDefaultInputDevice;
- const AudioObjectPropertyAddress defaultDevicePropertyAddress = {
- selector, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster
- };
+ const AudioObjectPropertyAddress propertyAddress = { selector, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &defaultDevicePropertyAddress,
- 0, NULL, &size, &audioDevice) != noErr) {
- qWarning("QAudioDevice: Unable to find default %s device", (mode == QAudioDevice::Output) ? "output" : "input");
- return 0;
+ if (auto audioDevice = getAudioObject<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress,
+ "Default Device")) {
+ return *audioDevice;
}
- return audioDevice;
+ return 0;
}
static QByteArray uniqueId(AudioDeviceID device, QAudioDevice::Mode mode)
{
- CFStringRef name;
- UInt32 size = sizeof(CFStringRef);
-
- const AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input
- ? kAudioDevicePropertyScopeInput
- : kAudioDevicePropertyScopeOutput;
-
- const AudioObjectPropertyAddress audioDeviceNamePropertyAddress = {
- kAudioDevicePropertyDeviceUID, audioPropertyScope, kAudioObjectPropertyElementMaster
- };
+ const AudioObjectPropertyAddress propertyAddress =
+ makePropertyAddress(kAudioDevicePropertyDeviceUID, mode);
- if (AudioObjectGetPropertyData(device, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
- qWarning() << "QAudioDevice: Unable to get device UID";
- return QByteArray();
+ if (auto name = getAudioObject<CFStringRef>(device, propertyAddress, "Device UID")) {
+ QString s = QString::fromCFString(*name);
+ CFRelease(*name);
+ return s.toUtf8();
}
- QString s = QString::fromCFString(name);
- CFRelease(name);
- return s.toUtf8();
+ return QByteArray();
}
static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
@@ -82,42 +70,24 @@ static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
if (defaultDevice != 0)
devices << createAudioDevice(true, defaultDevice, uniqueId(defaultDevice, mode), mode);
- UInt32 propSize = 0;
const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
- if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0,
- nullptr, &propSize)
- == noErr) {
-
- std::vector<AudioDeviceID> audioDevices(propSize / sizeof(AudioDeviceID));
-
- if (!audioDevices.empty()
- && AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0,
- nullptr, &propSize, audioDevices.data())
- == noErr) {
-
- const auto propertyScope = mode == QAudioDevice::Input
- ? kAudioDevicePropertyScopeInput
- : kAudioDevicePropertyScopeOutput;
-
- for (const auto &device : audioDevices) {
- if (device == defaultDevice)
- continue;
+ if (auto audioDevices = getAudioData<AudioDeviceID>(
+ kAudioObjectSystemObject, audioDevicesPropertyAddress, "Audio Devices")) {
+ const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
+ makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
- AudioStreamBasicDescription sf = {};
- UInt32 size = sizeof(AudioStreamBasicDescription);
- const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = {
- kAudioDevicePropertyStreamFormat, propertyScope,
- kAudioObjectPropertyElementMaster
- };
+ for (const auto &device : *audioDevices) {
+ if (device == defaultDevice)
+ continue;
- if (AudioObjectGetPropertyData(device, &audioDeviceStreamFormatPropertyAddress, 0,
- nullptr, &size, &sf)
- == noErr)
- devices << createAudioDevice(false, device, uniqueId(device, mode), mode);
+ if (getAudioObject<AudioStreamBasicDescription>(device,
+ audioDeviceStreamFormatPropertyAddress,
+ nullptr /*don't print logs*/)) {
+ devices << createAudioDevice(false, device, uniqueId(device, mode), mode);
}
}
}
diff --git a/src/multimedia/darwin/qmacosaudiodatautils_p.h b/src/multimedia/darwin/qmacosaudiodatautils_p.h
new file mode 100644
index 000000000..5cd6fced2
--- /dev/null
+++ b/src/multimedia/darwin/qmacosaudiodatautils_p.h
@@ -0,0 +1,112 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMACOSAUDIODATAUTILS_P_H
+#define QMACOSAUDIODATAUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <CoreAudio/AudioHardware.h>
+
+#include "qaudiodevice.h"
+#include "qdebug.h"
+
+#include <optional>
+#include <vector>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+template<typename... Args>
+void printUnableToReadWarning(const char *logName, AudioObjectID objectID, const AudioObjectPropertyAddress &address, Args &&...args)
+{
+ if (!logName)
+ return;
+
+ char scope[5] = {0};
+ memcpy(&scope, &address.mScope, 4);
+ std::reverse(scope, scope + 4);
+
+ auto warn = qWarning();
+ warn << "Unable to read property" << logName << "for object" << objectID << ", scope" << scope << ";";
+ (warn << ... << args);
+ warn << "\n If the warning is unexpected use test_audio_config to get comprehensive audio info and report a bug";
+}
+
+inline static AudioObjectPropertyAddress
+makePropertyAddress(AudioObjectPropertySelector selector, QAudioDevice::Mode mode,
+ AudioObjectPropertyElement element = kAudioObjectPropertyElementMaster)
+{
+ return { selector,
+ mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput
+ : kAudioDevicePropertyScopeOutput,
+ element };
+}
+
+inline static bool getAudioData(AudioObjectID objectID, const AudioObjectPropertyAddress &address,
+ void *dst, UInt32 dstSize, const char *logName)
+{
+ UInt32 readBytes = dstSize;
+ const auto res = AudioObjectGetPropertyData(objectID, &address, 0, nullptr, &readBytes, dst);
+
+ if (res != noErr)
+ printUnableToReadWarning(logName, objectID, address, "Err:", res);
+ else if (readBytes != dstSize)
+ printUnableToReadWarning(logName, objectID, address, "Data size", readBytes, "VS", dstSize,
+ "expected");
+ else
+ return true;
+
+ return false;
+}
+
+template<typename T>
+std::optional<std::vector<T>> getAudioData(AudioObjectID objectID,
+ const AudioObjectPropertyAddress &address,
+ const char *logName, size_t minDataSize = 0)
+{
+ static_assert(std::is_trivial_v<T>, "A trivial type is expected");
+
+ UInt32 size = 0;
+ const auto res = AudioObjectGetPropertyDataSize(objectID, &address, 0, nullptr, &size);
+
+ if (res != noErr) {
+ printUnableToReadWarning(logName, objectID, address,
+ "AudioObjectGetPropertyDataSize failed, Err:", res);
+ } else if (size / sizeof(T) < minDataSize) {
+ printUnableToReadWarning(logName, objectID, address, "Data size is too small:", size, "VS",
+ minDataSize * sizeof(T), "bytes");
+ } else {
+ std::vector<T> data(size / sizeof(T));
+ if (getAudioData(objectID, address, data.data(), data.size() * sizeof(T), logName))
+ return { std::move(data) };
+ }
+
+ return {};
+}
+
+template<typename T>
+std::optional<T> getAudioObject(AudioObjectID objectID, const AudioObjectPropertyAddress &address,
+ const char *logName)
+{
+ static_assert(std::is_trivial_v<T>, "A trivial type is expected");
+
+ T object{};
+ if (getAudioData(objectID, address, &object, sizeof(T), logName))
+ return { object };
+
+ return {};
+}
+
+QT_END_NAMESPACE
+
+#endif // QMACOSAUDIODATAUTILS_P_H