6#include <QtCore/qloggingcategory.h>
7#include <QtCore/qdebug.h>
8#include <QtMultimedia/qmediadevices.h>
9#include <QtMultimedia/private/qaudio_qiodevice_support_p.h>
10#include <QtMultimedia/private/qaudiohelpers_p.h>
11#include <QtMultimedia/private/qaudioringbuffer_p.h>
12#include <QtMultimedia/private/qaudiosystem_platform_stream_support_p.h>
13#include <QtMultimedia/private/qautoresetevent_p.h>
14#include <QtMultimedia/private/qcoreaudioutils_p.h>
15#include <QtMultimedia/private/qapple_utils_p.h>
16#include <QtMultimedia/private/qcoreaudiodevice_p.h>
17#include <QtMultimedia/private/qcoreaudiodevices_p.h>
19#include <AudioUnit/AudioUnit.h>
21# include <AudioUnit/AudioComponent.h>
22# include <QtMultimedia/private/qmacosaudiodatautils_p.h>
31QCoreAudioSinkStream::QCoreAudioSinkStream(QAudioDevice audioDevice,
const QAudioFormat& format,
32 std::optional<qsizetype> ringbufferSize,
33 QCoreAudioSink *parent,
float volume,
34 std::optional<QtMultimediaPrivate::NativePeriodFrames> nativePeriodFrames,
36 : QPlatformAudioSinkStream {
37 std::move(audioDevice), format, ringbufferSize, nativePeriodFrames, volume,
43QCoreAudioSinkStream::~QCoreAudioSinkStream()
46 m_stopOnDisconnected.cancelChain();
50bool QCoreAudioSinkStream::
open()
52 using namespace QCoreAudioUtils;
57 std::optional<AudioDeviceID> audioDeviceId = findAudioDeviceId(m_audioDevice);
59 qWarning() <<
"QAudioSource: Unable to use find most recent CoreAudio AudioDeviceID for "
60 "given device-id. The device might not be connected.";
63 const AudioDeviceID nativeDeviceId = audioDeviceId.value();
66 if (
auto audioUnit = makeAudioUnitForIO())
67 m_audioUnit = std::move(*audioUnit);
73 if (!setDisconnectListener(*audioDeviceId))
77 audioUnitSetCurrentDevice(m_audioUnit, nativeDeviceId);
79 if (m_nativePeriodFrames)
80 audioObjectSetFramesPerBuffer(*audioDeviceId,
81 qToUnderlying(*m_nativePeriodFrames));
85 const AudioStreamBasicDescription streamFormat = toAudioStreamBasicDescription(m_format);
86 if (!audioUnitSetInputStreamFormat(m_audioUnit, 0, streamFormat))
89 return m_audioUnit.initialize();
92bool QCoreAudioSinkStream::
start(QIODevice *device)
94 AURenderCallbackStruct callback{
95 .inputProc = [](
void *self, [[maybe_unused]] AudioUnitRenderActionFlags *ioActionFlags,
96 [[maybe_unused]]
const AudioTimeStamp *inTimeStamp,
97 [[maybe_unused]] UInt32 inBusNumber, UInt32 inNumberFrames,
98 AudioBufferList *ioData)
noexcept Q_DECL_NONBLOCKING_FUNCTION -> OSStatus {
99 return reinterpret_cast<QCoreAudioSinkStream *>(self)->processRingbuffer(inNumberFrames,
102 .inputProcRefCon =
this,
104 if (!audioUnitSetRenderCallback(m_audioUnit, callback))
107 setQIODevice(device);
111 m_sessionActivation = QtMultimediaPrivate::QAVAudioSessionManager::instance()->activate(
115 if (!startAudioUnit())
118 createQIODeviceConnections(device);
125 QIODevice *reader = createRingbufferWriterDevice();
127 bool success = start(reader);
134bool QCoreAudioSinkStream::
start(AudioCallback cb)
136 m_audioCallback = std::move(cb);
138 AURenderCallbackStruct callback{
139 .inputProc = [](
void *self, [[maybe_unused]] AudioUnitRenderActionFlags *ioActionFlags,
140 [[maybe_unused]]
const AudioTimeStamp *inTimeStamp,
141 [[maybe_unused]] UInt32 inBusNumber, UInt32 inNumberFrames,
142 AudioBufferList *ioData)
noexcept Q_DECL_NONBLOCKING_FUNCTION -> OSStatus {
143 return reinterpret_cast<QCoreAudioSinkStream *>(self)->processAudioCallback(inNumberFrames,
146 .inputProcRefCon =
this,
148 if (!audioUnitSetRenderCallback(m_audioUnit, callback))
152 m_sessionActivation = QtMultimediaPrivate::QAVAudioSessionManager::instance()->activate(
156 return startAudioUnit();
159void QCoreAudioSinkStream::
stop(ShutdownPolicy policy)
168 if (m_audioCallback) {
173 disconnectQIODeviceConnections();
177 case ShutdownPolicy::DrainRingbuffer:
180 case ShutdownPolicy::DiscardRingbuffer:
184 Q_UNREACHABLE_RETURN();
196 connectIdleHandler([
this, ownedSelf = shared_from_this()]()
mutable {
210 m_stopOnDisconnected.cancelChain();
216 m_sessionActivation.reset();
222 const auto status = AudioOutputUnitStop(m_audioUnit.get());
226 qDebug() <<
"AudioOutputUnitStop failed:" << QtMultimediaPrivate::QOSStatus(status);
231 return startAudioUnit();
236 if (QCoreAudioUtils::audioUnitIsRunning(m_audioUnit))
245 handleIOError(m_parent);
248OSStatus QCoreAudioSinkStream::processRingbuffer(uint32_t numberOfFrames,
249 AudioBufferList *ioData)
noexcept Q_DECL_NONBLOCKING_FUNCTION
251 Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames));
253 QSpan audioBufferSpan{
254 reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData),
255 ioData->mBuffers[0].mDataByteSize,
258 QPlatformAudioSinkStream::process(audioBufferSpan, numberOfFrames);
263OSStatus QCoreAudioSinkStream::processAudioCallback(uint32_t numberOfFrames,
264 AudioBufferList *ioData)
noexcept Q_DECL_NONBLOCKING_FUNCTION
266 using namespace QtMultimediaPrivate;
268 Q_ASSERT(int64_t(ioData->mBuffers[0].mDataByteSize) == m_format.bytesForFrames(numberOfFrames));
270 QSpan<std::byte> inputSpan{
271 reinterpret_cast<std::byte *>(ioData->mBuffers[0].mData),
272 ioData->mBuffers[0].mDataByteSize,
275 runAudioCallback(*m_audioCallback, inputSpan, m_format, volume());
281void QCoreAudioSinkStream::updateStreamIdle(
bool arg)
284 m_parent->updateStreamIdle(arg);
287bool QCoreAudioSinkStream::startAudioUnit()
294 const OSStatus status = AudioOutputUnitStart(m_audioUnit.get());
295 if (status != noErr) {
296 qDebug() <<
"AudioOutputUnitStart failed:" << QtMultimediaPrivate::QOSStatus(status);
303void QCoreAudioSinkStream::stopAudioUnit()
305 invalidateAudioUnit();
308 m_stopOnDisconnected.cancelChain();
312void QCoreAudioSinkStream::invalidateAudioUnit()
317 const auto status = AudioOutputUnitStop(m_audioUnit.get());
319 qDebug() <<
"AudioOutputUnitStop failed:" << QtMultimediaPrivate::QOSStatus(status);
325bool QCoreAudioSinkStream::setDisconnectListener(AudioObjectID id)
327 m_stopOnDisconnected.cancelChain();
329 auto disconnectionFuture = m_disconnectMonitor.setDisconnectListener(id);
330 if (!disconnectionFuture)
333 m_stopOnDisconnected = disconnectionFuture->then(m_parent, [
this] {
349QCoreAudioSink::QCoreAudioSink(QAudioDevice device,
const QAudioFormat &format, QObject *parent)
350 : BaseClass(std::move(device), format, parent)
354QCoreAudioSink::~QCoreAudioSink()
361 if (!m_stream || state() == QAudio::SuspendedState)
363 if (!m_stream->resumeIfNecessary())
364 qWarning() <<
"QCoreAudioSink: failed to resume audio unit";
371 m_recovery.userResumeRequested();
void stop(ShutdownPolicy policy)
bool start(AudioCallback cb)
bool start(QIODevice *device)
void stopStreamWhenBufferDrained()
void resumeStreamIfNecessary()
void notifyStreamStopped()