LCOV - code coverage report
Current view: top level - src/media/audio/pulseaudio - pulselayer.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 43 461 9.3 %
Date: 2024-04-19 19:18:04 Functions: 5 46 10.9 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
       5             :  *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
       6             :  *  Author: Андрей Лухнов <aol.nnov@gmail.com>
       7             :  *  Author: Adrien Beraud <adrien.beraud@gmail.com>
       8             :  *
       9             :  *  This program is free software; you can redistribute it and/or modify
      10             :  *  it under the terms of the GNU General Public License as published by
      11             :  *  the Free Software Foundation; either version 3 of the License, or
      12             :  *  (at your option) any later version.
      13             :  *
      14             :  *  This program is distributed in the hope that it will be useful,
      15             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :  *  GNU General Public License for more details.
      18             :  *
      19             :  *  You should have received a copy of the GNU General Public License
      20             :  *  along with this program; if not, write to the Free Software
      21             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      22             :  */
      23             : 
      24             : #include "compiler_intrinsics.h"
      25             : #include "audiostream.h"
      26             : #include "pulselayer.h"
      27             : #include "audio/ringbufferpool.h"
      28             : #include "audio/ringbuffer.h"
      29             : #include "libav_utils.h"
      30             : #include "logger.h"
      31             : #include "manager.h"
      32             : 
      33             : #include <algorithm> // for std::find
      34             : #include <stdexcept>
      35             : 
      36             : #include <unistd.h>
      37             : #include <cstdlib>
      38             : #include <fstream>
      39             : #include <cstring>
      40             : 
      41             : #include <regex>
      42             : 
      43             : // uncomment to log pulseaudio sink and sources
      44             : //#define PA_LOG_SINK_SOURCES
      45             : 
      46             : namespace jami {
      47             : 
      48             : static const std::regex PA_EC_SUFFIX {"\\.echo-cancel(?:\\..+)?$"};
      49             : 
      50           3 : PulseMainLoopLock::PulseMainLoopLock(pa_threaded_mainloop* loop)
      51           3 :     : loop_(loop)
      52             : {
      53           3 :     pa_threaded_mainloop_lock(loop_);
      54           3 : }
      55             : 
      56           3 : PulseMainLoopLock::~PulseMainLoopLock()
      57             : {
      58           3 :     pa_threaded_mainloop_unlock(loop_);
      59           3 : }
      60             : 
      61           3 : PulseLayer::PulseLayer(AudioPreference& pref)
      62             :     : AudioLayer(pref)
      63           3 :     , playback_()
      64           3 :     , record_()
      65           3 :     , ringtone_()
      66           3 :     , mainloop_(pa_threaded_mainloop_new(), pa_threaded_mainloop_free)
      67           9 :     , preference_(pref)
      68             : {
      69           3 :     JAMI_INFO("[audiolayer] created pulseaudio layer");
      70           3 :     if (!mainloop_)
      71           0 :         throw std::runtime_error("Couldn't create pulseaudio mainloop");
      72             : 
      73           3 :     if (pa_threaded_mainloop_start(mainloop_.get()) < 0)
      74           0 :         throw std::runtime_error("Failed to start pulseaudio mainloop");
      75             : 
      76           3 :     setHasNativeNS(false);
      77             : 
      78           3 :     PulseMainLoopLock lock(mainloop_.get());
      79             : 
      80             :     std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl(pa_proplist_new(),
      81           3 :                                                                  pa_proplist_free);
      82           3 :     pa_proplist_sets(pl.get(), PA_PROP_MEDIA_ROLE, "phone");
      83             : 
      84           3 :     context_ = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop_.get()),
      85             :                                             PACKAGE_NAME,
      86           3 :                                             pl.get());
      87           3 :     if (!context_)
      88           0 :         throw std::runtime_error("Couldn't create pulseaudio context");
      89             : 
      90           3 :     pa_context_set_state_callback(context_, context_state_callback, this);
      91             : 
      92           3 :     if (pa_context_connect(context_, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0)
      93           3 :         throw std::runtime_error("Could not connect pulseaudio context to the server");
      94             : 
      95             :     // wait until context is ready
      96             :     for (;;) {
      97           0 :         pa_context_state_t context_state = pa_context_get_state(context_);
      98           0 :         if (not PA_CONTEXT_IS_GOOD(context_state))
      99           0 :             throw std::runtime_error("Pulse audio context is bad");
     100           0 :         if (context_state == PA_CONTEXT_READY)
     101           0 :             break;
     102           0 :         pa_threaded_mainloop_wait(mainloop_.get());
     103           0 :     }
     104          39 : }
     105             : 
     106           0 : PulseLayer::~PulseLayer()
     107             : {
     108           0 :     if (streamStarter_.joinable())
     109           0 :         streamStarter_.join();
     110             : 
     111           0 :     disconnectAudioStream();
     112             : 
     113             :     {
     114           0 :         PulseMainLoopLock lock(mainloop_.get());
     115           0 :         pa_context_set_state_callback(context_, NULL, NULL);
     116           0 :         pa_context_set_subscribe_callback(context_, NULL, NULL);
     117           0 :         pa_context_disconnect(context_);
     118           0 :         pa_context_unref(context_);
     119           0 :     }
     120             : 
     121           0 :     if (subscribeOp_)
     122           0 :         pa_operation_unref(subscribeOp_);
     123             : 
     124           0 :     playbackChanged(false);
     125           0 :     recordChanged(false);
     126           0 : }
     127             : 
     128             : void
     129           6 : PulseLayer::context_state_callback(pa_context* c, void* user_data)
     130             : {
     131           6 :     PulseLayer* pulse = static_cast<PulseLayer*>(user_data);
     132           6 :     if (c and pulse)
     133           6 :         pulse->contextStateChanged(c);
     134           6 : }
     135             : 
     136             : void
     137           6 : PulseLayer::contextStateChanged(pa_context* c)
     138             : {
     139           6 :     const pa_subscription_mask_t mask = (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK
     140             :                                                                   | PA_SUBSCRIPTION_MASK_SOURCE);
     141             : 
     142           6 :     switch (pa_context_get_state(c)) {
     143           3 :     case PA_CONTEXT_CONNECTING:
     144             :     case PA_CONTEXT_AUTHORIZING:
     145             :     case PA_CONTEXT_SETTING_NAME:
     146           3 :         JAMI_DBG("Waiting....");
     147           3 :         break;
     148             : 
     149           0 :     case PA_CONTEXT_READY:
     150           0 :         JAMI_DBG("Connection to PulseAudio server established");
     151           0 :         pa_threaded_mainloop_signal(mainloop_.get(), 0);
     152           0 :         subscribeOp_ = pa_context_subscribe(c, mask, nullptr, this);
     153           0 :         pa_context_set_subscribe_callback(c, context_changed_callback, this);
     154           0 :         updateSinkList();
     155           0 :         updateSourceList();
     156           0 :         updateServerInfo();
     157           0 :         waitForDeviceList();
     158           0 :         break;
     159             : 
     160           0 :     case PA_CONTEXT_TERMINATED:
     161           0 :         if (subscribeOp_) {
     162           0 :             pa_operation_unref(subscribeOp_);
     163           0 :             subscribeOp_ = nullptr;
     164             :         }
     165           0 :         break;
     166             : 
     167           3 :     case PA_CONTEXT_FAILED:
     168             :     default:
     169           3 :         JAMI_ERR("%s", pa_strerror(pa_context_errno(c)));
     170           3 :         pa_threaded_mainloop_signal(mainloop_.get(), 0);
     171           3 :         break;
     172             :     }
     173           6 : }
     174             : 
     175             : void
     176           0 : PulseLayer::updateSinkList()
     177             : {
     178           0 :     std::unique_lock lk(readyMtx_);
     179           0 :     if (not enumeratingSinks_) {
     180           0 :         JAMI_DBG("Updating PulseAudio sink list");
     181           0 :         enumeratingSinks_ = true;
     182           0 :         sinkList_.clear();
     183           0 :         sinkList_.emplace_back();
     184           0 :         sinkList_.front().channel_map.channels = std::min(defaultAudioFormat_.nb_channels, 2u);
     185           0 :         if (auto op = pa_context_get_sink_info_list(context_, sink_input_info_callback, this))
     186           0 :             pa_operation_unref(op);
     187             :         else
     188           0 :             enumeratingSinks_ = false;
     189             :     }
     190           0 : }
     191             : 
     192             : void
     193           0 : PulseLayer::updateSourceList()
     194             : {
     195           0 :     std::unique_lock lk(readyMtx_);
     196           0 :     if (not enumeratingSources_) {
     197           0 :         JAMI_DBG("Updating PulseAudio source list");
     198           0 :         enumeratingSources_ = true;
     199           0 :         sourceList_.clear();
     200           0 :         sourceList_.emplace_back();
     201           0 :         sourceList_.front().channel_map.channels = std::min(defaultAudioFormat_.nb_channels, 2u);
     202           0 :         if (auto op = pa_context_get_source_info_list(context_, source_input_info_callback, this))
     203           0 :             pa_operation_unref(op);
     204             :         else
     205           0 :             enumeratingSources_ = false;
     206             :     }
     207           0 : }
     208             : 
     209             : void
     210           0 : PulseLayer::updateServerInfo()
     211             : {
     212           0 :     std::unique_lock lk(readyMtx_);
     213           0 :     if (not gettingServerInfo_) {
     214           0 :         JAMI_DBG("Updating PulseAudio server infos");
     215           0 :         gettingServerInfo_ = true;
     216           0 :         if (auto op = pa_context_get_server_info(context_, server_info_callback, this))
     217           0 :             pa_operation_unref(op);
     218             :         else
     219           0 :             gettingServerInfo_ = false;
     220             :     }
     221           0 : }
     222             : 
     223             : bool
     224           0 : PulseLayer::inSinkList(const std::string& deviceName)
     225             : {
     226           0 :     return std::find_if(sinkList_.begin(),
     227             :                         sinkList_.end(),
     228             :                         PaDeviceInfos::NameComparator(deviceName))
     229           0 :            != sinkList_.end();
     230             : }
     231             : 
     232             : bool
     233           0 : PulseLayer::inSourceList(const std::string& deviceName)
     234             : {
     235           0 :     return std::find_if(sourceList_.begin(),
     236             :                         sourceList_.end(),
     237             :                         PaDeviceInfos::NameComparator(deviceName))
     238           0 :            != sourceList_.end();
     239             : }
     240             : 
     241             : std::vector<std::string>
     242           0 : PulseLayer::getCaptureDeviceList() const
     243             : {
     244           0 :     std::vector<std::string> names;
     245           0 :     names.reserve(sourceList_.size());
     246           0 :     for (const auto& s : sourceList_)
     247           0 :         names.emplace_back(s.description);
     248           0 :     return names;
     249           0 : }
     250             : 
     251             : std::vector<std::string>
     252           0 : PulseLayer::getPlaybackDeviceList() const
     253             : {
     254           0 :     std::vector<std::string> names;
     255           0 :     names.reserve(sinkList_.size());
     256           0 :     for (const auto& s : sinkList_)
     257           0 :         names.emplace_back(s.description);
     258           0 :     return names;
     259           0 : }
     260             : 
     261             : int
     262           0 : PulseLayer::getAudioDeviceIndex(const std::string& descr, AudioDeviceType type) const
     263             : {
     264           0 :     switch (type) {
     265           0 :     case AudioDeviceType::PLAYBACK:
     266             :     case AudioDeviceType::RINGTONE:
     267           0 :         return std::distance(sinkList_.begin(),
     268             :                              std::find_if(sinkList_.begin(),
     269             :                                           sinkList_.end(),
     270           0 :                                           PaDeviceInfos::DescriptionComparator(descr)));
     271           0 :     case AudioDeviceType::CAPTURE:
     272           0 :         return std::distance(sourceList_.begin(),
     273             :                              std::find_if(sourceList_.begin(),
     274             :                                           sourceList_.end(),
     275           0 :                                           PaDeviceInfos::DescriptionComparator(descr)));
     276           0 :     default:
     277           0 :         JAMI_ERR("Unexpected device type");
     278           0 :         return 0;
     279             :     }
     280             : }
     281             : 
     282             : int
     283           0 : PulseLayer::getAudioDeviceIndexByName(const std::string& name, AudioDeviceType type) const
     284             : {
     285           0 :     if (name.empty())
     286           0 :         return 0;
     287           0 :     switch (type) {
     288           0 :     case AudioDeviceType::PLAYBACK:
     289             :     case AudioDeviceType::RINGTONE:
     290           0 :         return std::distance(sinkList_.begin(),
     291             :                              std::find_if(sinkList_.begin(),
     292             :                                           sinkList_.end(),
     293           0 :                                           PaDeviceInfos::NameComparator(name)));
     294           0 :     case AudioDeviceType::CAPTURE:
     295           0 :         return std::distance(sourceList_.begin(),
     296             :                              std::find_if(sourceList_.begin(),
     297             :                                           sourceList_.end(),
     298           0 :                                           PaDeviceInfos::NameComparator(name)));
     299           0 :     default:
     300           0 :         JAMI_ERR("Unexpected device type");
     301           0 :         return 0;
     302             :     }
     303             : }
     304             : 
     305             : bool
     306           0 : endsWith(const std::string& str, const std::string& ending)
     307             : {
     308           0 :     if (ending.size() >= str.size())
     309           0 :         return false;
     310           0 :     return std::equal(ending.rbegin(), ending.rend(), str.rbegin());
     311             : }
     312             : 
     313             : /**
     314             :  * Find default device for PulseAudio to open, filter monitors and EC.
     315             :  */
     316             : const PaDeviceInfos*
     317           0 : findBest(const std::vector<PaDeviceInfos>& list)
     318             : {
     319           0 :     if (list.empty())
     320           0 :         return nullptr;
     321           0 :     for (const auto& info : list)
     322           0 :         if (info.monitor_of == PA_INVALID_INDEX)
     323           0 :             return &info;
     324           0 :     return &list[0];
     325             : }
     326             : 
     327             : const PaDeviceInfos*
     328           0 : PulseLayer::getDeviceInfos(const std::vector<PaDeviceInfos>& list, const std::string& name) const
     329             : {
     330           0 :     auto dev_info = std::find_if(list.begin(), list.end(), PaDeviceInfos::NameComparator(name));
     331           0 :     if (dev_info == list.end()) {
     332           0 :         JAMI_WARN("Preferred device %s not found in device list, selecting default %s instead.",
     333             :                   name.c_str(),
     334             :                   list.front().name.c_str());
     335           0 :         return &list.front();
     336             :     }
     337           0 :     return &(*dev_info);
     338             : }
     339             : 
     340             : std::string
     341           0 : PulseLayer::getAudioDeviceName(int index, AudioDeviceType type) const
     342             : {
     343           0 :     switch (type) {
     344           0 :     case AudioDeviceType::PLAYBACK:
     345             :     case AudioDeviceType::RINGTONE:
     346           0 :         if (index < 0 or static_cast<size_t>(index) >= sinkList_.size()) {
     347           0 :             JAMI_ERR("Index %d out of range", index);
     348           0 :             return "";
     349             :         }
     350           0 :         return sinkList_[index].name;
     351             : 
     352           0 :     case AudioDeviceType::CAPTURE:
     353           0 :         if (index < 0 or static_cast<size_t>(index) >= sourceList_.size()) {
     354           0 :             JAMI_ERR("Index %d out of range", index);
     355           0 :             return "";
     356             :         }
     357           0 :         return sourceList_[index].name;
     358             : 
     359           0 :     default:
     360             :         // Should never happen
     361           0 :         JAMI_ERR("Unexpected type");
     362           0 :         return "";
     363             :     }
     364             : }
     365             : 
     366             : void
     367           0 : PulseLayer::onStreamReady()
     368             : {
     369           0 :     if (--pendingStreams == 0) {
     370           0 :         JAMI_DBG("All streams ready, starting audio");
     371             :         // Flush outside the if statement: every time start stream is
     372             :         // called is to notify a new event
     373           0 :         flushUrgent();
     374           0 :         flushMain();
     375           0 :         if (playback_) {
     376           0 :             playback_->start();
     377           0 :             playbackChanged(true);
     378             :         }
     379           0 :         if (ringtone_) {
     380           0 :             ringtone_->start();
     381             :         }
     382           0 :         if (record_) {
     383           0 :             record_->start();
     384           0 :             recordChanged(true);
     385             :         }
     386             :     }
     387           0 : }
     388             : 
     389             : void
     390           0 : PulseLayer::createStream(std::unique_ptr<AudioStream>& stream,
     391             :                          AudioDeviceType type,
     392             :                          const PaDeviceInfos& dev_infos,
     393             :                          bool ec,
     394             :                          std::function<void(size_t)>&& onData)
     395             : {
     396           0 :     if (stream) {
     397           0 :         JAMI_WARN("Stream already exists");
     398           0 :         return;
     399             :     }
     400           0 :     pendingStreams++;
     401           0 :     const char* name = type == AudioDeviceType::PLAYBACK
     402           0 :                            ? "Playback"
     403             :                            : (type == AudioDeviceType::CAPTURE
     404           0 :                                   ? "Record"
     405           0 :                                   : (type == AudioDeviceType::RINGTONE ? "Ringtone" : "?"));
     406           0 :     stream.reset(new AudioStream(context_,
     407           0 :                                  mainloop_.get(),
     408             :                                  name,
     409             :                                  type,
     410             :                                  audioFormat_.sample_rate,
     411           0 :                                  pulseSampleFormatFromAv(audioFormat_.sampleFormat),
     412             :                                  dev_infos,
     413             :                                  ec,
     414           0 :                                  std::bind(&PulseLayer::onStreamReady, this),
     415           0 :                                  std::move(onData)));
     416             : }
     417             : 
     418             : void
     419           0 : PulseLayer::disconnectAudioStream()
     420             : {
     421           0 :     PulseMainLoopLock lock(mainloop_.get());
     422           0 :     playback_.reset();
     423           0 :     ringtone_.reset();
     424           0 :     record_.reset();
     425           0 :     playbackChanged(false);
     426           0 :     recordChanged(false);
     427           0 :     pendingStreams = 0;
     428           0 :     status_ = Status::Idle;
     429           0 :     startedCv_.notify_all();
     430           0 : }
     431             : 
     432             : void
     433           0 : PulseLayer::startStream(AudioDeviceType type)
     434             : {
     435           0 :     waitForDevices();
     436           0 :     PulseMainLoopLock lock(mainloop_.get());
     437           0 :     bool ec = preference_.getEchoCanceller() == "system"
     438           0 :               || preference_.getEchoCanceller() == "auto";
     439             : 
     440             :     // Create Streams
     441           0 :     if (type == AudioDeviceType::PLAYBACK) {
     442           0 :         if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
     443           0 :             createStream(playback_,
     444             :                          type,
     445             :                          *dev_infos,
     446             :                          ec,
     447           0 :                          std::bind(&PulseLayer::writeToSpeaker, this));
     448             :         }
     449           0 :     } else if (type == AudioDeviceType::RINGTONE) {
     450           0 :         if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice()))
     451           0 :             createStream(ringtone_,
     452             :                          type,
     453             :                          *dev_infos,
     454             :                          false,
     455           0 :                          std::bind(&PulseLayer::ringtoneToSpeaker, this));
     456           0 :     } else if (type == AudioDeviceType::CAPTURE) {
     457           0 :         if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
     458           0 :             createStream(record_, type, *dev_infos, ec, std::bind(&PulseLayer::readFromMic, this));
     459             : 
     460             :             // whenever the stream is moved, it will call this cb
     461           0 :             record_->setEchoCancelCb([this](bool echoCancel) { setHasNativeAEC(echoCancel); });
     462             :         }
     463             :     }
     464           0 :     pa_threaded_mainloop_signal(mainloop_.get(), 0);
     465             : 
     466           0 :     std::lock_guard lk(mutex_);
     467           0 :     status_ = Status::Started;
     468           0 :     startedCv_.notify_all();
     469           0 : }
     470             : 
     471             : void
     472           0 : PulseLayer::stopStream(AudioDeviceType type)
     473             : {
     474           0 :     waitForDevices();
     475           0 :     PulseMainLoopLock lock(mainloop_.get());
     476           0 :     auto& stream(getStream(type));
     477           0 :     if (not stream)
     478           0 :         return;
     479             : 
     480           0 :     if (not stream->isReady())
     481           0 :         pendingStreams--;
     482           0 :     stream->stop();
     483           0 :     stream.reset();
     484             : 
     485           0 :     if (type == AudioDeviceType::PLAYBACK || type == AudioDeviceType::ALL)
     486           0 :         playbackChanged(false);
     487             : 
     488           0 :     std::lock_guard lk(mutex_);
     489           0 :     if (not playback_ and not ringtone_ and not record_) {
     490           0 :         pendingStreams = 0;
     491           0 :         status_ = Status::Idle;
     492           0 :         startedCv_.notify_all();
     493             :     }
     494           0 : }
     495             : 
     496             : void
     497           0 : PulseLayer::writeToSpeaker()
     498             : {
     499           0 :     if (!playback_ or !playback_->isReady())
     500           0 :         return;
     501             : 
     502             :     // available bytes to be written in pulseaudio internal buffer
     503           0 :     void* data = nullptr;
     504           0 :     size_t writableBytes = (size_t) -1;
     505           0 :     int ret = pa_stream_begin_write(playback_->stream(), &data, &writableBytes);
     506           0 :     if (ret == 0 and data and writableBytes != 0) {
     507           0 :         writableBytes = std::min(pa_stream_writable_size(playback_->stream()), writableBytes);
     508           0 :         const auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
     509           0 :         if (not buff or isPlaybackMuted_)
     510           0 :             memset(data, 0, writableBytes);
     511             :         else
     512           0 :             std::memcpy(data,
     513           0 :                         buff->pointer()->data[0],
     514           0 :                         buff->pointer()->nb_samples * playback_->frameSize());
     515           0 :         pa_stream_write(playback_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
     516           0 :     }
     517             : }
     518             : 
     519             : void
     520           0 : PulseLayer::readFromMic()
     521             : {
     522           0 :     if (!record_ or !record_->isReady())
     523           0 :         return;
     524             : 
     525           0 :     const char* data = nullptr;
     526             :     size_t bytes;
     527           0 :     if (pa_stream_peek(record_->stream(), (const void**) &data, &bytes) < 0 or !data)
     528           0 :         return;
     529             : 
     530           0 :     if (bytes == 0)
     531           0 :         return;
     532             : 
     533           0 :     size_t sample_size = record_->frameSize();
     534           0 :     const size_t samples = bytes / sample_size;
     535             : 
     536           0 :     auto out = std::make_shared<AudioFrame>(record_->format(), samples);
     537           0 :     if (isCaptureMuted_)
     538           0 :         libav_utils::fillWithSilence(out->pointer());
     539             :     else
     540           0 :         std::memcpy(out->pointer()->data[0], data, bytes);
     541             : 
     542           0 :     if (pa_stream_drop(record_->stream()) < 0)
     543           0 :         JAMI_ERR("Capture stream drop failed: %s", pa_strerror(pa_context_errno(context_)));
     544             : 
     545           0 :     putRecorded(std::move(out));
     546           0 : }
     547             : 
     548             : void
     549           0 : PulseLayer::ringtoneToSpeaker()
     550             : {
     551           0 :     if (!ringtone_ or !ringtone_->isReady())
     552           0 :         return;
     553             : 
     554           0 :     void* data = nullptr;
     555           0 :     size_t writableBytes = (size_t) -1;
     556           0 :     int ret = pa_stream_begin_write(ringtone_->stream(), &data, &writableBytes);
     557           0 :     if (ret == 0 and data and writableBytes != 0) {
     558           0 :         writableBytes = std::min(pa_stream_writable_size(ringtone_->stream()), writableBytes);
     559           0 :         const auto& buff = getToRing(ringtone_->format(), writableBytes / ringtone_->frameSize());
     560           0 :         if (not buff or isRingtoneMuted_)
     561           0 :             memset(data, 0, writableBytes);
     562             :         else
     563           0 :             std::memcpy(data,
     564           0 :                         buff->pointer()->data[0],
     565           0 :                         buff->pointer()->nb_samples * ringtone_->frameSize());
     566           0 :         pa_stream_write(ringtone_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
     567           0 :     }
     568             : }
     569             : 
     570             : std::string
     571           0 : stripEchoSufix(const std::string& deviceName)
     572             : {
     573           0 :     return std::regex_replace(deviceName, PA_EC_SUFFIX, "");
     574             : }
     575             : 
     576             : void
     577           0 : PulseLayer::context_changed_callback(pa_context* c,
     578             :                                      pa_subscription_event_type_t type,
     579             :                                      uint32_t idx,
     580             :                                      void* userdata)
     581             : {
     582           0 :     static_cast<PulseLayer*>(userdata)->contextChanged(c, type, idx);
     583           0 : }
     584             : 
     585             : void
     586           0 : PulseLayer::contextChanged(pa_context* c UNUSED,
     587             :                            pa_subscription_event_type_t type,
     588             :                            uint32_t idx UNUSED)
     589             : {
     590           0 :     bool reset = false;
     591             : 
     592           0 :     switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
     593           0 :     case PA_SUBSCRIPTION_EVENT_SINK:
     594           0 :         switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
     595           0 :         case PA_SUBSCRIPTION_EVENT_NEW:
     596             :         case PA_SUBSCRIPTION_EVENT_REMOVE:
     597           0 :             updateSinkList();
     598           0 :             reset = true;
     599           0 :         default:
     600           0 :             break;
     601             :         }
     602             : 
     603           0 :         break;
     604             : 
     605           0 :     case PA_SUBSCRIPTION_EVENT_SOURCE:
     606           0 :         switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
     607           0 :         case PA_SUBSCRIPTION_EVENT_NEW:
     608             :         case PA_SUBSCRIPTION_EVENT_REMOVE:
     609           0 :             updateSourceList();
     610           0 :             reset = true;
     611           0 :         default:
     612           0 :             break;
     613             :         }
     614             : 
     615           0 :         break;
     616             : 
     617           0 :     default:
     618           0 :         JAMI_DBG("Unhandled event type 0x%x", type);
     619           0 :         break;
     620             :     }
     621             : 
     622           0 :     if (reset) {
     623           0 :         updateServerInfo();
     624           0 :         waitForDeviceList();
     625             :     }
     626           0 : }
     627             : 
     628             : void
     629           0 : PulseLayer::waitForDevices()
     630             : {
     631           0 :     std::unique_lock lk(readyMtx_);
     632           0 :     readyCv_.wait(lk, [this] {
     633           0 :         return !(enumeratingSinks_ or enumeratingSources_ or gettingServerInfo_);
     634             :     });
     635           0 : }
     636             : 
     637             : void
     638           0 : PulseLayer::waitForDeviceList()
     639             : {
     640           0 :     std::unique_lock lock(readyMtx_);
     641           0 :     if (waitingDeviceList_.exchange(true))
     642           0 :         return;
     643           0 :     if (streamStarter_.joinable())
     644           0 :         streamStarter_.join();
     645           0 :     streamStarter_ = std::thread([this]() mutable {
     646             :         bool playbackDeviceChanged, recordDeviceChanged;
     647             : 
     648           0 :         waitForDevices();
     649           0 :         waitingDeviceList_ = false;
     650             : 
     651             :         // If a current device changed, restart streams
     652           0 :         devicesChanged();
     653           0 :         auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
     654           0 :         playbackDeviceChanged = playback_
     655           0 :                                 and (!playbackInfo->name.empty()
     656           0 :                                      and playbackInfo->name
     657           0 :                                              != stripEchoSufix(playback_->getDeviceName()));
     658             : 
     659           0 :         auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
     660           0 :         recordDeviceChanged = record_
     661           0 :                               and (!recordInfo->name.empty()
     662           0 :                                    and recordInfo->name != stripEchoSufix(record_->getDeviceName()));
     663             : 
     664           0 :         if (status_ != Status::Started)
     665           0 :             return;
     666           0 :         if (playbackDeviceChanged) {
     667           0 :             JAMI_WARN("Playback devices changed, restarting streams.");
     668           0 :             stopStream(AudioDeviceType::PLAYBACK);
     669           0 :             startStream(AudioDeviceType::PLAYBACK);
     670             :         }
     671           0 :         if (recordDeviceChanged) {
     672           0 :             JAMI_WARN("Record devices changed, restarting streams.");
     673           0 :             stopStream(AudioDeviceType::CAPTURE);
     674           0 :             startStream(AudioDeviceType::CAPTURE);
     675             :         }
     676           0 :     });
     677           0 : }
     678             : 
     679             : void
     680           0 : PulseLayer::server_info_callback(pa_context*, const pa_server_info* i, void* userdata)
     681             : {
     682           0 :     if (!i)
     683           0 :         return;
     684             :     char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     685           0 :     JAMI_DBG("PulseAudio server info:\n"
     686             :              "    Server name: %s\n"
     687             :              "    Server version: %s\n"
     688             :              "    Default Sink %s\n"
     689             :              "    Default Source %s\n"
     690             :              "    Default Sample Specification: %s\n"
     691             :              "    Default Channel Map: %s\n",
     692             :              i->server_name,
     693             :              i->server_version,
     694             :              i->default_sink_name,
     695             :              i->default_source_name,
     696             :              pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
     697             :              pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
     698             : 
     699           0 :     PulseLayer* context = static_cast<PulseLayer*>(userdata);
     700           0 :     std::lock_guard lk(context->readyMtx_);
     701           0 :     context->defaultSink_ = {};
     702           0 :     context->defaultSource_ = {};
     703           0 :     context->defaultAudioFormat_ = {
     704           0 :         i->sample_spec.rate,
     705           0 :         i->sample_spec.channels,
     706           0 :         sampleFormatFromPulse(i->sample_spec.format)
     707             :     };
     708             :     {
     709           0 :         std::lock_guard lk(context->mutex_);
     710           0 :         context->hardwareFormatAvailable(context->defaultAudioFormat_);
     711           0 :     }
     712             :     /*if (not context->sinkList_.empty())
     713             :         context->sinkList_.front().channel_map.channels = std::min(i->sample_spec.channels,
     714             :                                                                    (uint8_t) 2);
     715             :     if (not context->sourceList_.empty())
     716             :         context->sourceList_.front().channel_map.channels = std::min(i->sample_spec.channels,
     717             :                                                                      (uint8_t) 2);*/
     718           0 :     context->gettingServerInfo_ = false;
     719           0 :     context->readyCv_.notify_all();
     720           0 : }
     721             : 
     722             : void
     723           0 : PulseLayer::source_input_info_callback(pa_context* c UNUSED,
     724             :                                        const pa_source_info* i,
     725             :                                        int eol,
     726             :                                        void* userdata)
     727             : {
     728           0 :     PulseLayer* context = static_cast<PulseLayer*>(userdata);
     729             : 
     730           0 :     if (eol) {
     731           0 :         std::lock_guard lk(context->readyMtx_);
     732           0 :         context->enumeratingSources_ = false;
     733           0 :         context->readyCv_.notify_all();
     734           0 :         return;
     735           0 :     }
     736             : #ifdef PA_LOG_SINK_SOURCES
     737             :     char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     738             :     JAMI_DBG("Source %u\n"
     739             :              "    Name: %s\n"
     740             :              "    Driver: %s\n"
     741             :              "    Description: %s\n"
     742             :              "    Sample Specification: %s\n"
     743             :              "    Channel Map: %s\n"
     744             :              "    Owner Module: %u\n"
     745             :              "    Volume: %s\n"
     746             :              "    Monitor if Sink: %u\n"
     747             :              "    Latency: %0.0f usec\n"
     748             :              "    Flags: %s%s%s\n",
     749             :              i->index,
     750             :              i->name,
     751             :              i->driver,
     752             :              i->description,
     753             :              pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
     754             :              pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
     755             :              i->owner_module,
     756             :              i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
     757             :              i->monitor_of_sink,
     758             :              (double) i->latency,
     759             :              i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
     760             :              i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
     761             :              i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : "");
     762             : #endif
     763           0 :     if (not context->inSourceList(i->name)) {
     764           0 :         context->sourceList_.emplace_back(*i);
     765             :     }
     766             : }
     767             : 
     768             : void
     769           0 : PulseLayer::sink_input_info_callback(pa_context* c UNUSED,
     770             :                                      const pa_sink_info* i,
     771             :                                      int eol,
     772             :                                      void* userdata)
     773             : {
     774           0 :     PulseLayer* context = static_cast<PulseLayer*>(userdata);
     775           0 :     std::lock_guard lk(context->readyMtx_);
     776             : 
     777           0 :     if (eol) {
     778           0 :         context->enumeratingSinks_ = false;
     779           0 :         context->readyCv_.notify_all();
     780           0 :         return;
     781             :     }
     782             : #ifdef PA_LOG_SINK_SOURCES
     783             :     char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     784             :     JAMI_DBG("Sink %u\n"
     785             :              "    Name: %s\n"
     786             :              "    Driver: %s\n"
     787             :              "    Description: %s\n"
     788             :              "    Sample Specification: %s\n"
     789             :              "    Channel Map: %s\n"
     790             :              "    Owner Module: %u\n"
     791             :              "    Volume: %s\n"
     792             :              "    Monitor Source: %u\n"
     793             :              "    Latency: %0.0f usec\n"
     794             :              "    Flags: %s%s%s\n",
     795             :              i->index,
     796             :              i->name,
     797             :              i->driver,
     798             :              i->description,
     799             :              pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
     800             :              pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
     801             :              i->owner_module,
     802             :              i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
     803             :              i->monitor_source,
     804             :              static_cast<double>(i->latency),
     805             :              i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
     806             :              i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
     807             :              i->flags & PA_SINK_HARDWARE ? "HARDWARE" : "");
     808             : #endif
     809           0 :     if (not context->inSinkList(i->name)) {
     810           0 :         context->sinkList_.emplace_back(*i);
     811             :     }
     812           0 : }
     813             : 
     814             : void
     815           0 : PulseLayer::updatePreference(AudioPreference& preference, int index, AudioDeviceType type)
     816             : {
     817           0 :     const std::string devName(getAudioDeviceName(index, type));
     818             : 
     819           0 :     switch (type) {
     820           0 :     case AudioDeviceType::PLAYBACK:
     821           0 :         JAMI_DBG("setting %s for playback", devName.c_str());
     822           0 :         preference.setPulseDevicePlayback(devName);
     823           0 :         break;
     824             : 
     825           0 :     case AudioDeviceType::CAPTURE:
     826           0 :         JAMI_DBG("setting %s for capture", devName.c_str());
     827           0 :         preference.setPulseDeviceRecord(devName);
     828           0 :         break;
     829             : 
     830           0 :     case AudioDeviceType::RINGTONE:
     831           0 :         JAMI_DBG("setting %s for ringer", devName.c_str());
     832           0 :         preference.setPulseDeviceRingtone(devName);
     833           0 :         break;
     834             : 
     835           0 :     default:
     836           0 :         break;
     837             :     }
     838           0 : }
     839             : 
     840             : int
     841           0 : PulseLayer::getIndexCapture() const
     842             : {
     843           0 :     return getAudioDeviceIndexByName(preference_.getPulseDeviceRecord(), AudioDeviceType::CAPTURE);
     844             : }
     845             : 
     846             : int
     847           0 : PulseLayer::getIndexPlayback() const
     848             : {
     849           0 :     return getAudioDeviceIndexByName(preference_.getPulseDevicePlayback(),
     850           0 :                                      AudioDeviceType::PLAYBACK);
     851             : }
     852             : 
     853             : int
     854           0 : PulseLayer::getIndexRingtone() const
     855             : {
     856           0 :     return getAudioDeviceIndexByName(preference_.getPulseDeviceRingtone(),
     857           0 :                                      AudioDeviceType::RINGTONE);
     858             : }
     859             : 
     860             : std::string
     861           0 : PulseLayer::getPreferredPlaybackDevice() const
     862             : {
     863           0 :     const std::string& device(preference_.getPulseDevicePlayback());
     864           0 :     return stripEchoSufix(device.empty() ? defaultSink_ : device);
     865             : }
     866             : 
     867             : std::string
     868           0 : PulseLayer::getPreferredRingtoneDevice() const
     869             : {
     870           0 :     const std::string& device(preference_.getPulseDeviceRingtone());
     871           0 :     return stripEchoSufix(device.empty() ? defaultSink_ : device);
     872             : }
     873             : 
     874             : std::string
     875           0 : PulseLayer::getPreferredCaptureDevice() const
     876             : {
     877           0 :     const std::string& device(preference_.getPulseDeviceRecord());
     878           0 :     return stripEchoSufix(device.empty() ? defaultSource_ : device);
     879             : }
     880             : 
     881             : } // namespace jami

Generated by: LCOV version 1.14