diff options
author | Artem Dyomin <artem.dyomin@qt.io> | 2022-12-28 17:16:56 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-01-02 21:16:02 +0000 |
commit | 30ff855504a4cec725512a53c51894d723d0be27 (patch) | |
tree | 5cb79a0caf88bc734f84a4033d5342ebec632e60 | |
parent | c51430d1fbd553790de94891168ec0e00552f44e (diff) | |
download | qtmultimedia-30ff855504a4cec725512a53c51894d723d0be27.tar.gz |
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.txt | 5 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinaudiodevice.mm | 93 | ||||
-rw-r--r-- | src/multimedia/darwin/qdarwinmediadevices.mm | 82 | ||||
-rw-r--r-- | src/multimedia/darwin/qmacosaudiodatautils_p.h | 112 |
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 |