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<NativePeriodFrames> nativePeriodFrames,
20 AudioEndpointRole role)
25{
27
28 qCDebug(qLcAndroidAudioSink) << "Creating sink for device id:" << m_audioDevice.id()
29 << ", description:" << m_audioDevice.description();
30
32
33 // Set buffer parameters
36 : 1024;
37
38 // NOTE: AAudio doesn't support UINT8, so convert to INT16 if that's requested
42 }
43
44 // Set builder parameters for audio sink
47 switch (m_role) {
51 break;
55 break;
60 break;
61 }
62
65
66 builder.userData = this;
67 builder.callback = [](AAudioStream *, void *userData, void *audioData,
68 int32_t numFrames) -> int {
69 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
74 };
76 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
79 };
80
84 // Original sample format unsupported, so doing sample format conversion
87 }
88}
89
90bool QAndroidAudioSinkStream::open()
91{
92 if (!m_stream->isOpen()) {
93 qCWarning(qLcAndroidAudioSink) << "Stream null";
94 requestStop();
95 return false;
96 }
97
98 if (!m_stream->areStreamParametersRespected())
99 qCWarning(qLcAndroidAudioSink) << "Stream parameters not correct";
100
101 return true;
102}
103
104bool QAndroidAudioSinkStream::start(QIODevice *device)
105{
106 Q_ASSERT(thread()->isCurrentThread());
107 setQIODevice(device);
108 pullFromQIODevice();
109 createQIODeviceConnections(device);
110
111 // TODO: Fill host ringbuffer before starting
112 if (!m_stream->start()) {
113 requestStop();
114 return false;
115 }
116
117 return true;
118}
119
120QIODevice *QAndroidAudioSinkStream::start()
121{
122 auto *writer = createRingbufferWriterDevice();
123 return start(writer) ? writer : nullptr;
124}
125
126bool QAndroidAudioSinkStream::start(AudioCallback cb)
127{
128 Q_ASSERT(thread()->isCurrentThread());
129 m_audioCallback = std::move(cb);
130
131 if (!m_stream->start()) {
132 requestStop();
133 return false;
134 }
135
136 return true;
137}
138
139void QAndroidAudioSinkStream::suspend()
140{
141 Q_ASSERT(thread()->isCurrentThread());
142 m_stream->stop();
143}
144
145void QAndroidAudioSinkStream::resume()
146{
147 Q_ASSERT(thread()->isCurrentThread());
148 m_stream->start();
149}
150
151void QAndroidAudioSinkStream::stop(ShutdownPolicy policy)
152{
153 requestStop();
154 disconnectQIODeviceConnections();
155
156 switch (policy) {
157 case ShutdownPolicy::DrainRingbuffer:
158 stop();
159 break;
160 case ShutdownPolicy::DiscardRingbuffer:
161 reset();
162 break;
163 default:
164 Q_UNREACHABLE_RETURN();
165 }
166}
167
168void QAndroidAudioSinkStream::stop()
169{
170 if (isIdle() || m_audioCallback)
171 return reset();
172
173 stopIdleDetection();
174 connectIdleHandler([this] {
175 Q_ASSERT(thread()->isCurrentThread());
176 if (!isIdle()) // Only handle <not idle> -> <idle> transitions
177 return;
178
179 // We have written everything we want to write, synchronous stop on application thread
180 m_stream->stop();
181 m_self = nullptr; // might delete the instance
182 });
183
184 m_parent = nullptr;
185
186 // Take ownership of self to avoid deletion until AAudio stream is stopped
187 m_self = shared_from_this();
188}
189
190void QAndroidAudioSinkStream::reset()
191{
192 Q_ASSERT(thread()->isCurrentThread());
193 m_stream->stop();
194}
195
196void QAndroidAudioSinkStream::updateStreamIdle(bool arg)
197{
198 if (m_parent)
199 m_parent->updateStreamIdle(arg);
200}
201
202QSpan<std::byte>
204 int numFrames) const noexcept Q_DECL_NONBLOCKING_FUNCTION
205{
208 return QSpan{ reinterpret_cast<std::byte *>(audioData), byteAmount };
209}
210
214{
220 if (consumedFrames != static_cast<uint64_t>(numFrames) && isStopRequested())
222
224}
225
228{
229 if (isStopRequested())
231
232 if (m_hostFormat)
234 *m_hostFormat);
235 else
237
239}
240
242{
243 // Handle as IO error which closes the stream
244 // TODO: Check for underruns
245 requestStop();
246 invokeOnAppThread([this] {
247 // clang-format off
249 // clang-format on
250 });
251}
252
257
259 = default;
260
261} // namespace QtAAudio
262
263QT_END_NAMESPACE
void stop(ShutdownPolicy policy)
void updateStreamIdle(bool arg) override
Combined button and popup list for selecting options.