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");
79 throw std::runtime_error(
"Unable to create PulseAudio context");
84 throw std::runtime_error(
"Unable to connect PulseAudio context to the server");
90 throw std::runtime_error(
"Pulse audio context is bad");
99 if (streamStarter_.joinable())
100 streamStarter_.join();
102 disconnectAudioStream();
124 pulse->contextStateChanged(c);
141 JAMI_DBG(
"Connection to PulseAudio server established");
154 subscribeOp_ =
nullptr;
169 std::unique_lock
lk(readyMtx_);
170 if (
not enumeratingSinks_) {
171 JAMI_DBG(
"Updating PulseAudio sink list");
172 enumeratingSinks_ =
true;
174 sinkList_.emplace_back();
175 sinkList_.front().channel_map.channels = std::min(defaultAudioFormat_.
nb_channels, 2u);
179 enumeratingSinks_ =
false;
186 std::unique_lock
lk(readyMtx_);
187 if (
not enumeratingSources_) {
188 JAMI_DBG(
"Updating PulseAudio source list");
189 enumeratingSources_ =
true;
191 sourceList_.emplace_back();
192 sourceList_.front().channel_map.channels = std::min(defaultAudioFormat_.
nb_channels, 2u);
196 enumeratingSources_ =
false;
203 std::unique_lock
lk(readyMtx_);
204 if (
not gettingServerInfo_) {
205 JAMI_DBG(
"Updating PulseAudio server info");
206 gettingServerInfo_ =
true;
210 gettingServerInfo_ =
false;
225 != sourceList_.end();
228std::vector<std::string>
231 std::vector<std::string>
names;
232 names.reserve(sourceList_.size());
233 for (
const auto& s : sourceList_)
234 names.emplace_back(s.description);
238std::vector<std::string>
241 std::vector<std::string>
names;
242 names.reserve(sinkList_.size());
243 for (
const auto& s : sinkList_)
244 names.emplace_back(s.description);
254 return std::distance(sinkList_.begin(),
255 std::find_if(sinkList_.begin(),
259 return std::distance(sourceList_.begin(),
260 std::find_if(sourceList_.begin(),
277 return std::distance(sinkList_.begin(),
280 return std::distance(sourceList_.begin(),
304 for (
const auto& info :
list)
311PulseLayer::getDeviceInfos(
const std::vector<PaDeviceInfos>&
list,
const std::string& name)
const
313 auto dev_info = std::find_if(
list.begin(),
list.end(), PaDeviceInfos::NameComparator(name));
315 JAMI_WARN(
"Preferred device %s not found in device list, selecting default %s instead.",
317 list.front().name.c_str());
318 return &
list.front();
329 if (index < 0
or static_cast<size_t>(index) >= sinkList_.size()) {
330 JAMI_ERR(
"Index %d out of range", index);
333 return sinkList_[index].name;
336 if (index < 0
or static_cast<size_t>(index) >= sourceList_.size()) {
337 JAMI_ERR(
"Index %d out of range", index);
340 return sourceList_[index].name;
350PulseLayer::onStreamReady()
352 if (--pendingStreams == 0) {
353 JAMI_DBG(
"All streams ready, starting audio");
373PulseLayer::createStream(std::unique_ptr<AudioStream>& stream,
377 std::function<
void(
size_t)>&&
onData)
389 stream.reset(
new AudioStream(context_,
397 std::bind(&PulseLayer::onStreamReady,
this),
402PulseLayer::disconnectAudioStream()
404 PulseMainLoopLock lock(mainloop_.get());
424 if (
auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
428 if (
auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice()))
431 if (
auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
449 JAMI_WARNING(
"[pulselayer] Loopback capture already running");
456 JAMI_ERROR(
"[pulselayer] Failed to get ring buffer for id {}",
id);
460 JAMI_DEBUG(
"[pulselayer] Starting loopback capture for ID {}",
id);
463 const auto channels = loopbackCapture_.
channels();
466 if (!data ||
length == 0) {
473 JAMI_ERROR(
"[pulselayer] Invalid frame size for captured audio");
479 JAMI_WARNING(
"[pulselayer] Ignoring empty capture buffer");
490 JAMI_ERROR(
"[pulselayer] Failed to start loopback capture");
499 JAMI_WARNING(
"[pulselayer] Loopback capture is not running");
503 JAMI_DEBUG(
"[pulselayer] Stopping loopback capture for ID {}",
id);
516 auto& stream(getStream(type));
520 if (
not stream->isReady())
539 if (!playback_
or !playback_->isReady())
543 void* data =
nullptr;
552 std::memcpy(data,
buff->pointer()->data[0],
buff->pointer()->nb_samples * playback_->frameSize());
560 if (!record_
or !record_->isReady())
563 const char* data =
nullptr;
565 if (
pa_stream_peek(record_->stream(), (
const void**) &data, &bytes) < 0
or !data)
574 auto out = std::make_shared<AudioFrame>(record_->format(), samples);
578 std::memcpy(
out->pointer()->data[0], data, bytes);
589 if (!ringtone_
or !ringtone_->isReady())
592 void* data =
nullptr;
601 std::memcpy(data,
buff->pointer()->data[0],
buff->pointer()->nb_samples * ringtone_->frameSize());
609 return std::regex_replace(deviceName,
PA_EC_SUFFIX,
"");
615 static_cast<PulseLayer*
>(
userdata)->contextChanged(c, type, idx);
649 JAMI_DBG(
"Unhandled event type 0x%x", type);
660PulseLayer::waitForDevices()
662 std::unique_lock
lk(readyMtx_);
663 readyCv_.wait(
lk, [
this] {
return !(enumeratingSinks_
or enumeratingSources_
or gettingServerInfo_); });
667PulseLayer::waitForDeviceList()
669 std::unique_lock lock(readyMtx_);
670 if (waitingDeviceList_.exchange(
true))
672 if (streamStarter_.joinable())
673 streamStarter_.join();
674 streamStarter_ = std::thread([
this]()
mutable {
678 waitingDeviceList_ =
false;
682 auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
687 auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
695 JAMI_WARN(
"Playback devices changed, restarting streams.");
700 JAMI_WARN(
"Record devices changed, restarting streams.");
715 "\n Server version: %s"
716 "\n Default sink: %s"
717 "\n Default source: %s"
718 "\n Default sample specification: %s"
719 "\n Default channel map: %s",
722 i->default_sink_name,
723 i->default_source_name,
728 std::lock_guard
lk(context->readyMtx_);
729 context->defaultSink_ = {};
730 context->defaultSource_ = {};
731 context->defaultAudioFormat_ = {
i->sample_spec.rate,
732 i->sample_spec.channels,
735 std::lock_guard
lk(context->mutex_);
736 context->hardwareFormatAvailable(context->defaultAudioFormat_);
744 context->gettingServerInfo_ =
false;
745 context->readyCv_.notify_all();
754 std::lock_guard
lk(context->readyMtx_);
755 context->enumeratingSources_ =
false;
756 context->readyCv_.notify_all();
759#ifdef PA_LOG_SINK_SOURCES
765 " Sample Specification: %s\n"
767 " Owner Module: %u\n"
769 " Monitor if Sink: %u\n"
770 " Latency: %0.0f usec\n"
786 if (
not context->inSourceList(
i->name)) {
787 context->sourceList_.emplace_back(*
i);
795 std::lock_guard
lk(context->readyMtx_);
798 context->enumeratingSinks_ =
false;
799 context->readyCv_.notify_all();
802#ifdef PA_LOG_SINK_SOURCES
808 " Sample Specification: %s\n"
810 " Owner Module: %u\n"
812 " Monitor Source: %u\n"
813 " Latency: %0.0f usec\n"
829 if (
not context->inSinkList(
i->name)) {
830 context->sinkList_.emplace_back(*
i);
861PulseLayer::getIndexCapture()
const
867PulseLayer::getIndexPlayback()
const
873PulseLayer::getIndexRingtone()
const
879PulseLayer::getPreferredPlaybackDevice()
const
886PulseLayer::getPreferredRingtoneDevice()
const
893PulseLayer::getPreferredCaptureDevice()
const
uint32_t sampleRate() const
bool startCaptureAsync(AudioFrameCallback callback)
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
static LIBJAMI_TEST_EXPORT Manager & instance()
RingBufferPool & getRingBufferPool()
Return a pointer to the instance of the RingBufferPool.
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 void startStream(AudioDeviceType stream)
Start the capture stream and prepare the playback stream.
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 stopCaptureStream(const std::string &id) override
Stop an ongoing capture stream on the given device.
void readFromMic()
Write data from the ring buffer to the hardware and read data from the hardware.
virtual void startCaptureStream(const std::string &id) override
Start a capture stream on the given device (eg.
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)
std::shared_ptr< RingBuffer > createRingBuffer(const std::string &id)
Create a new ringbuffer with a default readoffset.
void unBindAll(const std::string &ringbufferId)
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
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)