Ring Daemon 16.0.0
Loading...
Searching...
No Matches
audio_recorder.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 <cstring>
19#include <cstdlib>
20#include "audio_recorder.h"
21
22namespace jami {
23namespace opensl {
24
25/*
26 * bqRecorderCallback(): called for every buffer is full;
27 * pass directly to handler
28 */
29void
31{
32 (static_cast<AudioRecorder*>(rec))->processSLCallback(bq);
33}
34
35void
37{
38 try {
39 sample_buf* dataBuf {nullptr};
40 if (devShadowQueue_.front(&dataBuf)) {
41 devShadowQueue_.pop();
42 dataBuf->size_ = dataBuf->cap_; // device only calls us when it is really full
43 if (dataBuf != &silentBuf_)
44 recQueue_->push(dataBuf);
45 }
46
48 while (freeQueue_->front(&freeBuf) && devShadowQueue_.push(freeBuf)) {
49 freeQueue_->pop();
50 SLASSERT((*bq)->Enqueue(bq, freeBuf->buf_, freeBuf->cap_));
51 }
52
53 // should leave the device to sleep to save power if no buffers
54 if (devShadowQueue_.size() == 0) {
55 // JAMI_WARN("OpenSL: processSLCallback empty queue");
56 (*bq)->Enqueue(bq, silentBuf_.buf_, silentBuf_.cap_);
57 devShadowQueue_.push(&silentBuf_);
58 }
59 if (callback_)
60 callback_();
61 } catch (const std::exception& e) {
62 JAMI_ERR("OpenSL: processSLCallback exception: %s", e.what());
63 }
64}
65
67 : sampleInfo_(sampleFormat)
68{
69 JAMI_DBG("Creating OpenSL record stream");
70
71 // configure audio source/
75 nullptr};
76 SLDataSource audioSrc = {&loc_dev, nullptr};
77
78 // configure audio sink
81
82 auto format_pcm = convertToSLSampleFormat(sampleInfo_);
84
85 // create audio recorder
86 // (requires the RECORD_AUDIO permission)
92 const SLboolean req[] = {SL_BOOLEAN_TRUE,
97
98 SLresult result;
99 result = (*slEngine)->CreateAudioRecorder(slEngine,
100 &recObjectItf_,
101 &audioSrc,
102 &audioSnk,
103 sizeof(ids) / sizeof(ids[0]),
104 ids,
105 req);
106 SLASSERT(result);
107
110 result = (*recObjectItf_)
111 ->GetInterface(recObjectItf_, SL_IID_ANDROIDCONFIGURATION, &recordConfig);
112 SLASSERT(result);
113 result = (*recordConfig)
114 ->SetConfiguration(recordConfig,
116 &streamType,
117 sizeof(SLint32));
118
119 bool aec {true}, agc(true), ns(true);
120
121 result = (*recObjectItf_)->Realize(recObjectItf_, SL_BOOLEAN_FALSE);
122 SLASSERT(result);
123 result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_RECORD, &recItf_);
124 SLASSERT(result);
125
126 /* Check actual performance mode granted*/
128 SLuint32 modeSize = sizeof(SLuint32);
129 result = (*recordConfig)
130 ->GetConfiguration(recordConfig,
132 &modeSize,
133 (void*) &modeRetrieved);
134 if (result == SL_RESULT_SUCCESS) {
135 JAMI_WARN("Actual performance mode is %u\n", modeRetrieved);
136 }
137
138 /* Enable AEC if requested */
139 if (aec) {
141 result = (*recObjectItf_)
142 ->GetInterface(recObjectItf_,
144 (void*) &aecItf);
145 JAMI_WARN("AEC is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
146 if (SL_RESULT_SUCCESS == result) {
147 SLboolean enabled;
148 if ((*aecItf)->IsEnabled(aecItf, &enabled) == SL_RESULT_SUCCESS) {
149 JAMI_WARN("AEC was %s\n", enabled ? "enabled" : "not enabled");
150 (*aecItf)->SetEnabled(aecItf, true);
151 if ((*aecItf)->IsEnabled(aecItf, &enabled) == SL_RESULT_SUCCESS) {
152 JAMI_WARN("AEC is now %s\n", enabled ? "enabled" : "not enabled");
153 hasNativeAEC_ = enabled;
154 }
155 }
156 }
157 }
158 /* Enable AGC if requested */
159 if (agc) {
161 result = (*recObjectItf_)
162 ->GetInterface(recObjectItf_,
164 (void*) &agcItf);
165 JAMI_WARN("AGC is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
166 if (SL_RESULT_SUCCESS == result) {
167 SLboolean enabled;
168 if ((*agcItf)->IsEnabled(agcItf, &enabled) == SL_RESULT_SUCCESS) {
169 JAMI_WARN("AGC was %s\n", enabled ? "enabled" : "not enabled");
170 (*agcItf)->SetEnabled(agcItf, true);
171 if ((*agcItf)->IsEnabled(agcItf, &enabled) == SL_RESULT_SUCCESS) {
172 JAMI_WARN("AGC is now %s\n", enabled ? "enabled" : "not enabled");
173 }
174 }
175 }
176 }
177 /* Enable NS if requested */
178 if (ns) {
180 result = (*recObjectItf_)
181 ->GetInterface(recObjectItf_, SL_IID_ANDROIDNOISESUPPRESSION, (void*) &nsItf);
182 JAMI_WARN("NS is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
183 if (SL_RESULT_SUCCESS == result) {
184 SLboolean enabled;
185 if ((*nsItf)->IsEnabled(nsItf, &enabled) == SL_RESULT_SUCCESS) {
186 JAMI_WARN("NS was %s\n", enabled ? "enabled" : "not enabled");
187 (*nsItf)->SetEnabled(nsItf, true);
188 if ((*nsItf)->IsEnabled(nsItf, &enabled) == SL_RESULT_SUCCESS) {
189 JAMI_WARN("NS is now %s\n", enabled ? "enabled" : "not enabled");
190 hasNativeNS_ = enabled;
191 }
192 }
193 }
194 }
195
196 result = (*recObjectItf_)
197 ->GetInterface(recObjectItf_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recBufQueueItf_);
198 SLASSERT(result);
199
200 result = (*recBufQueueItf_)->RegisterCallback(recBufQueueItf_, bqRecorderCallback, this);
201 SLASSERT(result);
202
203 silentBuf_ = {(format_pcm.containerSize >> 3) * format_pcm.numChannels * bufSize};
204 silentBuf_.size_ = silentBuf_.cap_;
205 av_samples_set_silence(&silentBuf_.buf_, 0, (int)bufSize, (int)sampleInfo_.nb_channels, sampleInfo_.sampleFormat);
206}
207
208bool
210{
211 JAMI_DBG("OpenSL record start");
212 if (!freeQueue_ || !recQueue_) {
213 JAMI_ERR("====NULL pointer to Start(%p, %p)", freeQueue_, recQueue_);
214 return false;
215 }
216 audioBufCount = 0;
217
218 SLresult result;
219 // in case already recording, stop recording and clear buffer queue
220 result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);
221 SLASSERT(result);
222 result = (*recBufQueueItf_)->Clear(recBufQueueItf_);
223 SLASSERT(result);
224
225 for (int i = 0; i < RECORD_DEVICE_KICKSTART_BUF_COUNT; i++) {
227 if (!freeQueue_->front(&buf)) {
228 JAMI_ERR("=====OutOfFreeBuffers @ startingRecording @ (%d)", i);
229 break;
230 }
231 freeQueue_->pop();
232 assert(buf->buf_ && buf->cap_ && !buf->size_);
233
234 result = (*recBufQueueItf_)->Enqueue(recBufQueueItf_, buf->buf_, buf->cap_);
235 SLASSERT(result);
236 devShadowQueue_.push(buf);
237 }
238
239 result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_RECORDING);
240 SLASSERT(result);
241
242 return result == SL_RESULT_SUCCESS;
243}
244
245bool
247{
248 JAMI_DBG("OpenSL record stop");
249 // in case already recording, stop recording and clear buffer queue
251 SLresult result = (*recItf_)->GetRecordState(recItf_, &curState);
252 SLASSERT(result);
254 return true;
255
256 result = (*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);
257 SLASSERT(result);
258 callback_ = {};
259
260 result = (*recBufQueueItf_)->Clear(recBufQueueItf_);
261 SLASSERT(result);
262
263 sample_buf* buf {nullptr};
264 while (devShadowQueue_.front(&buf)) {
265 devShadowQueue_.pop();
266 freeQueue_->push(buf);
267 }
268
269 return true;
270}
271
273{
274 JAMI_DBG("Destroying OpenSL record stream");
275
276 // destroy audio recorder object, and invalidate all associated interfaces
277 if (recObjectItf_) {
278 (*recObjectItf_)->Destroy(recObjectItf_);
279 }
280}
281
282void
284{
285 assert(freeQ && recQ);
286 freeQueue_ = freeQ;
287 recQueue_ = recQ;
288}
289
290size_t
292{
293 return devShadowQueue_.size();
294}
295
296} // namespace opensl
297} // namespace jami
#define SLASSERT(x)
#define DEVICE_SHADOW_BUFFER_QUEUE_LEN
#define RECORD_DEVICE_KICKSTART_BUF_COUNT
bool front(T *out_item)
uint32_t size(void)
bool push(const T &item)
Definition buf_manager.h:53
void processSLCallback(SLAndroidSimpleBufferQueueItf bq)
void setBufQueues(AudioQueue *freeQ, AudioQueue *recQ)
AudioRecorder(jami::AudioFormat, size_t bufSize, SLEngineItf engineEngine)
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *rec)
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
size_t cap_
uint8_t * buf_
size_t size_