Ring Daemon 16.0.0
Loading...
Searching...
No Matches
pulselayer.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 "compiler_intrinsics.h"
19#include "audiostream.h"
20#include "pulselayer.h"
22#include "audio/ringbuffer.h"
23#include "libav_utils.h"
24#include "logger.h"
25#include "manager.h"
26
27#include <algorithm> // for std::find
28#include <stdexcept>
29
30#include <unistd.h>
31#include <cstdlib>
32#include <fstream>
33#include <cstring>
34
35#include <regex>
36
37// uncomment to log PulseAudio sink and sources
38//#define PA_LOG_SINK_SOURCES
39
40namespace jami {
41
42static const std::regex PA_EC_SUFFIX {"\\.echo-cancel(?:\\..+)?$"};
43
49
54
57 , playback_()
58 , record_()
59 , ringtone_()
61 , preference_(pref)
62{
63 JAMI_INFO("[audiolayer] Created PulseAudio layer");
64 if (!mainloop_)
65 throw std::runtime_error("Unable to create PulseAudio mainloop");
66
67 if (pa_threaded_mainloop_start(mainloop_.get()) < 0)
68 throw std::runtime_error("Failed to start PulseAudio mainloop");
69
70 setHasNativeNS(false);
71
72 PulseMainLoopLock lock(mainloop_.get());
73
74 std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl(pa_proplist_new(),
76 pa_proplist_sets(pl.get(), PA_PROP_MEDIA_ROLE, "phone");
77
80 pl.get());
81 if (!context_)
82 throw std::runtime_error("Unable to create PulseAudio context");
83
84 pa_context_set_state_callback(context_, context_state_callback, this);
85
86 if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0)
87 throw std::runtime_error("Unable to connect PulseAudio context to the server");
88
89 // wait until context is ready
90 for (;;) {
93 throw std::runtime_error("Pulse audio context is bad");
95 break;
96 pa_threaded_mainloop_wait(mainloop_.get());
97 }
98}
99
101{
102 if (streamStarter_.joinable())
103 streamStarter_.join();
104
105 disconnectAudioStream();
106
107 {
108 PulseMainLoopLock lock(mainloop_.get());
111 pa_context_disconnect(context_);
112 pa_context_unref(context_);
113 }
114
115 if (subscribeOp_)
116 pa_operation_unref(subscribeOp_);
117
118 playbackChanged(false);
119 recordChanged(false);
120}
121
122void
123PulseLayer::context_state_callback(pa_context* c, void* user_data)
124{
125 PulseLayer* pulse = static_cast<PulseLayer*>(user_data);
126 if (c and pulse)
127 pulse->contextStateChanged(c);
128}
129
130void
131PulseLayer::contextStateChanged(pa_context* c)
132{
135
136 switch (pa_context_get_state(c)) {
140 JAMI_DBG("Waiting…");
141 break;
142
143 case PA_CONTEXT_READY:
144 JAMI_DBG("Connection to PulseAudio server established");
145 pa_threaded_mainloop_signal(mainloop_.get(), 0);
146 subscribeOp_ = pa_context_subscribe(c, mask, nullptr, this);
147 pa_context_set_subscribe_callback(c, context_changed_callback, this);
151 waitForDeviceList();
152 break;
153
155 if (subscribeOp_) {
156 pa_operation_unref(subscribeOp_);
157 subscribeOp_ = nullptr;
158 }
159 break;
160
162 default:
164 pa_threaded_mainloop_signal(mainloop_.get(), 0);
165 break;
166 }
167}
168
169void
171{
172 std::unique_lock lk(readyMtx_);
173 if (not enumeratingSinks_) {
174 JAMI_DBG("Updating PulseAudio sink list");
175 enumeratingSinks_ = true;
176 sinkList_.clear();
177 sinkList_.emplace_back();
178 sinkList_.front().channel_map.channels = std::min(defaultAudioFormat_.nb_channels, 2u);
179 if (auto op = pa_context_get_sink_info_list(context_, sink_input_info_callback, this))
181 else
182 enumeratingSinks_ = false;
183 }
184}
185
186void
188{
189 std::unique_lock lk(readyMtx_);
190 if (not enumeratingSources_) {
191 JAMI_DBG("Updating PulseAudio source list");
192 enumeratingSources_ = true;
193 sourceList_.clear();
194 sourceList_.emplace_back();
195 sourceList_.front().channel_map.channels = std::min(defaultAudioFormat_.nb_channels, 2u);
196 if (auto op = pa_context_get_source_info_list(context_, source_input_info_callback, this))
198 else
199 enumeratingSources_ = false;
200 }
201}
202
203void
205{
206 std::unique_lock lk(readyMtx_);
207 if (not gettingServerInfo_) {
208 JAMI_DBG("Updating PulseAudio server info");
209 gettingServerInfo_ = true;
210 if (auto op = pa_context_get_server_info(context_, server_info_callback, this))
212 else
213 gettingServerInfo_ = false;
214 }
215}
216
217bool
218PulseLayer::inSinkList(const std::string& deviceName)
219{
220 return std::find_if(sinkList_.begin(),
221 sinkList_.end(),
223 != sinkList_.end();
224}
225
226bool
227PulseLayer::inSourceList(const std::string& deviceName)
228{
229 return std::find_if(sourceList_.begin(),
230 sourceList_.end(),
232 != sourceList_.end();
233}
234
235std::vector<std::string>
237{
238 std::vector<std::string> names;
239 names.reserve(sourceList_.size());
240 for (const auto& s : sourceList_)
241 names.emplace_back(s.description);
242 return names;
243}
244
245std::vector<std::string>
247{
248 std::vector<std::string> names;
249 names.reserve(sinkList_.size());
250 for (const auto& s : sinkList_)
251 names.emplace_back(s.description);
252 return names;
253}
254
255int
257{
258 switch (type) {
261 return std::distance(sinkList_.begin(),
262 std::find_if(sinkList_.begin(),
263 sinkList_.end(),
266 return std::distance(sourceList_.begin(),
267 std::find_if(sourceList_.begin(),
268 sourceList_.end(),
270 default:
271 JAMI_ERR("Unexpected device type");
272 return 0;
273 }
274}
275
276int
277PulseLayer::getAudioDeviceIndexByName(const std::string& name, AudioDeviceType type) const
278{
279 if (name.empty())
280 return 0;
281 switch (type) {
284 return std::distance(sinkList_.begin(),
285 std::find_if(sinkList_.begin(),
286 sinkList_.end(),
289 return std::distance(sourceList_.begin(),
290 std::find_if(sourceList_.begin(),
291 sourceList_.end(),
293 default:
294 JAMI_ERR("Unexpected device type");
295 return 0;
296 }
297}
298
299bool
300endsWith(const std::string& str, const std::string& ending)
301{
302 if (ending.size() >= str.size())
303 return false;
304 return std::equal(ending.rbegin(), ending.rend(), str.rbegin());
305}
306
310const PaDeviceInfos*
311findBest(const std::vector<PaDeviceInfos>& list)
312{
313 if (list.empty())
314 return nullptr;
315 for (const auto& info : list)
316 if (info.monitor_of == PA_INVALID_INDEX)
317 return &info;
318 return &list[0];
319}
320
321const PaDeviceInfos*
322PulseLayer::getDeviceInfos(const std::vector<PaDeviceInfos>& list, const std::string& name) const
323{
324 auto dev_info = std::find_if(list.begin(), list.end(), PaDeviceInfos::NameComparator(name));
325 if (dev_info == list.end()) {
326 JAMI_WARN("Preferred device %s not found in device list, selecting default %s instead.",
327 name.c_str(),
328 list.front().name.c_str());
329 return &list.front();
330 }
331 return &(*dev_info);
332}
333
334std::string
336{
337 switch (type) {
340 if (index < 0 or static_cast<size_t>(index) >= sinkList_.size()) {
341 JAMI_ERR("Index %d out of range", index);
342 return "";
343 }
344 return sinkList_[index].name;
345
347 if (index < 0 or static_cast<size_t>(index) >= sourceList_.size()) {
348 JAMI_ERR("Index %d out of range", index);
349 return "";
350 }
351 return sourceList_[index].name;
352
353 default:
354 // Should never happen
355 JAMI_ERR("Unexpected type");
356 return "";
357 }
358}
359
360void
361PulseLayer::onStreamReady()
362{
363 if (--pendingStreams == 0) {
364 JAMI_DBG("All streams ready, starting audio");
365 // Flush outside the if statement: every time start stream is
366 // called is to notify a new event
367 flushUrgent();
368 flushMain();
369 if (playback_) {
370 playback_->start();
371 playbackChanged(true);
372 }
373 if (ringtone_) {
374 ringtone_->start();
375 }
376 if (record_) {
377 record_->start();
378 recordChanged(true);
379 }
380 }
381}
382
383void
384PulseLayer::createStream(std::unique_ptr<AudioStream>& stream,
385 AudioDeviceType type,
386 const PaDeviceInfos& dev_infos,
387 bool ec,
388 std::function<void(size_t)>&& onData)
389{
390 if (stream) {
391 JAMI_WARN("Stream already exists");
392 return;
393 }
394 pendingStreams++;
395 const char* name = type == AudioDeviceType::PLAYBACK
396 ? "Playback"
397 : (type == AudioDeviceType::CAPTURE
398 ? "Record"
399 : (type == AudioDeviceType::RINGTONE ? "Ringtone" : "?"));
400 stream.reset(new AudioStream(context_,
401 mainloop_.get(),
402 name,
403 type,
406 dev_infos,
407 ec,
408 std::bind(&PulseLayer::onStreamReady, this),
409 std::move(onData)));
410}
411
412void
413PulseLayer::disconnectAudioStream()
414{
415 PulseMainLoopLock lock(mainloop_.get());
416 playback_.reset();
417 ringtone_.reset();
418 record_.reset();
419 playbackChanged(false);
420 recordChanged(false);
421 pendingStreams = 0;
423 startedCv_.notify_all();
424}
425
426void
428{
429 waitForDevices();
430 PulseMainLoopLock lock(mainloop_.get());
431 bool ec = preference_.getEchoCanceller() == "system"
432 || preference_.getEchoCanceller() == "auto";
433
434 // Create Streams
435 if (type == AudioDeviceType::PLAYBACK) {
436 if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
437 createStream(playback_,
438 type,
439 *dev_infos,
440 ec,
441 std::bind(&PulseLayer::writeToSpeaker, this));
442 }
443 } else if (type == AudioDeviceType::RINGTONE) {
444 if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice()))
445 createStream(ringtone_,
446 type,
447 *dev_infos,
448 false,
449 std::bind(&PulseLayer::ringtoneToSpeaker, this));
450 } else if (type == AudioDeviceType::CAPTURE) {
451 if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
452 createStream(record_, type, *dev_infos, ec, std::bind(&PulseLayer::readFromMic, this));
453
454 // whenever the stream is moved, it will call this cb
455 record_->setEchoCancelCb([this](bool echoCancel) { setHasNativeAEC(echoCancel); });
456 }
457 }
458 pa_threaded_mainloop_signal(mainloop_.get(), 0);
459
460 std::lock_guard lk(mutex_);
462 startedCv_.notify_all();
463}
464
465void
467{
468 waitForDevices();
469 PulseMainLoopLock lock(mainloop_.get());
470 auto& stream(getStream(type));
471 if (not stream)
472 return;
473
474 if (not stream->isReady())
475 pendingStreams--;
476 stream->stop();
477 stream.reset();
478
479 if (type == AudioDeviceType::PLAYBACK || type == AudioDeviceType::ALL)
480 playbackChanged(false);
481
482 std::lock_guard lk(mutex_);
483 if (not playback_ and not ringtone_ and not record_) {
484 pendingStreams = 0;
486 startedCv_.notify_all();
487 }
488}
489
490void
492{
493 if (!playback_ or !playback_->isReady())
494 return;
495
496 // available bytes to be written in PulseAudio internal buffer
497 void* data = nullptr;
498 size_t writableBytes = (size_t) -1;
499 int ret = pa_stream_begin_write(playback_->stream(), &data, &writableBytes);
500 if (ret == 0 and data and writableBytes != 0) {
501 writableBytes = std::min(pa_stream_writable_size(playback_->stream()), writableBytes);
502 const auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
504 memset(data, 0, writableBytes);
505 else
506 std::memcpy(data,
507 buff->pointer()->data[0],
508 buff->pointer()->nb_samples * playback_->frameSize());
509 pa_stream_write(playback_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
510 }
511}
512
513void
515{
516 if (!record_ or !record_->isReady())
517 return;
518
519 const char* data = nullptr;
520 size_t bytes;
521 if (pa_stream_peek(record_->stream(), (const void**) &data, &bytes) < 0 or !data)
522 return;
523
524 if (bytes == 0)
525 return;
526
527 size_t sample_size = record_->frameSize();
528 const size_t samples = bytes / sample_size;
529
530 auto out = std::make_shared<AudioFrame>(record_->format(), samples);
531 if (isCaptureMuted_)
533 else
534 std::memcpy(out->pointer()->data[0], data, bytes);
535
536 if (pa_stream_drop(record_->stream()) < 0)
537 JAMI_ERR("Capture stream drop failed: %s", pa_strerror(pa_context_errno(context_)));
538
539 putRecorded(std::move(out));
540}
541
542void
544{
545 if (!ringtone_ or !ringtone_->isReady())
546 return;
547
548 void* data = nullptr;
549 size_t writableBytes = (size_t) -1;
550 int ret = pa_stream_begin_write(ringtone_->stream(), &data, &writableBytes);
551 if (ret == 0 and data and writableBytes != 0) {
552 writableBytes = std::min(pa_stream_writable_size(ringtone_->stream()), writableBytes);
553 const auto& buff = getToRing(ringtone_->format(), writableBytes / ringtone_->frameSize());
555 memset(data, 0, writableBytes);
556 else
557 std::memcpy(data,
558 buff->pointer()->data[0],
559 buff->pointer()->nb_samples * ringtone_->frameSize());
560 pa_stream_write(ringtone_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
561 }
562}
563
564std::string
565stripEchoSufix(const std::string& deviceName)
566{
567 return std::regex_replace(deviceName, PA_EC_SUFFIX, "");
568}
569
570void
571PulseLayer::context_changed_callback(pa_context* c,
573 uint32_t idx,
574 void* userdata)
575{
576 static_cast<PulseLayer*>(userdata)->contextChanged(c, type, idx);
577}
578
579void
580PulseLayer::contextChanged(pa_context* c UNUSED,
582 uint32_t idx UNUSED)
583{
584 bool reset = false;
585
588 switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
592 reset = true;
593 default:
594 break;
595 }
596
597 break;
598
600 switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
604 reset = true;
605 default:
606 break;
607 }
608
609 break;
610
611 default:
612 JAMI_DBG("Unhandled event type 0x%x", type);
613 break;
614 }
615
616 if (reset) {
618 waitForDeviceList();
619 }
620}
621
622void
623PulseLayer::waitForDevices()
624{
625 std::unique_lock lk(readyMtx_);
626 readyCv_.wait(lk, [this] {
627 return !(enumeratingSinks_ or enumeratingSources_ or gettingServerInfo_);
628 });
629}
630
631void
632PulseLayer::waitForDeviceList()
633{
634 std::unique_lock lock(readyMtx_);
635 if (waitingDeviceList_.exchange(true))
636 return;
637 if (streamStarter_.joinable())
638 streamStarter_.join();
639 streamStarter_ = std::thread([this]() mutable {
641
642 waitForDevices();
643 waitingDeviceList_ = false;
644
645 // If a current device changed, restart streams
647 auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
648 playbackDeviceChanged = playback_
649 and (!playbackInfo->name.empty()
650 and playbackInfo->name
651 != stripEchoSufix(playback_->getDeviceName()));
652
653 auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
654 recordDeviceChanged = record_
655 and (!recordInfo->name.empty()
656 and recordInfo->name != stripEchoSufix(record_->getDeviceName()));
657
659 return;
661 JAMI_WARN("Playback devices changed, restarting streams.");
664 }
666 JAMI_WARN("Record devices changed, restarting streams.");
669 }
670 });
671}
672
673void
674PulseLayer::server_info_callback(pa_context*, const pa_server_info* i, void* userdata)
675{
676 if (!i)
677 return;
679 JAMI_DBG("PulseAudio server info:"
680 "\n Server name: %s"
681 "\n Server version: %s"
682 "\n Default sink: %s"
683 "\n Default source: %s"
684 "\n Default sample specification: %s"
685 "\n Default channel map: %s",
686 i->server_name,
687 i->server_version,
688 i->default_sink_name,
689 i->default_source_name,
690 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
691 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
692
693 PulseLayer* context = static_cast<PulseLayer*>(userdata);
694 std::lock_guard lk(context->readyMtx_);
695 context->defaultSink_ = {};
696 context->defaultSource_ = {};
697 context->defaultAudioFormat_ = {
698 i->sample_spec.rate,
699 i->sample_spec.channels,
700 sampleFormatFromPulse(i->sample_spec.format)
701 };
702 {
703 std::lock_guard lk(context->mutex_);
704 context->hardwareFormatAvailable(context->defaultAudioFormat_);
705 }
706 /*if (not context->sinkList_.empty())
707 context->sinkList_.front().channel_map.channels = std::min(i->sample_spec.channels,
708 (uint8_t) 2);
709 if (not context->sourceList_.empty())
710 context->sourceList_.front().channel_map.channels = std::min(i->sample_spec.channels,
711 (uint8_t) 2);*/
712 context->gettingServerInfo_ = false;
713 context->readyCv_.notify_all();
714}
715
716void
717PulseLayer::source_input_info_callback(pa_context* c UNUSED,
718 const pa_source_info* i,
719 int eol,
720 void* userdata)
721{
722 PulseLayer* context = static_cast<PulseLayer*>(userdata);
723
724 if (eol) {
725 std::lock_guard lk(context->readyMtx_);
726 context->enumeratingSources_ = false;
727 context->readyCv_.notify_all();
728 return;
729 }
730#ifdef PA_LOG_SINK_SOURCES
732 JAMI_DBG("Source %u\n"
733 " Name: %s\n"
734 " Driver: %s\n"
735 " Description: %s\n"
736 " Sample Specification: %s\n"
737 " Channel Map: %s\n"
738 " Owner Module: %u\n"
739 " Volume: %s\n"
740 " Monitor if Sink: %u\n"
741 " Latency: %0.0f usec\n"
742 " Flags: %s%s%s\n",
743 i->index,
744 i->name,
745 i->driver,
746 i->description,
747 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
748 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
749 i->owner_module,
750 i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
752 (double) i->latency,
753 i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
754 i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
755 i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : "");
756#endif
757 if (not context->inSourceList(i->name)) {
758 context->sourceList_.emplace_back(*i);
759 }
760}
761
762void
763PulseLayer::sink_input_info_callback(pa_context* c UNUSED,
764 const pa_sink_info* i,
765 int eol,
766 void* userdata)
767{
768 PulseLayer* context = static_cast<PulseLayer*>(userdata);
769 std::lock_guard lk(context->readyMtx_);
770
771 if (eol) {
772 context->enumeratingSinks_ = false;
773 context->readyCv_.notify_all();
774 return;
775 }
776#ifdef PA_LOG_SINK_SOURCES
778 JAMI_DBG("Sink %u\n"
779 " Name: %s\n"
780 " Driver: %s\n"
781 " Description: %s\n"
782 " Sample Specification: %s\n"
783 " Channel Map: %s\n"
784 " Owner Module: %u\n"
785 " Volume: %s\n"
786 " Monitor Source: %u\n"
787 " Latency: %0.0f usec\n"
788 " Flags: %s%s%s\n",
789 i->index,
790 i->name,
791 i->driver,
792 i->description,
793 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
794 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
795 i->owner_module,
796 i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
798 static_cast<double>(i->latency),
799 i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
800 i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
801 i->flags & PA_SINK_HARDWARE ? "HARDWARE" : "");
802#endif
803 if (not context->inSinkList(i->name)) {
804 context->sinkList_.emplace_back(*i);
805 }
806}
807
808void
809PulseLayer::updatePreference(AudioPreference& preference, int index, AudioDeviceType type)
810{
811 const std::string devName(getAudioDeviceName(index, type));
812
813 switch (type) {
815 JAMI_DBG("setting %s for playback", devName.c_str());
816 preference.setPulseDevicePlayback(devName);
817 break;
818
820 JAMI_DBG("setting %s for capture", devName.c_str());
821 preference.setPulseDeviceRecord(devName);
822 break;
823
825 JAMI_DBG("setting %s for ringer", devName.c_str());
826 preference.setPulseDeviceRingtone(devName);
827 break;
828
829 default:
830 break;
831 }
832}
833
834int
835PulseLayer::getIndexCapture() const
836{
838}
839
840int
841PulseLayer::getIndexPlayback() const
842{
845}
846
847int
848PulseLayer::getIndexRingtone() const
849{
852}
853
854std::string
855PulseLayer::getPreferredPlaybackDevice() const
856{
857 const std::string& device(preference_.getPulseDevicePlayback());
858 return stripEchoSufix(device.empty() ? defaultSink_ : device);
859}
860
861std::string
862PulseLayer::getPreferredRingtoneDevice() const
863{
864 const std::string& device(preference_.getPulseDeviceRingtone());
865 return stripEchoSufix(device.empty() ? defaultSink_ : device);
866}
867
868std::string
869PulseLayer::getPreferredCaptureDevice() const
870{
871 const std::string& device(preference_.getPulseDeviceRecord());
872 return stripEchoSufix(device.empty() ? defaultSource_ : device);
873}
874
875} // namespace jami
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)
bool isPlaybackMuted_
True if playback is not to be used.
Definition audiolayer.h:226
std::shared_ptr< AudioFrame > getToRing(AudioFormat format, size_t writableSamples)
void flushUrgent()
Flush urgent buffer.
void setHasNativeAEC(bool hasEAC)
std::shared_ptr< AudioFrame > getToPlay(AudioFormat format, size_t writableSamples)
bool isRingtoneMuted_
True if ringtone should be muted.
Definition audiolayer.h:231
void recordChanged(bool started)
AudioFormat audioFormat_
Sample Rate that should be sent to the sound card.
Definition audiolayer.h:266
void flushMain()
Flush main buffer.
std::condition_variable startedCv_
Definition audiolayer.h:261
void putRecorded(std::shared_ptr< AudioFrame > &&frame)
const std::string & getPulseDeviceRecord() const
const std::string & getPulseDeviceRingtone() const
const std::string & getEchoCanceller() const
const std::string & getPulseDevicePlayback() const
Unary function to search for a device by name in a list using std functions.
Definition pulselayer.h:72
virtual void stopStream(AudioDeviceType stream=AudioDeviceType::ALL)
Stop the playback and capture streams.
std::string getAudioDeviceName(int index, AudioDeviceType type) const
virtual std::vector< std::string > getPlaybackDeviceList() const
PulseLayer(AudioPreference &pref)
virtual std::vector< std::string > getCaptureDeviceList() const
bool inSourceList(const std::string &deviceName)
virtual void startStream(AudioDeviceType stream=AudioDeviceType::ALL)
Start the capture stream and prepare the playback stream.
void readFromMic()
Write data from the ring buffer to the hardware and read data from the hardware.
int getAudioDeviceIndex(const std::string &descr, AudioDeviceType type) const
int getAudioDeviceIndexByName(const std::string &name, AudioDeviceType type) const
bool inSinkList(const std::string &deviceName)
PulseMainLoopLock(pa_threaded_mainloop *loop)
#define UNUSED
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARN(...)
Definition logger.h:217
#define JAMI_INFO(...)
Definition logger.h:215
void fillWithSilence(AVFrame *frame)
AVSampleFormat sampleFormatFromPulse(pa_sample_format_t format)
Definition audiostream.h:28
const PaDeviceInfos * findBest(const std::vector< PaDeviceInfos > &list)
Find default device for PulseAudio to open, filter monitors and EC.
std::string stripEchoSufix(const std::string &deviceName)
void emitSignal(Args... args)
Definition ring_signal.h:64
static const std::regex PA_EC_SUFFIX
pa_sample_format_t pulseSampleFormatFromAv(AVSampleFormat format)
Definition audiostream.h:46
bool endsWith(const std::string &str, const std::string &ending)
AudioDeviceType
Definition audiolayer.h:58
AVSampleFormat sampleFormat