Ring Daemon 16.0.0
Loading...
Searching...
No Matches
opensllayer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "opensllayer.h"
19
20#include "client/ring_signal.h"
21
23#include "audio/ringbuffer.h"
24#include "libav_utils.h"
25#include "manager.h"
26#include "logger.h"
27
28#include <SLES/OpenSLES_AndroidConfiguration.h>
29
30#include <thread>
31#include <chrono>
32#include <cstdio>
33#include <cassert>
34#include <unistd.h>
35
36namespace jami {
37
38// Constructor
42
43// Destructor
48
49void
51{
52 using namespace std::placeholders;
53
54 std::lock_guard lock(mutex_);
55 if (!engineObject_)
57
58 JAMI_WARN("Start OpenSL audio layer");
59
60 if (stream == AudioDeviceType::PLAYBACK) {
61 if (not player_) {
62 try {
63 player_.reset(new opensl::AudioPlayer(hardwareFormat_,
64 hardwareBuffSize_,
65 engineInterface_,
67 player_->setBufQueue(&playBufQueue_, &freePlayBufQueue_);
68 player_->registerCallback(std::bind(&OpenSLLayer::engineServicePlay, this));
69 player_->start();
70 playbackChanged(true);
71 } catch (const std::exception& e) {
72 JAMI_ERR("Error initializing audio playback: %s", e.what());
73 }
74 if (recorder_)
76 }
77 } else if (stream == AudioDeviceType::RINGTONE) {
78 if (not ringtone_) {
79 try {
80 ringtone_.reset(new opensl::AudioPlayer(hardwareFormat_,
81 hardwareBuffSize_,
82 engineInterface_,
84 ringtone_->setBufQueue(&ringBufQueue_, &freeRingBufQueue_);
85 ringtone_->registerCallback(std::bind(&OpenSLLayer::engineServiceRing, this));
86 ringtone_->start();
87 } catch (const std::exception& e) {
88 JAMI_ERR("Error initializing ringtone playback: %s", e.what());
89 }
90 }
91 } else if (stream == AudioDeviceType::CAPTURE) {
92 if (not recorder_) {
93 std::lock_guard lck(recMtx);
94 try {
95 recorder_.reset(
96 new opensl::AudioRecorder(hardwareFormat_, hardwareBuffSize_, engineInterface_));
97 recorder_->setBufQueues(&freeRecBufQueue_, &recBufQueue_);
98 recorder_->registerCallback(std::bind(&OpenSLLayer::engineServiceRec, this));
99 setHasNativeAEC(recorder_->hasNativeAEC());
100 setHasNativeNS(recorder_->hasNativeNS());
101 } catch (const std::exception& e) {
102 JAMI_ERR("Error initializing audio capture: %s", e.what());
103 }
104 if (player_)
106 }
107 }
108 JAMI_WARN("OpenSL audio layer started");
110}
111
112void
114{
115 std::lock_guard lock(mutex_);
116 if (!engineObject_)
117 return; // bufs_ should be initialized
118 JAMI_WARN("Stopping OpenSL audio layer for type %u", (unsigned) stream);
119
120 if (stream == AudioDeviceType::PLAYBACK) {
121 if (player_) {
122 playbackChanged(false);
123 player_->stop();
124 player_.reset();
125 }
126 freePlayBufQueue_.clear();
127 for (int i = 0; i < BUF_COUNT; i++)
128 freePlayBufQueue_.push(&bufs_[i]);
129 playBufQueue_.clear();
130 } else if (stream == AudioDeviceType::RINGTONE) {
131 if (ringtone_) {
132 ringtone_->stop();
133 ringtone_.reset();
134 }
135 // Flush buffer
136 freeRingBufQueue_.clear();
137 for (int i = BUF_COUNT; i < 2 * BUF_COUNT; i++)
138 freeRingBufQueue_.push(&bufs_[i]);
139 ringBufQueue_.clear();
140 } else if (stream == AudioDeviceType::CAPTURE) {
142 // Flush buffer
143 freeRecBufQueue_.clear();
144 for (int i = 2 * BUF_COUNT; i < 3 * BUF_COUNT; i++)
145 freeRecBufQueue_.push(&bufs_[i]);
146 recBufQueue_.clear();
147 }
148
149 if (not player_ and not ringtone_ and not recorder_)
151
152 flush();
153}
154
155std::vector<sample_buf>
156allocateSampleBufs(unsigned count, size_t sizeInByte)
157{
158 std::vector<sample_buf> bufs;
159 if (!count || !sizeInByte)
160 return bufs;
161 bufs.reserve(count);
162 size_t allocSize = (sizeInByte + 3) & ~3; // padding to 4 bytes aligned
163 for (unsigned i = 0; i < count; i++)
164 bufs.emplace_back(allocSize, sizeInByte);
165 return bufs;
166}
167
168void
170{
171 JAMI_WARN("OpenSL init started");
172 std::vector<int32_t> hw_infos;
173 hw_infos.reserve(4);
175 hardwareFormat_ = AudioFormat(hw_infos[0], 1, AV_SAMPLE_FMT_FLT); // Mono on Android
176 hardwareBuffSize_ = hw_infos[1];
177 hardwareFormatAvailable(hardwareFormat_, hardwareBuffSize_);
178
179 SLASSERT(slCreateEngine(&engineObject_, 0, nullptr, 0, nullptr, nullptr));
180 SLASSERT((*engineObject_)->Realize(engineObject_, SL_BOOLEAN_FALSE));
181 SLASSERT((*engineObject_)->GetInterface(engineObject_, SL_IID_ENGINE, &engineInterface_));
182
183 size_t bufSize = hardwareBuffSize_ * hardwareFormat_.getBytesPerFrame();
184 JAMI_LOG("OpenSL init: using buffer of {:d} bytes to support {} with {:d} samples per channel",
185 bufSize,
186 hardwareFormat_.toString(),
187 hardwareBuffSize_);
189 for (int i = 0; i < BUF_COUNT; i++)
190 freePlayBufQueue_.push(&bufs_[i]);
191 for (int i = BUF_COUNT; i < 2 * BUF_COUNT; i++)
192 freeRingBufQueue_.push(&bufs_[i]);
193 for (int i = 2 * BUF_COUNT; i < 3 * BUF_COUNT; i++)
194 freeRecBufQueue_.push(&bufs_[i]);
195 JAMI_WARN("OpenSL init ended");
196}
197
198void
200{
201 JAMI_DBG("Stopping OpenSL");
203
204 if (player_) {
205 player_->stop();
206 player_.reset();
207 }
208 if (ringtone_) {
209 ringtone_->stop();
210 ringtone_.reset();
211 }
212
213 // destroy engine object, and invalidate all associated interfaces
214 JAMI_DBG("Shutdown audio engine");
215 if (engineObject_ != nullptr) {
216 (*engineObject_)->Destroy(engineObject_);
217 engineObject_ = nullptr;
218 engineInterface_ = nullptr;
219 }
220
221 freeRecBufQueue_.clear();
222 recBufQueue_.clear();
223 freePlayBufQueue_.clear();
224 playBufQueue_.clear();
225 freeRingBufQueue_.clear();
226 ringBufQueue_.clear();
227
228 startedCv_.notify_all();
229 bufs_.clear();
230}
231
233OpenSLLayer::dbgEngineGetBufCount()
234{
235 uint32_t count_player = player_->dbgGetDevBufCount();
236 count_player += freePlayBufQueue_.size();
237 count_player += playBufQueue_.size();
238
239 uint32_t count_ringtone = ringtone_->dbgGetDevBufCount();
240 count_ringtone += freeRingBufQueue_.size();
241 count_ringtone += ringBufQueue_.size();
242
243 JAMI_ERR("Buf Disrtibutions: PlayerDev=%zu, PlayQ=%u, FreePlayQ=%u",
244 player_->dbgGetDevBufCount(),
245 playBufQueue_.size(),
246 freePlayBufQueue_.size());
247 JAMI_ERR("Buf Disrtibutions: RingDev=%zu, RingQ=%u, FreeRingQ=%u",
248 ringtone_->dbgGetDevBufCount(),
249 ringBufQueue_.size(),
250 freeRingBufQueue_.size());
251
252 if (count_player != BUF_COUNT) {
253 JAMI_ERR("====Lost Bufs among the queue(supposed = %d, found = %u)",
254 BUF_COUNT,
256 }
257 return count_player;
258}
259
260void
262{
264 while (player_ and freePlayBufQueue_.front(&buf)) {
265 if (auto dat = getToPlay(hardwareFormat_, hardwareBuffSize_)) {
266 buf->size_ = dat->pointer()->nb_samples * dat->getFormat().getBytesPerFrame();
267 if (buf->size_ > buf->cap_) {
268 JAMI_ERR("buf->size_(%zu) > buf->cap_(%zu)", buf->size_, buf->cap_);
269 break;
270 }
271 if (not dat->pointer()->data[0] or not buf->buf_) {
272 JAMI_ERR("null bufer %p -> %p %d",
273 dat->pointer()->data[0],
274 buf->buf_,
275 dat->pointer()->nb_samples);
276 break;
277 }
278 memcpy(buf->buf_, dat->pointer()->data[0], buf->size_);
279 if (!playBufQueue_.push(buf)) {
280 JAMI_WARN("playThread player_ PLAY_KICKSTART_BUFFER_COUNT 1");
281 break;
282 } else
283 freePlayBufQueue_.pop();
284 } else {
285 break;
286 }
287 }
288}
289
290void
292{
294 while (ringtone_ and freeRingBufQueue_.front(&buf)) {
295 if (auto dat = getToRing(hardwareFormat_, hardwareBuffSize_)) {
296 buf->size_ = dat->pointer()->nb_samples * dat->getFormat().getBytesPerFrame();
297 if (buf->size_ > buf->cap_) {
298 JAMI_ERR("buf->size_(%zu) > buf->cap_(%zu)", buf->size_, buf->cap_);
299 break;
300 }
301 if (not dat->pointer()->data[0] or not buf->buf_) {
302 JAMI_ERR("null bufer %p -> %p %d",
303 dat->pointer()->data[0],
304 buf->buf_,
305 dat->pointer()->nb_samples);
306 break;
307 }
308 memcpy(buf->buf_, dat->pointer()->data[0], buf->size_);
309 if (!ringBufQueue_.push(buf)) {
310 JAMI_WARN("playThread ringtone_ PLAY_KICKSTART_BUFFER_COUNT 1");
311 break;
312 } else
313 freeRingBufQueue_.pop();
314 } else {
315 break;
316 }
317 }
318}
319
320void
322{
323 recCv.notify_one();
324}
325
326void
328{
329 if (not recorder_)
330 return;
331 JAMI_DBG("Start audio capture");
332
333 if (recThread.joinable())
334 return;
335 recThread = std::thread([&]() {
336 std::unique_lock lck(recMtx);
337 if (recorder_)
338 recorder_->start();
339 recordChanged(true);
340 while (recorder_) {
341 recCv.wait_for(lck, std::chrono::seconds(1));
342 if (not recorder_)
343 break;
345 while (recBufQueue_.front(&buf)) {
346 recBufQueue_.pop();
347 if (buf->size_ > 0) {
348 auto nb_samples = buf->size_ / hardwareFormat_.getBytesPerFrame();
349 auto out = std::make_shared<AudioFrame>(hardwareFormat_, nb_samples);
350 if (isCaptureMuted_)
352 else
353 memcpy(out->pointer()->data[0], buf->buf_, buf->size_);
354 putRecorded(std::move(out));
355 }
356 buf->size_ = 0;
357 freeRecBufQueue_.push(buf);
358 }
359 }
360 recordChanged(false);
361 });
362
363 JAMI_DBG("Audio capture started");
364}
365
366void
368{
369 JAMI_DBG("Stop audio capture");
370
371 {
372 std::lock_guard lck(recMtx);
373 if (recorder_) {
374 recorder_->stop();
375 recorder_.reset();
376 }
377 }
378 if (recThread.joinable()) {
379 recCv.notify_all();
380 recThread.join();
381 }
382
383 JAMI_DBG("Audio capture stopped");
384}
385
386std::vector<std::string>
388{
389 std::vector<std::string> captureDeviceList;
390
391 // Although OpenSL ES specification allows enumerating
392 // available output (and also input) devices, NDK implementation is not mature enough to
393 // obtain or select proper one (SLAudioIODeviceCapabilitiesItf, the official interface
394 // to obtain such an information)-> SL_FEATURE_UNSUPPORTED
395
397 SLint32 numInputs = 0;
400
402
403 // Get the Audio IO DEVICE CAPABILITIES interface, implicit
405 res = (*engineObject_)
406 ->GetInterface(engineObject_,
408 (void*) &deviceCapabilities);
409 if (res != SL_RESULT_SUCCESS)
410 return captureDeviceList;
411
413 res = (*deviceCapabilities)
414 ->GetAvailableAudioInputs(deviceCapabilities, &numInputs, InputDeviceIDs);
415 if (res != SL_RESULT_SUCCESS)
416 return captureDeviceList;
417
418 // Search for either earpiece microphone or headset microphone input
419 // device - with a preference for the latter
420 for (int i = 0; i < numInputs; i++) {
422 res = (*deviceCapabilities)
423 ->QueryAudioInputCapabilities(deviceCapabilities,
426 if (res != SL_RESULT_SUCCESS)
427 return captureDeviceList;
428
432 JAMI_DBG("SL_DEVCONNECTION_ATTACHED_WIRED : mic_deviceID: %d", InputDeviceIDs[i]);
435 break;
436 } else if (audioInputDescriptor_.deviceConnection == SL_DEVCONNECTION_INTEGRATED
439 JAMI_DBG("SL_DEVCONNECTION_INTEGRATED : mic_deviceID: %d", InputDeviceIDs[i]);
442 break;
443 }
444 }
445
446 if (!mic_available)
447 JAMI_ERR("No mic available");
448
449 return captureDeviceList;
450}
451
452std::vector<std::string>
454{
455 std::vector<std::string> playbackDeviceList;
456 return playbackDeviceList;
457}
458
459void
460OpenSLLayer::updatePreference(AudioPreference& /*preference*/,
461 int /*index*/,
462 AudioDeviceType /*type*/)
463{}
464
465void
467{
468 SLresult result;
469 JAMI_DBG("Engine Interfaces");
472 assert(SL_RESULT_SUCCESS == result);
475
476 JAMI_DBG("Engine number of supported interfaces %u", numSupportedInterfaces);
477 for (SLuint32 i = 0; i < numSupportedInterfaces; i++) {
480 const char* nm = "unknown iid";
481
483 nm = "null";
484 else if (pInterfaceId == SL_IID_OBJECT)
485 nm = "object";
487 nm = "audiodevicecapabilities";
488 else if (pInterfaceId == SL_IID_LED)
489 nm = "led";
490 else if (pInterfaceId == SL_IID_VIBRA)
491 nm = "vibra";
493 nm = "metadataextraction";
495 nm = "metadatatraversal";
497 nm = "dynamicsource";
498 else if (pInterfaceId == SL_IID_OUTPUTMIX)
499 nm = "outputmix";
500 else if (pInterfaceId == SL_IID_PLAY)
501 nm = "play";
503 nm = "prefetchstatus";
505 nm = "playbackrate";
506 else if (pInterfaceId == SL_IID_SEEK)
507 nm = "seek";
508 else if (pInterfaceId == SL_IID_RECORD)
509 nm = "record";
510 else if (pInterfaceId == SL_IID_EQUALIZER)
511 nm = "equalizer";
512 else if (pInterfaceId == SL_IID_VOLUME)
513 nm = "volume";
515 nm = "devicevolume";
517 nm = "bufferqueue";
519 nm = "presetreverb";
521 nm = "environmentalreverb";
523 nm = "effectsend";
525 nm = "3dgrouping";
526 else if (pInterfaceId == SL_IID_3DCOMMIT)
527 nm = "3dcommit";
529 nm = "3dlocation";
530 else if (pInterfaceId == SL_IID_3DDOPPLER)
531 nm = "3ddoppler";
532 else if (pInterfaceId == SL_IID_3DSOURCE)
533 nm = "3dsource";
535 nm = "3dmacroscopic";
536 else if (pInterfaceId == SL_IID_MUTESOLO)
537 nm = "mutesolo";
539 nm = "dynamicinterfacemanagement";
541 nm = "midimessage";
543 nm = "midimutesolo";
544 else if (pInterfaceId == SL_IID_MIDITEMPO)
545 nm = "miditempo";
546 else if (pInterfaceId == SL_IID_MIDITIME)
547 nm = "miditime";
549 nm = "audiodecodercapabilities";
551 nm = "audioencodercapabilities";
553 nm = "audioencoder";
554 else if (pInterfaceId == SL_IID_BASSBOOST)
555 nm = "bassboost";
556 else if (pInterfaceId == SL_IID_PITCH)
557 nm = "pitch";
558 else if (pInterfaceId == SL_IID_RATEPITCH)
559 nm = "ratepitch";
561 nm = "virtualizer";
563 nm = "visualization";
564 else if (pInterfaceId == SL_IID_ENGINE)
565 nm = "engine";
567 nm = "enginecapabilities";
569 nm = "theadsync";
571 nm = "androideffect";
573 nm = "androideffectsend";
575 nm = "androideffectcapabilities";
577 nm = "androidconfiguration";
579 nm = "simplebuferqueue";
580 // else if (pInterfaceId==//SL_IID_ANDROIDBUFFERQUEUESOURCE) nm="bufferqueuesource";
581 JAMI_DBG("%s,", nm);
582 }
583}
584
585} // namespace jami
#define SLASSERT(x)
#define BUF_COUNT
bool front(T *out_item)
uint32_t size(void)
bool push(const T &item)
Definition buf_manager.h:53
bool isCaptureMuted_
True if capture is not to be used.
Definition audiolayer.h:221
std::mutex mutex_
Lock for the entire audio layer.
Definition audiolayer.h:283
std::atomic< Status > status_
Whether or not the audio layer's playback stream is started.
Definition audiolayer.h:260
void playbackChanged(bool started)
void setHasNativeNS(bool hasNS)
void hardwareFormatAvailable(AudioFormat playback, size_t bufSize=0)
Callback to be called by derived classes when the audio output is opened.
std::shared_ptr< AudioFrame > getToRing(AudioFormat format, size_t writableSamples)
void setHasNativeAEC(bool hasEAC)
std::shared_ptr< AudioFrame > getToPlay(AudioFormat format, size_t writableSamples)
void recordChanged(bool started)
std::condition_variable startedCv_
Definition audiolayer.h:261
void putRecorded(std::shared_ptr< AudioFrame > &&frame)
std::vector< std::string > getCaptureDeviceList() const override
Scan the sound card available for capture on the system.
void startStream(AudioDeviceType stream=AudioDeviceType::ALL) override
Start the capture stream and prepare the playback stream.
OpenSLLayer(const AudioPreference &pref)
Constructor.
std::vector< std::string > getPlaybackDeviceList() const override
Scan the sound card available for capture on the system.
~OpenSLLayer()
Destructor.
void stopStream(AudioDeviceType stream=AudioDeviceType::ALL) override
Stop the playback and capture streams.
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
void fillWithSilence(AVFrame *frame)
void emitSignal(Args... args)
Definition ring_signal.h:64
std::vector< sample_buf > allocateSampleBufs(unsigned count, size_t sizeInByte)
AudioDeviceType
Definition audiolayer.h:58
void dumpAvailableEngineInterfaces()
#define MAX_NUMBER_INPUT_DEVICES
Definition opensllayer.h:42
Structure to hold sample rate and channel number associated with audio data.
size_t getBytesPerFrame() const
Returns bytes necessary to hold one frame of audio data.
std::string toString() const
size_t size_