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
31 // NOTE: Don't set device when creating a stream for the default bluetooth device
34
35 // Set buffer parameters
38 : 1024;
39
40 // NOTE: AAudio doesn't support UINT8, so convert to INT16 if that's requested
44 }
45
46 // Set builder parameters for audio sink
49 switch (m_role) {
53 break;
57 break;
62 break;
63 }
64
65 builder.userData = this;
66 builder.callback = [](AAudioStream *, void *userData, void *audioData,
67 int32_t numFrames) -> int {
68 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
73 };
75 auto *stream = reinterpret_cast<QAndroidAudioSinkStream *>(userData);
78 };
79
83 // Original sample format unsupported, so doing sample format conversion
86 }
87}
88
89bool QAndroidAudioSinkStream::open()
90{
91 if (!m_stream->isOpen()) {
92 qCWarning(qLcAndroidAudioSink) << "Stream null";
93 requestStop();
94 return false;
95 }
96
97 if (!m_stream->areStreamParametersRespected())
98 qCWarning(qLcAndroidAudioSink) << "Stream parameters not correct";
99
100 return true;
101}
102
103bool QAndroidAudioSinkStream::start(QIODevice *device)
104{
105 Q_ASSERT(thread()->isCurrentThread());
106 setQIODevice(device);
107 pullFromQIODevice();
108 createQIODeviceConnections(device);
109
110 // TODO: Fill host ringbuffer before starting
111 if (!m_stream->start()) {
112 requestStop();
113 return false;
114 }
115
116 return true;
117}
118
119QIODevice *QAndroidAudioSinkStream::start()
120{
121 auto *writer = createRingbufferWriterDevice();
122 return start(writer) ? writer : nullptr;
123}
124
125bool QAndroidAudioSinkStream::start(AudioCallback cb)
126{
127 Q_ASSERT(thread()->isCurrentThread());
128 m_audioCallback = std::move(cb);
129
130 if (!m_stream->start()) {
131 requestStop();
132 return false;
133 }
134
135 return true;
136}
137
138void QAndroidAudioSinkStream::suspend()
139{
140 Q_ASSERT(thread()->isCurrentThread());
141 m_stream->stop();
142}
143
144void QAndroidAudioSinkStream::resume()
145{
146 Q_ASSERT(thread()->isCurrentThread());
147 m_stream->start();
148}
149
150void QAndroidAudioSinkStream::stop(ShutdownPolicy policy)
151{
152 requestStop();
153 disconnectQIODeviceConnections();
154
155 switch (policy) {
156 case ShutdownPolicy::DrainRingbuffer:
157 stop();
158 break;
159 case ShutdownPolicy::DiscardRingbuffer:
160 reset();
161 break;
162 default:
163 Q_UNREACHABLE_RETURN();
164 }
165}
166
167void QAndroidAudioSinkStream::stop()
168{
169 if (isIdle() || m_audioCallback)
170 return reset();
171
172 stopIdleDetection();
173 connectIdleHandler([this] {
174 Q_ASSERT(thread()->isCurrentThread());
175 if (!isIdle()) // Only handle <not idle> -> <idle> transitions
176 return;
177
178 // We have written everything we want to write, synchronous stop on application thread
179 m_stream->stop();
180 m_self = nullptr; // might delete the instance
181 });
182
183 m_parent = nullptr;
184
185 // Take ownership of self to avoid deletion until AAudio stream is stopped
186 m_self = shared_from_this();
187}
188
189void QAndroidAudioSinkStream::reset()
190{
191 Q_ASSERT(thread()->isCurrentThread());
192 m_stream->stop();
193}
194
195void QAndroidAudioSinkStream::updateStreamIdle(bool arg)
196{
197 if (m_parent)
198 m_parent->updateStreamIdle(arg);
199}
200
201QSpan<std::byte>
203 int numFrames) const noexcept QT_MM_NONBLOCKING
204{
207 return QSpan{ reinterpret_cast<std::byte *>(audioData), byteAmount };
208}
209
212 int numFrames) noexcept QT_MM_NONBLOCKING
213{
219 if (consumedFrames != static_cast<uint64_t>(numFrames) && isStopRequested())
221
223}
224
227{
228 if (isStopRequested())
230
231 if (m_hostFormat)
233 *m_hostFormat);
234 else
236
238}
239
241{
242 // Handle as IO error which closes the stream
243 // TODO: Check for underruns
244 requestStop();
245 invokeOnAppThread([this] {
246 // clang-format off
248 // clang-format on
249 });
250}
251
256
258 = default;
259
260} // namespace QtAAudio
261
262QT_END_NAMESPACE
void stop(ShutdownPolicy policy)
void updateStreamIdle(bool arg) override
Combined button and popup list for selecting options.