Ring Daemon 16.0.0
Loading...
Searching...
No Matches
audio_player.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 The Android Open Source Project
3 * Copyright 2015-2025 Savoir-faire Linux Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "audio_player.h"
19#include "logger.h"
20
21#include <SLES/OpenSLES_AndroidConfiguration.h>
22
23#include <cstdlib>
24
25namespace jami {
26namespace opensl {
27
28/*
29 * Called by OpenSL SimpleBufferQueue for every audio buffer played
30 * directly pass through to our handler.
31 * The regularity of this callback from openSL/Android System affects
32 * playback continuity. If it does not callback in the regular time
33 * slot, you are under big pressure for audio processing[here we do
34 * not do any filtering/mixing]. Callback from fast audio path are
35 * much more regular than other audio paths by my observation. If it
36 * very regular, you could buffer much less audio samples between
37 * recorder and player, hence lower latency.
38 */
39void
41{
42 (static_cast<AudioPlayer*>(ctx))->processSLCallback(bq);
43}
44
45void
47{
48 std::unique_lock lk(m_, std::defer_lock);
49 if (!lk.try_lock())
50 return;
51
52 // retrieve the finished device buf and put onto the free queue
53 // so recorder could re-use it
55 if (!devShadowQueue_.front(&buf)) {
56 JAMI_ERR("AudioPlayer buffer lost");
57 /*
58 * This should not happen: we got a callback,
59 * but we have no buffer in deviceShadowedQueue
60 * we lost buffers this way...(ERROR)
61 */
62 return;
63 }
64 devShadowQueue_.pop();
65
66 if (buf != &silentBuf_) {
67 buf->size_ = 0;
68 if (!freeQueue_->push(buf)) {
69 JAMI_ERR("buffer lost");
70 }
71 }
72
73 if (callback_)
74 callback_();
75
76 while (playQueue_->front(&buf) && devShadowQueue_.push(buf)) {
77 if ((*bq)->Enqueue(bq, buf->buf_, buf->size_) != SL_RESULT_SUCCESS) {
78 devShadowQueue_.pop();
79 JAMI_ERR("enqueue failed %zu %d %d %d",
80 buf->size_,
81 freeQueue_->size(),
82 playQueue_->size(),
83 devShadowQueue_.size());
84 break;
85 } else
86 playQueue_->pop();
87 }
88 if (devShadowQueue_.size() == 0) {
89 for (int i = 0; i < DEVICE_SHADOW_BUFFER_QUEUE_LEN; i++) {
90 if ((*bq)->Enqueue(bq, silentBuf_.buf_, silentBuf_.size_) == SL_RESULT_SUCCESS) {
91 devShadowQueue_.push(&silentBuf_);
92 } else {
93 JAMI_ERR("Enqueue silentBuf_ failed");
94 }
95 }
96 }
97}
98
100 size_t bufSize,
103 : sampleInfo_(sampleFormat)
104{
105 JAMI_DBG("Creating OpenSL playback stream %s", sampleFormat.toString().c_str());
106
107 SLresult result;
108 result = (*slEngine)->CreateOutputMix(slEngine, &outputMixObjectItf_, 0, nullptr, nullptr);
109 SLASSERT(result);
110
111 // realize the output mix
112 result = (*outputMixObjectItf_)->Realize(outputMixObjectItf_, SL_BOOLEAN_FALSE);
113 SLASSERT(result);
114
115 // configure audio source
118
119 auto format_pcm = convertToSLSampleFormat(sampleInfo_);
121
122 // configure audio sink
124 SLDataSink audioSnk = {&loc_outmix, nullptr};
125 /*
126 * create fast path audio player: SL_IID_BUFFERQUEUE and SL_IID_VOLUME interfaces ok,
127 * NO others!
128 */
131 result = (*slEngine)->CreateAudioPlayer(slEngine,
132 &playerObjectItf_,
133 &audioSrc,
134 &audioSnk,
135 sizeof(ids) / sizeof(ids[0]),
136 ids,
137 req);
138 SLASSERT(result);
139
141 result = (*playerObjectItf_)
142 ->GetInterface(playerObjectItf_, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
143 result = (*playerConfig)
144 ->SetConfiguration(playerConfig,
146 &streamType,
147 sizeof(SLint32));
148
149 // realize the player
150 result = (*playerObjectItf_)->Realize(playerObjectItf_, SL_BOOLEAN_FALSE);
151 SLASSERT(result);
152
153 // get the play interface
154 result = (*playerObjectItf_)->GetInterface(playerObjectItf_, SL_IID_PLAY, &playItf_);
155 SLASSERT(result);
156
157 // get the buffer queue interface
158 result = (*playerObjectItf_)
159 ->GetInterface(playerObjectItf_, SL_IID_BUFFERQUEUE, &playBufferQueueItf_);
160 SLASSERT(result);
161
162 // register callback on the buffer queue
163 result = (*playBufferQueueItf_)->RegisterCallback(playBufferQueueItf_, bqPlayerCallback, this);
164 SLASSERT(result);
165
166 result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
167 SLASSERT(result);
168
169 silentBuf_ = {(format_pcm.containerSize >> 3) * format_pcm.numChannels * bufSize};
170 silentBuf_.size_ = silentBuf_.cap_;
171 av_samples_set_silence(&silentBuf_.buf_, 0, (int)bufSize, (int)sampleInfo_.nb_channels, sampleInfo_.sampleFormat);
172}
173
175{
176 JAMI_DBG("Destroying OpenSL playback stream");
177 std::lock_guard lk(m_);
178
179 // destroy buffer queue audio player object, and invalidate all associated interfaces
180 if (playerObjectItf_) {
181 (*playerObjectItf_)->Destroy(playerObjectItf_);
182 }
183
184 // destroy output mix object, and invalidate all associated interfaces
185 if (outputMixObjectItf_) {
186 (*outputMixObjectItf_)->Destroy(outputMixObjectItf_);
187 }
188}
189
190void
192{
193 playQueue_ = playQ;
194 freeQueue_ = freeQ;
195}
196
197bool
199{
200 JAMI_DBG("OpenSL playback start");
201 std::unique_lock lk(m_);
202 SLuint32 state;
203 SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
204 if (result != SL_RESULT_SUCCESS)
205 return false;
206 if (state == SL_PLAYSTATE_PLAYING)
207 return true;
208
209 result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
210 SLASSERT(result);
211
212 devShadowQueue_.push(&silentBuf_);
213 result = (*playBufferQueueItf_)->Enqueue(playBufferQueueItf_, silentBuf_.buf_, silentBuf_.size_);
214 if (result != SL_RESULT_SUCCESS) {
215 JAMI_ERR("Enqueue silentBuf_ failed, result = %d", result);
216 devShadowQueue_.pop();
217 }
218
219 lk.unlock();
220 result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_PLAYING);
221 SLASSERT(result);
222
223 return true;
224}
225
226bool
228{
229 if (!playItf_)
230 return false;
231 SLuint32 state;
232 SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
233 return result == SL_RESULT_SUCCESS && state == SL_PLAYSTATE_PLAYING;
234}
235
236void
238{
239 JAMI_DBG("OpenSL playback stop");
240 SLuint32 state;
241
242 std::lock_guard lk(m_);
243 SLresult result = (*playItf_)->GetPlayState(playItf_, &state);
244 SLASSERT(result);
245
246 if (state == SL_PLAYSTATE_STOPPED)
247 return;
248
249 callback_ = {};
250 result = (*playItf_)->SetPlayState(playItf_, SL_PLAYSTATE_STOPPED);
251 SLASSERT(result);
252
253 // Consume all non-completed audio buffers
254 sample_buf* buf = nullptr;
255 while (devShadowQueue_.front(&buf)) {
256 buf->size_ = 0;
257 devShadowQueue_.pop();
258 freeQueue_->push(buf);
259 }
260 while (playQueue_->front(&buf)) {
261 buf->size_ = 0;
262 playQueue_->pop();
263 freeQueue_->push(buf);
264 }
265}
266
267void
269{
270 while (count--) {
271 sample_buf* buf = nullptr;
272 if (!playQueue_->front(&buf)) {
273 JAMI_ERR("====Run out of buffers in %s @(count = %d)", __FUNCTION__, count);
274 break;
275 }
276 if (!devShadowQueue_.push(buf)) {
277 break; // PlayerBufferQueue is full!!!
278 }
279
280 SLresult result = (*playBufferQueueItf_)->Enqueue(playBufferQueueItf_, buf->buf_, buf->size_);
281 if (result != SL_RESULT_SUCCESS) {
282 JAMI_ERR("%s Error @( %p, %zu ), result = %d",
284 (void*) buf->buf_,
285 buf->size_,
286 result);
287 /*
288 * when this happens, a buffer is lost. Need to remove the buffer
289 * from top of the devShadowQueue. Since I do not have it now,
290 * just pop out the one that is being played right now. Afer a
291 * cycle it will be normal.
292 */
293 devShadowQueue_.front(&buf), devShadowQueue_.pop();
294 freeQueue_->push(buf);
295 break;
296 }
297 playQueue_->pop(); // really pop out the buffer
298 }
299}
300
301size_t
303{
304 return devShadowQueue_.size();
305}
306
307} // namespace opensl
308} // namespace jami
#define SLASSERT(x)
#define DEVICE_SHADOW_BUFFER_QUEUE_LEN
bool front(T *out_item)
uint32_t size(void)
bool push(const T &item)
Definition buf_manager.h:53
void processSLCallback(SLAndroidSimpleBufferQueueItf bq)
void setBufQueue(AudioQueue *playQ, AudioQueue *freeQ)
void playAudioBuffers(unsigned count)
AudioPlayer(jami::AudioFormat sampleFormat, size_t bufSize, SLEngineItf engine, SLint32 streamType)
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *ctx)
SLAndroidDataFormat_PCM_EX convertToSLSampleFormat(const jami::AudioFormat &infos)
void emitSignal(Args... args)
Definition ring_signal.h:64
Structure to hold sample rate and channel number associated with audio data.
AVSampleFormat sampleFormat
std::string toString() const
size_t cap_
uint8_t * buf_
size_t size_