63 JAMI_INFO(
"[audiolayer] Created PulseAudio layer");
65 throw std::runtime_error(
"Unable to create PulseAudio mainloop");
68 throw std::runtime_error(
"Failed to start PulseAudio mainloop");
82 throw std::runtime_error(
"Unable to create PulseAudio context");
87 throw std::runtime_error(
"Unable to connect PulseAudio context to the server");
93 throw std::runtime_error(
"Pulse audio context is bad");
102 if (streamStarter_.joinable())
103 streamStarter_.join();
105 disconnectAudioStream();
127 pulse->contextStateChanged(c);
144 JAMI_DBG(
"Connection to PulseAudio server established");
157 subscribeOp_ =
nullptr;
172 std::unique_lock
lk(readyMtx_);
173 if (
not enumeratingSinks_) {
174 JAMI_DBG(
"Updating PulseAudio sink list");
175 enumeratingSinks_ =
true;
177 sinkList_.emplace_back();
178 sinkList_.front().channel_map.channels = std::min(defaultAudioFormat_.
nb_channels, 2u);
182 enumeratingSinks_ =
false;
189 std::unique_lock
lk(readyMtx_);
190 if (
not enumeratingSources_) {
191 JAMI_DBG(
"Updating PulseAudio source list");
192 enumeratingSources_ =
true;
194 sourceList_.emplace_back();
195 sourceList_.front().channel_map.channels = std::min(defaultAudioFormat_.
nb_channels, 2u);
199 enumeratingSources_ =
false;
206 std::unique_lock
lk(readyMtx_);
207 if (
not gettingServerInfo_) {
208 JAMI_DBG(
"Updating PulseAudio server info");
209 gettingServerInfo_ =
true;
213 gettingServerInfo_ =
false;
220 return std::find_if(sinkList_.begin(),
229 return std::find_if(sourceList_.begin(),
232 != sourceList_.end();
235std::vector<std::string>
238 std::vector<std::string>
names;
239 names.reserve(sourceList_.size());
240 for (
const auto& s : sourceList_)
241 names.emplace_back(s.description);
245std::vector<std::string>
248 std::vector<std::string>
names;
249 names.reserve(sinkList_.size());
250 for (
const auto& s : sinkList_)
251 names.emplace_back(s.description);
261 return std::distance(sinkList_.begin(),
262 std::find_if(sinkList_.begin(),
266 return std::distance(sourceList_.begin(),
267 std::find_if(sourceList_.begin(),
284 return std::distance(sinkList_.begin(),
285 std::find_if(sinkList_.begin(),
289 return std::distance(sourceList_.begin(),
290 std::find_if(sourceList_.begin(),
315 for (
const auto& info :
list)
322PulseLayer::getDeviceInfos(
const std::vector<PaDeviceInfos>&
list,
const std::string& name)
const
324 auto dev_info = std::find_if(
list.begin(),
list.end(), PaDeviceInfos::NameComparator(name));
326 JAMI_WARN(
"Preferred device %s not found in device list, selecting default %s instead.",
328 list.front().name.c_str());
329 return &
list.front();
340 if (index < 0
or static_cast<size_t>(index) >= sinkList_.size()) {
341 JAMI_ERR(
"Index %d out of range", index);
344 return sinkList_[index].name;
347 if (index < 0
or static_cast<size_t>(index) >= sourceList_.size()) {
348 JAMI_ERR(
"Index %d out of range", index);
351 return sourceList_[index].name;
361PulseLayer::onStreamReady()
363 if (--pendingStreams == 0) {
364 JAMI_DBG(
"All streams ready, starting audio");
384PulseLayer::createStream(std::unique_ptr<AudioStream>& stream,
388 std::function<
void(
size_t)>&&
onData)
400 stream.reset(
new AudioStream(context_,
408 std::bind(&PulseLayer::onStreamReady,
this),
413PulseLayer::disconnectAudioStream()
415 PulseMainLoopLock lock(mainloop_.get());
436 if (
auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
437 createStream(playback_,
444 if (
auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice()))
445 createStream(ringtone_,
451 if (
auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
470 auto& stream(getStream(type));
474 if (
not stream->isReady())
493 if (!playback_
or !playback_->isReady())
497 void* data =
nullptr;
507 buff->pointer()->data[0],
508 buff->pointer()->nb_samples * playback_->frameSize());
516 if (!record_
or !record_->isReady())
519 const char* data =
nullptr;
521 if (
pa_stream_peek(record_->stream(), (
const void**) &data, &bytes) < 0
or !data)
530 auto out = std::make_shared<AudioFrame>(record_->format(), samples);
534 std::memcpy(
out->pointer()->data[0], data, bytes);
545 if (!ringtone_
or !ringtone_->isReady())
548 void* data =
nullptr;
558 buff->pointer()->data[0],
559 buff->pointer()->nb_samples * ringtone_->frameSize());
567 return std::regex_replace(deviceName,
PA_EC_SUFFIX,
"");
571PulseLayer::context_changed_callback(
pa_context* c,
576 static_cast<PulseLayer*
>(
userdata)->contextChanged(c, type, idx);
612 JAMI_DBG(
"Unhandled event type 0x%x", type);
623PulseLayer::waitForDevices()
625 std::unique_lock
lk(readyMtx_);
626 readyCv_.wait(
lk, [
this] {
627 return !(enumeratingSinks_
or enumeratingSources_
or gettingServerInfo_);
632PulseLayer::waitForDeviceList()
634 std::unique_lock lock(readyMtx_);
635 if (waitingDeviceList_.exchange(
true))
637 if (streamStarter_.joinable())
638 streamStarter_.join();
639 streamStarter_ = std::thread([
this]()
mutable {
643 waitingDeviceList_ =
false;
647 auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
653 auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
661 JAMI_WARN(
"Playback devices changed, restarting streams.");
666 JAMI_WARN(
"Record devices changed, restarting streams.");
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",
688 i->default_sink_name,
689 i->default_source_name,
694 std::lock_guard
lk(context->readyMtx_);
695 context->defaultSink_ = {};
696 context->defaultSource_ = {};
697 context->defaultAudioFormat_ = {
699 i->sample_spec.channels,
703 std::lock_guard
lk(context->mutex_);
704 context->hardwareFormatAvailable(context->defaultAudioFormat_);
712 context->gettingServerInfo_ =
false;
713 context->readyCv_.notify_all();
725 std::lock_guard
lk(context->readyMtx_);
726 context->enumeratingSources_ =
false;
727 context->readyCv_.notify_all();
730#ifdef PA_LOG_SINK_SOURCES
736 " Sample Specification: %s\n"
738 " Owner Module: %u\n"
740 " Monitor if Sink: %u\n"
741 " Latency: %0.0f usec\n"
757 if (
not context->inSourceList(
i->name)) {
758 context->sourceList_.emplace_back(*
i);
769 std::lock_guard
lk(context->readyMtx_);
772 context->enumeratingSinks_ =
false;
773 context->readyCv_.notify_all();
776#ifdef PA_LOG_SINK_SOURCES
782 " Sample Specification: %s\n"
784 " Owner Module: %u\n"
786 " Monitor Source: %u\n"
787 " Latency: %0.0f usec\n"
803 if (
not context->inSinkList(
i->name)) {
804 context->sinkList_.emplace_back(*
i);
835PulseLayer::getIndexCapture()
const
841PulseLayer::getIndexPlayback()
const
848PulseLayer::getIndexRingtone()
const
855PulseLayer::getPreferredPlaybackDevice()
const
862PulseLayer::getPreferredRingtoneDevice()
const
869PulseLayer::getPreferredCaptureDevice()
const
bool isCaptureMuted_
True if capture is not to be used.
std::mutex mutex_
Lock for the entire audio layer.
std::atomic< Status > status_
Whether or not the audio layer's playback stream is started.
void playbackChanged(bool started)
void setHasNativeNS(bool hasNS)
bool isPlaybackMuted_
True if playback is not to be used.
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.
void recordChanged(bool started)
AudioFormat audioFormat_
Sample Rate that should be sent to the sound card.
void flushMain()
Flush main buffer.
std::condition_variable startedCv_
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.
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)
void fillWithSilence(AVFrame *frame)
AVSampleFormat sampleFormatFromPulse(pa_sample_format_t format)
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)
static const std::regex PA_EC_SUFFIX
pa_sample_format_t pulseSampleFormatFromAv(AVSampleFormat format)
bool endsWith(const std::string &str, const std::string &ending)