Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidaudiosink.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
7
8#include <QtMultimedia/private/qaudiohelpers_p.h>
9
11
12namespace QtAAudio {
13
14Q_STATIC_LOGGING_CATEGORY(qLcAndroidAudioSink, "qt.multimedia.android.audiosink")
15
16QAndroidAudioSinkStream::QAndroidAudioSinkStream(QAudioDevice device, const QAudioFormat &format,
17 std::optional<qsizetype> ringbufferSize,
18 QAndroidAudioSink *parent, float volume,
19 std::optional<int32_t> hardwareBufferFrames,
20 AudioEndpointRole role)
25{
27
28 qCDebug(qLcAndroidAudioSink) << "Creating sink for device id:" << m_audioDevice.id()
29 << ", description:" << m_audioDevice.description();
30
31 // NOTE: Don't set device when creating a stream for the default bluetooth device
34
35 // Set buffer parameters
37
38 // NOTE: AAudio doesn't support UINT8, so convert to INT16 if that's requested
41
42 // Set builder parameters for audio sink
45 switch (m_role) {
49 break;
53 break;
58 break;
59 }
60
61 builder.userData = this;
62 builder.callback = [](AAudioStream *, void *userData, void *audioData,
63 int32_t numFrames) -> int {
64 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
68 };
70 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
73 };
74
77}
78
79bool QAndroidAudioSinkStream::open()
80{
81 if (!m_stream->isOpen()) {
82 qCWarning(qLcAndroidAudioSink) << "Stream null";
83 requestStop();
84 return false;
85 }
86
87 if (!m_stream->areStreamParametersRespected())
88 qCWarning(qLcAndroidAudioSink) << "Stream parameters not correct";
89
90 return true;
91}
92
93bool QAndroidAudioSinkStream::start(QIODevice *device)
94{
95 Q_ASSERT(thread()->isCurrentThread());
96 setQIODevice(device);
97 pullFromQIODevice();
98 createQIODeviceConnections(device);
99
100 // TODO: Fill host ringbuffer before starting
101 if (!m_stream->start()) {
102 requestStop();
103 return false;
104 }
105
106 return true;
107}
108
109QIODevice *QAndroidAudioSinkStream::start()
110{
111 auto *writer = createRingbufferWriterDevice();
112 return start(writer) ? writer : nullptr;
113}
114
115bool QAndroidAudioSinkStream::start(AudioCallback cb)
116{
117 Q_ASSERT(thread()->isCurrentThread());
118 m_audioCallback = std::move(cb);
119
120 if (!m_stream->start()) {
121 requestStop();
122 return false;
123 }
124
125 return true;
126}
127
128void QAndroidAudioSinkStream::suspend()
129{
130 Q_ASSERT(thread()->isCurrentThread());
131 m_stream->stop();
132}
133
134void QAndroidAudioSinkStream::resume()
135{
136 Q_ASSERT(thread()->isCurrentThread());
137 m_stream->start();
138}
139
140void QAndroidAudioSinkStream::stop(ShutdownPolicy policy)
141{
142 requestStop();
143 disconnectQIODeviceConnections();
144
145 switch (policy) {
146 case ShutdownPolicy::DrainRingbuffer:
147 stop();
148 break;
149 case ShutdownPolicy::DiscardRingbuffer:
150 reset();
151 break;
152 default:
153 Q_UNREACHABLE_RETURN();
154 }
155}
156
157void QAndroidAudioSinkStream::stop()
158{
159 if (isIdle() || m_audioCallback)
160 return reset();
161
162 stopIdleDetection();
163 connectIdleHandler([this] {
164 Q_ASSERT(thread()->isCurrentThread());
165 if (!isIdle()) // Only handle <not idle> -> <idle> transitions
166 return;
167
168 // We have written everything we want to write, synchronous stop on application thread
169 m_stream->stop();
170 m_self = nullptr; // might delete the instance
171 });
172
173 m_parent = nullptr;
174
175 // Take ownership of self to avoid deletion until AAudio stream is stopped
176 m_self = shared_from_this();
177}
178
179void QAndroidAudioSinkStream::reset()
180{
181 Q_ASSERT(thread()->isCurrentThread());
182 m_stream->stop();
183}
184
185void QAndroidAudioSinkStream::updateStreamIdle(bool arg)
186{
187 if (m_parent)
188 m_parent->updateStreamIdle(arg);
189}
190
191QSpan<std::byte>
193 int numFrames) const noexcept QT_MM_NONBLOCKING
194{
197 * numFrames)
199 return QSpan<std::byte>{ reinterpret_cast<std::byte *>(audioData), byteAmount };
200}
201
204 int numFrames) noexcept QT_MM_NONBLOCKING
205{
208 if (consumedFrames != static_cast<uint64_t>(numFrames) && isStopRequested())
210
212}
213
216{
217 if (isStopRequested())
219
221 m_format, volume());
223}
224
226{
227 // Handle as IO error which closes the stream
228 // TODO: Check for underruns
229 requestStop();
230 invokeOnAppThread([this] {
231 // clang-format off
233 // clang-format on
234 });
235}
236
241
242} // namespace QtAAudio
243
244QT_END_NAMESPACE
void stop(ShutdownPolicy policy)
void updateStreamIdle(bool arg) override