Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
4
#
include
"qandroidaudiosink_p.h"
5
6
#
include
"qandroidaudioutil_p.h"
7
8
#
include
<
QtMultimedia
/
private
/
qaudiohelpers_p
.
h
>
9
10
QT_BEGIN_NAMESPACE
11
12
namespace
QtAAudio
{
13
14
Q_STATIC_LOGGING_CATEGORY(
qLcAndroidAudioSink
,
"qt.multimedia.android.audiosink"
)
15
16
QAndroidAudioSinkStream::QAndroidAudioSinkStream(QAudioDevice device,
const
QAudioFormat &format,
17
std::optional<qsizetype> ringbufferSize,
18
QAndroidAudioSink *parent,
float
volume,
19
std::optional<NativePeriodFrames> nativePeriodFrames,
20
AudioEndpointRole role)
21
:
QtMultimediaPrivate
::
QPlatformAudioSinkStream
(
std
::
move
(
device
),
format
,
ringbufferSize
,
22
nativePeriodFrames
,
volume
),
23
m_parent
(
parent
),
24
m_role
(
role
)
25
{
26
QtAAudio
::
StreamBuilder
builder
(
format
);
27
28
qCDebug
(
qLcAndroidAudioSink
) <<
"Creating sink for device id:"
<<
m_audioDevice
.
id
()
29
<<
", description:"
<<
m_audioDevice
.
description
();
30
31
builder
.
deviceId
=
m_audioDevice
.
id
().
toInt
();
32
33
// Set buffer parameters
34
builder
.
bufferCapacity
=
m_nativePeriodFrames
35
?
qToUnderlying
(*
m_nativePeriodFrames
)
36
: 1024;
37
38
// NOTE: AAudio doesn't support UINT8, so convert to INT16 if that's requested
39
if
(
format
.
sampleFormat
() ==
QAudioFormat
::
UInt8
) {
40
m_hostFormat
=
format
;
41
m_hostFormat
->
setSampleFormat
(
QAudioFormat
::
Int16
);
42
}
43
44
// Set builder parameters for audio sink
45
builder
.
params
.
sharingMode
=
AAUDIO_SHARING_MODE_SHARED
;
46
builder
.
params
.
direction
=
AAUDIO_DIRECTION_OUTPUT
;
47
switch
(
m_role
) {
48
case
QtMultimediaPrivate
::
AudioEndpointRole
::
SoundEffect
:
49
builder
.
params
.
outputUsage
=
AAUDIO_USAGE_GAME
;
50
builder
.
params
.
outputContentType
=
AAUDIO_CONTENT_TYPE_SONIFICATION
;
51
break
;
52
case
QtMultimediaPrivate
::
AudioEndpointRole
::
Accessibility
:
53
builder
.
params
.
outputUsage
=
AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY
;
54
builder
.
params
.
outputContentType
=
AAUDIO_CONTENT_TYPE_SPEECH
;
55
break
;
56
case
QtMultimediaPrivate
::
AudioEndpointRole
::
MediaPlayback
:
57
case
QtMultimediaPrivate
::
AudioEndpointRole
::
Other
:
58
builder
.
params
.
outputUsage
=
AAUDIO_USAGE_MEDIA
;
59
builder
.
params
.
outputContentType
=
AAUDIO_CONTENT_TYPE_MUSIC
;
60
break
;
61
}
62
63
if
(
QAndroidAudioUtil
::
supportsLowLatency
() && !
QAndroidAudioUtil
::
isBluetoothDevice
(
m_audioDevice
))
64
builder
.
params
.
performanceMode
=
AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
;
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
);
70
Q_ASSERT
(
stream
);
71
QSpan
<
std
::
byte
>
audioSpan
=
stream
->
getHostSpan
(
audioData
,
numFrames
);
72
return
stream
->
m_audioCallback
?
stream
->
processCallback
(
audioSpan
)
73
:
stream
->
processRingbuffer
(
audioSpan
,
numFrames
);
74
};
75
builder
.
errorCallback
= [](
AAudioStream
*,
void
*
userData
,
aaudio_result_t
error
) ->
void
{
76
auto
*
stream
=
reinterpret_cast
<
QAndroidAudioSinkStream
*>(
userData
);
77
Q_ASSERT
(
stream
);
78
stream
->
handleError
(
error
);
79
};
80
81
builder
.
setupBuilder
();
82
m_stream
=
std
::
make_unique
<
QtAAudio
::
Stream
>(
builder
);
83
if
(
builder
.
format
.
sampleFormat
() !=
format
.
sampleFormat
()) {
84
// Original sample format unsupported, so doing sample format conversion
85
Q_ASSERT
(
builder
.
format
.
sampleFormat
() ==
QAudioFormat
::
Float
);
86
m_hostFormat
=
builder
.
format
;
87
}
88
}
89
90
bool
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
104
bool
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
120
QIODevice
*QAndroidAudioSinkStream::
start
()
121
{
122
auto
*writer = createRingbufferWriterDevice();
123
return
start(writer) ? writer :
nullptr
;
124
}
125
126
bool
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
139
void
QAndroidAudioSinkStream::
suspend
()
140
{
141
Q_ASSERT(thread()->isCurrentThread());
142
m_stream->stop();
143
}
144
145
void
QAndroidAudioSinkStream::
resume
()
146
{
147
Q_ASSERT(thread()->isCurrentThread());
148
m_stream->start();
149
}
150
151
void
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
168
void
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
190
void
QAndroidAudioSinkStream::reset()
191
{
192
Q_ASSERT(thread()->isCurrentThread());
193
m_stream->stop();
194
}
195
196
void
QAndroidAudioSinkStream::
updateStreamIdle
(
bool
arg)
197
{
198
if
(m_parent)
199
m_parent->updateStreamIdle(arg);
200
}
201
202
QSpan
<
std
::
byte
>
203
QAndroidAudioSinkStream
::
getHostSpan
(
void
*
audioData
,
204
int
numFrames
)
const
noexcept
Q_DECL_NONBLOCKING_FUNCTION
205
{
206
qsizetype
byteAmount
=
m_hostFormat
?
m_hostFormat
->
bytesForFrames
(
numFrames
)
207
:
m_format
.
bytesForFrames
(
numFrames
);
208
return
QSpan
{
reinterpret_cast
<
std
::
byte
*>(
audioData
),
byteAmount
};
209
}
210
211
aaudio_data_callback_result_t
212
QAndroidAudioSinkStream
::
processRingbuffer
(
QSpan
<
std
::
byte
>
audioSpan
,
213
int
numFrames
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
214
{
215
auto
consumedFrames
=
m_hostFormat
216
?
QPlatformAudioSinkStream
::
process
(
217
audioSpan
,
numFrames
,
218
QAudioHelperInternal
::
toNativeSampleFormat
(
m_hostFormat
->
sampleFormat
()))
219
:
QPlatformAudioSinkStream
::
process
(
audioSpan
,
numFrames
);
220
if
(
consumedFrames
!=
static_cast
<
uint64_t
>(
numFrames
) &&
isStopRequested
())
221
return
AAUDIO_CALLBACK_RESULT_STOP
;
222
223
return
AAUDIO_CALLBACK_RESULT_CONTINUE
;
224
}
225
226
aaudio_data_callback_result_t
227
QAndroidAudioSinkStream
::
processCallback
(
QSpan
<
std
::
byte
>
audioSpan
)
noexcept
Q_DECL_NONBLOCKING_FUNCTION
228
{
229
if
(
isStopRequested
())
230
return
AAUDIO_CALLBACK_RESULT_STOP
;
231
232
if
(
m_hostFormat
)
233
QtMultimediaPrivate
::
runAudioCallback
(*
m_audioCallback
,
audioSpan
,
m_format
,
volume
(),
234
*
m_hostFormat
);
235
else
236
QtMultimediaPrivate
::
runAudioCallback
(*
m_audioCallback
,
audioSpan
,
m_format
,
volume
());
237
238
return
AAUDIO_CALLBACK_RESULT_CONTINUE
;
239
}
240
241
void
QAndroidAudioSinkStream
::
handleError
(
aaudio_result_t
)
242
{
243
// Handle as IO error which closes the stream
244
// TODO: Check for underruns
245
requestStop
();
246
invokeOnAppThread
([
this
] {
247
// clang-format off
248
handleIOError
(
m_parent
);
249
// clang-format on
250
});
251
}
252
253
QAndroidAudioSink
::
QAndroidAudioSink
(
QAudioDevice
device
,
const
QAudioFormat
&
fmt
,
QObject
*
parent
)
254
:
BaseClass
(
std
::
move
(
device
),
fmt
,
parent
)
255
{
256
}
257
258
QAndroidAudioSink
::~
QAndroidAudioSink
()
259
=
default
;
260
261
}
// namespace QtAAudio
262
263
QT_END_NAMESPACE
QtAAudio::QAndroidAudioSinkStream::open
bool open()
Definition
qandroidaudiosink.cpp:90
QtAAudio::QAndroidAudioSinkStream::start
QIODevice * start()
Definition
qandroidaudiosink.cpp:120
QtAAudio::QAndroidAudioSinkStream::start
bool start(AudioCallback cb)
Definition
qandroidaudiosink.cpp:126
QtAAudio::QAndroidAudioSinkStream::stop
void stop(ShutdownPolicy policy)
Definition
qandroidaudiosink.cpp:151
QtAAudio::QAndroidAudioSinkStream::suspend
void suspend()
Definition
qandroidaudiosink.cpp:139
QtAAudio::QAndroidAudioSinkStream::start
bool start(QIODevice *device)
Definition
qandroidaudiosink.cpp:104
QtAAudio::QAndroidAudioSinkStream::updateStreamIdle
void updateStreamIdle(bool arg) override
Definition
qandroidaudiosink.cpp:196
QtAAudio::QAndroidAudioSinkStream::resume
void resume()
Definition
qandroidaudiosink.cpp:145
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
QtAAudio
Definition
qaaudiostream.cpp:58
qtmultimedia
src
multimedia
android
qandroidaudiosink.cpp
Generated on
for Qt by
1.18.0