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