Unverified Commit 7ba51932 authored by Adrien Béraud's avatar Adrien Béraud Committed by Sébastien Blin
Browse files

alsa: cleanup, fix deadlock

Change-Id: Ia395103ba2ec2ec85400cf710a07c40888568d68
parent c247f1d5
...@@ -39,71 +39,14 @@ ...@@ -39,71 +39,14 @@
namespace jami { namespace jami {
class AlsaThread
{
public:
AlsaThread(AlsaLayer* alsa);
~AlsaThread();
void start();
bool isRunning() const;
private:
NON_COPYABLE(AlsaThread);
void run();
AlsaLayer* alsa_;
std::atomic<bool> running_;
std::thread thread_;
};
AlsaThread::AlsaThread(AlsaLayer* alsa)
: alsa_(alsa)
, running_(false)
, thread_()
{}
bool
AlsaThread::isRunning() const
{
return running_;
}
AlsaThread::~AlsaThread()
{
running_ = false;
if (thread_.joinable())
thread_.join();
}
void
AlsaThread::start()
{
running_ = true;
thread_ = std::thread(&AlsaThread::run, this);
}
void
AlsaThread::run()
{
alsa_->run(running_);
}
AlsaLayer::AlsaLayer(const AudioPreference& pref) AlsaLayer::AlsaLayer(const AudioPreference& pref)
: AudioLayer(pref) : AudioLayer(pref)
, indexIn_(pref.getAlsaCardin()) , indexIn_(pref.getAlsaCardin())
, indexOut_(pref.getAlsaCardout()) , indexOut_(pref.getAlsaCardout())
, indexRing_(pref.getAlsaCardRingtone()) , indexRing_(pref.getAlsaCardRingtone())
, playbackHandle_(nullptr)
, ringtoneHandle_(nullptr)
, captureHandle_(nullptr)
, audioPlugin_(pref.getAlsaPlugin()) , audioPlugin_(pref.getAlsaPlugin())
, playbackBuff_(0, audioFormat_) , playbackBuff_(0, audioFormat_)
, captureBuff_(0, audioFormat_) , captureBuff_(0, audioFormat_)
, is_capture_prepared_(false)
, is_playback_running_(false)
, is_capture_running_(false)
, is_playback_open_(false)
, is_capture_open_(false)
, audioThread_(nullptr)
{ {
setHasNativeAEC(false); setHasNativeAEC(false);
} }
...@@ -111,7 +54,7 @@ AlsaLayer::AlsaLayer(const AudioPreference& pref) ...@@ -111,7 +54,7 @@ AlsaLayer::AlsaLayer(const AudioPreference& pref)
AlsaLayer::~AlsaLayer() AlsaLayer::~AlsaLayer()
{ {
status_ = Status::Idle; status_ = Status::Idle;
audioThread_.reset(); stopThread();
/* Then close the audio devices */ /* Then close the audio devices */
closeCaptureStream(); closeCaptureStream();
...@@ -123,14 +66,14 @@ AlsaLayer::~AlsaLayer() ...@@ -123,14 +66,14 @@ AlsaLayer::~AlsaLayer()
* Reimplementation of run() * Reimplementation of run()
*/ */
void void
AlsaLayer::run(const std::atomic<bool>& isRunning) AlsaLayer::run()
{ {
if (playbackHandle_) if (playbackHandle_)
playbackChanged(true); playbackChanged(true);
if (captureHandle_) if (captureHandle_)
recordChanged(true); recordChanged(true);
while (status_ == Status::Started and isRunning) { while (status_ == Status::Started and running_) {
playback(); playback();
ringtone(); ringtone();
capture(); capture();
...@@ -148,8 +91,8 @@ AlsaLayer::openDevice(snd_pcm_t** pcm, ...@@ -148,8 +91,8 @@ AlsaLayer::openDevice(snd_pcm_t** pcm,
AudioFormat& format) AudioFormat& format)
{ {
JAMI_DBG("Alsa: Opening %s device '%s'", JAMI_DBG("Alsa: Opening %s device '%s'",
(stream == SND_PCM_STREAM_CAPTURE) ? "capture" : "playback", (stream == SND_PCM_STREAM_CAPTURE) ? "capture" : "playback",
dev.c_str()); dev.c_str());
static const int MAX_RETRIES = 10; // times of 100ms static const int MAX_RETRIES = 10; // times of 100ms
int err, tries = 0; int err, tries = 0;
...@@ -165,9 +108,9 @@ AlsaLayer::openDevice(snd_pcm_t** pcm, ...@@ -165,9 +108,9 @@ AlsaLayer::openDevice(snd_pcm_t** pcm,
if (err < 0) { if (err < 0) {
JAMI_ERR("Alsa: couldn't open %s device %s : %s", JAMI_ERR("Alsa: couldn't open %s device %s : %s",
(stream == SND_PCM_STREAM_CAPTURE) (stream == SND_PCM_STREAM_CAPTURE) ? "capture"
? "capture" : (stream == SND_PCM_STREAM_PLAYBACK) ? "playback"
: (stream == SND_PCM_STREAM_PLAYBACK) ? "playback" : "ringtone", : "ringtone",
dev.c_str(), dev.c_str(),
snd_strerror(err)); snd_strerror(err));
return false; return false;
...@@ -181,10 +124,12 @@ AlsaLayer::openDevice(snd_pcm_t** pcm, ...@@ -181,10 +124,12 @@ AlsaLayer::openDevice(snd_pcm_t** pcm,
return true; return true;
} }
void AlsaLayer::startStream(AudioDeviceType type) void
AlsaLayer::startStream(AudioDeviceType type)
{ {
std::unique_lock<std::mutex> lk(mutex_);
status_ = Status::Starting; status_ = Status::Starting;
audioThread_.reset(); stopThread();
bool dsnop = audioPlugin_ == PCM_DMIX_DSNOOP; bool dsnop = audioPlugin_ == PCM_DMIX_DSNOOP;
...@@ -200,7 +145,8 @@ void AlsaLayer::startStream(AudioDeviceType type) ...@@ -200,7 +145,8 @@ void AlsaLayer::startStream(AudioDeviceType type)
startPlaybackStream(); startPlaybackStream();
} }
if (type == AudioDeviceType::RINGTONE and getIndexPlayback() != getIndexRingtone() and not ringtoneHandle_) { if (type == AudioDeviceType::RINGTONE and getIndexPlayback() != getIndexRingtone()
and not ringtoneHandle_) {
if (!openDevice(&ringtoneHandle_, if (!openDevice(&ringtoneHandle_,
buildDeviceTopo(dsnop ? PCM_DMIX : audioPlugin_, indexRing_), buildDeviceTopo(dsnop ? PCM_DMIX : audioPlugin_, indexRing_),
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
...@@ -221,14 +167,14 @@ void AlsaLayer::startStream(AudioDeviceType type) ...@@ -221,14 +167,14 @@ void AlsaLayer::startStream(AudioDeviceType type)
} }
status_ = Status::Started; status_ = Status::Started;
audioThread_.reset(new AlsaThread(this)); startThread();
audioThread_->start();
} }
void void
AlsaLayer::stopStream(AudioDeviceType stream) AlsaLayer::stopStream(AudioDeviceType stream)
{ {
audioThread_.reset(); std::unique_lock<std::mutex> lk(mutex_);
stopThread();
if (stream == AudioDeviceType::CAPTURE && is_capture_open_) { if (stream == AudioDeviceType::CAPTURE && is_capture_open_) {
closeCaptureStream(); closeCaptureStream();
...@@ -245,13 +191,27 @@ AlsaLayer::stopStream(AudioDeviceType stream) ...@@ -245,13 +191,27 @@ AlsaLayer::stopStream(AudioDeviceType stream)
} }
if (is_capture_open_ or is_playback_open_ or ringtoneHandle_) { if (is_capture_open_ or is_playback_open_ or ringtoneHandle_) {
audioThread_.reset(new AlsaThread(this)); startThread();
audioThread_->start();
} else { } else {
status_ = Status::Idle; status_ = Status::Idle;
} }
} }
void
AlsaLayer::startThread()
{
running_ = true;
audioThread_ = std::thread(&AlsaLayer::run, this);
}
void
AlsaLayer::stopThread()
{
running_ = false;
if (audioThread_.joinable())
audioThread_.join();
}
/* /*
* GCC extension : statement expression * GCC extension : statement expression
* *
...@@ -284,7 +244,8 @@ AlsaLayer::closeCaptureStream() ...@@ -284,7 +244,8 @@ AlsaLayer::closeCaptureStream()
stopCaptureStream(); stopCaptureStream();
JAMI_DBG("Alsa: Closing capture stream"); JAMI_DBG("Alsa: Closing capture stream");
if (is_capture_open_ && ALSA_CALL(snd_pcm_close(captureHandle_), "Couldn't close capture") >= 0) { if (is_capture_open_
&& ALSA_CALL(snd_pcm_close(captureHandle_), "Couldn't close capture") >= 0) {
is_capture_open_ = false; is_capture_open_ = false;
captureHandle_ = nullptr; captureHandle_ = nullptr;
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <memory> #include <memory>
#include <thread>
#define PCM_DMIX "plug:dmix" /** Alsa plugin for software mixing */ #define PCM_DMIX "plug:dmix" /** Alsa plugin for software mixing */
...@@ -137,7 +138,7 @@ public: ...@@ -137,7 +138,7 @@ public:
*/ */
virtual int getIndexRingtone() const { return indexRing_; } virtual int getIndexRingtone() const { return indexRing_; }
void run(const std::atomic<bool>& isRunning); void run();
private: private:
/** /**
...@@ -188,6 +189,9 @@ private: ...@@ -188,6 +189,9 @@ private:
bool alsa_set_params(snd_pcm_t* pcm_handle, AudioFormat& format); bool alsa_set_params(snd_pcm_t* pcm_handle, AudioFormat& format);
void startThread();
void stopThread();
/** /**
* Copy a data buffer in the internal ring buffer * Copy a data buffer in the internal ring buffer
* ALSA Library API * ALSA Library API
...@@ -211,19 +215,19 @@ private: ...@@ -211,19 +215,19 @@ private:
* Handles to manipulate playback stream * Handles to manipulate playback stream
* ALSA Library API * ALSA Library API
*/ */
snd_pcm_t* playbackHandle_; snd_pcm_t* playbackHandle_ {nullptr};
/** /**
* Handles to manipulate ringtone stream * Handles to manipulate ringtone stream
* *
*/ */
snd_pcm_t* ringtoneHandle_; snd_pcm_t* ringtoneHandle_ {nullptr};
/** /**
* Handles to manipulate capture stream * Handles to manipulate capture stream
* ALSA Library API * ALSA Library API
*/ */
snd_pcm_t* captureHandle_; snd_pcm_t* captureHandle_ {nullptr};
/** /**
* name of the alsa audio plugin used * name of the alsa audio plugin used
...@@ -234,13 +238,14 @@ private: ...@@ -234,13 +238,14 @@ private:
AudioBuffer playbackBuff_; AudioBuffer playbackBuff_;
AudioBuffer captureBuff_; AudioBuffer captureBuff_;
bool is_capture_prepared_; bool is_capture_prepared_ {false};
bool is_playback_running_; bool is_playback_running_ {false};
bool is_capture_running_; bool is_capture_running_ {false};
bool is_playback_open_; bool is_playback_open_ {false};
bool is_capture_open_; bool is_capture_open_ {false};
std::unique_ptr<AlsaThread> audioThread_; std::atomic_bool running_ {false};
std::thread audioThread_;
}; };
} // namespace jami } // namespace jami
...@@ -62,7 +62,6 @@ void ...@@ -62,7 +62,6 @@ void
AudioLayer::hardwareFormatAvailable(AudioFormat playback, size_t bufSize) AudioLayer::hardwareFormatAvailable(AudioFormat playback, size_t bufSize)
{ {
JAMI_DBG("Hardware audio format available : %s %zu", playback.toString().c_str(), bufSize); JAMI_DBG("Hardware audio format available : %s %zu", playback.toString().c_str(), bufSize);
std::unique_lock<std::mutex> lk(mutex_);
audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback); audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback);
urgentRingBuffer_.setFormat(audioFormat_); urgentRingBuffer_.setFormat(audioFormat_);
nativeFrameSize_ = bufSize; nativeFrameSize_ = bufSize;
...@@ -89,7 +88,6 @@ AudioLayer::flushMain() ...@@ -89,7 +88,6 @@ AudioLayer::flushMain()
void void
AudioLayer::flushUrgent() AudioLayer::flushUrgent()
{ {
std::lock_guard<std::mutex> lock(mutex_);
urgentRingBuffer_.flushAll(); urgentRingBuffer_.flushAll();
} }
......
...@@ -379,12 +379,10 @@ JackLayer::process_playback(jack_nframes_t frames, void* arg) ...@@ -379,12 +379,10 @@ JackLayer::process_playback(jack_nframes_t frames, void* arg)
*/ */
void JackLayer::startStream(AudioDeviceType) void JackLayer::startStream(AudioDeviceType)
{ {
{ std::lock_guard<std::mutex> lock(mutex_);
std::lock_guard<std::mutex> lock(mutex_); if (status_ != Status::Idle)
if (status_ != Status::Idle) return;
return; status_ = Status::Started;
status_ = Status::Started;
}
dcblocker_.reset(); dcblocker_.reset();
if (jack_activate(playbackClient_) or jack_activate(captureClient_)) { if (jack_activate(playbackClient_) or jack_activate(captureClient_)) {
...@@ -408,13 +406,11 @@ JackLayer::onShutdown(void* /* data */) ...@@ -408,13 +406,11 @@ JackLayer::onShutdown(void* /* data */)
*/ */
void JackLayer::stopStream(AudioDeviceType) void JackLayer::stopStream(AudioDeviceType)
{ {
{ std::lock_guard<std::mutex> lock(mutex_);
std::lock_guard<std::mutex> lock(mutex_); if (status_ != Status::Started)
if (status_ != Status::Started) return;
return; status_ = Status::Idle;
status_ = Status::Idle; data_ready_.notify_one();
data_ready_.notify_one();
}
if (jack_deactivate(playbackClient_) or jack_deactivate(captureClient_)) { if (jack_deactivate(playbackClient_) or jack_deactivate(captureClient_)) {
JAMI_ERR("JACK client could not deactivate"); JAMI_ERR("JACK client could not deactivate");
......
...@@ -43,8 +43,7 @@ namespace jami { ...@@ -43,8 +43,7 @@ namespace jami {
// Constructor // Constructor
OpenSLLayer::OpenSLLayer(const AudioPreference& pref) OpenSLLayer::OpenSLLayer(const AudioPreference& pref)
: AudioLayer(pref) : AudioLayer(pref)
{ {}
}
// Destructor // Destructor
OpenSLLayer::~OpenSLLayer() OpenSLLayer::~OpenSLLayer()
...@@ -56,10 +55,11 @@ void ...@@ -56,10 +55,11 @@ void
OpenSLLayer::startStream(AudioDeviceType stream) OpenSLLayer::startStream(AudioDeviceType stream)
{ {
using namespace std::placeholders; using namespace std::placeholders;
std::lock_guard<std::mutex> lock(mutex_);
if (!engineObject_) if (!engineObject_)
initAudioEngine(); initAudioEngine();
std::lock_guard<std::mutex> lock(mutex_);
JAMI_WARN("Start OpenSL audio layer"); JAMI_WARN("Start OpenSL audio layer");
if (stream == AudioDeviceType::PLAYBACK) { if (stream == AudioDeviceType::PLAYBACK) {
...@@ -97,7 +97,8 @@ OpenSLLayer::startStream(AudioDeviceType stream) ...@@ -97,7 +97,8 @@ OpenSLLayer::startStream(AudioDeviceType stream)
if (not recorder_) { if (not recorder_) {
std::lock_guard<std::mutex> lck(recMtx); std::lock_guard<std::mutex> lck(recMtx);
try { try {
recorder_.reset(new opensl::AudioRecorder(hardwareFormat_, hardwareBuffSize_, engineInterface_)); recorder_.reset(
new opensl::AudioRecorder(hardwareFormat_, hardwareBuffSize_, engineInterface_));
recorder_->setBufQueues(&freeRecBufQueue_, &recBufQueue_); recorder_->setBufQueues(&freeRecBufQueue_, &recBufQueue_);
recorder_->registerCallback(std::bind(&OpenSLLayer::engineServiceRec, this)); recorder_->registerCallback(std::bind(&OpenSLLayer::engineServiceRec, this));
setHasNativeAEC(recorder_->hasNativeAEC()); setHasNativeAEC(recorder_->hasNativeAEC());
...@@ -163,7 +164,6 @@ OpenSLLayer::initAudioEngine() ...@@ -163,7 +164,6 @@ OpenSLLayer::initAudioEngine()
hardwareBuffSize_ = hw_infos[1]; hardwareBuffSize_ = hw_infos[1];
hardwareFormatAvailable(hardwareFormat_, hardwareBuffSize_); hardwareFormatAvailable(hardwareFormat_, hardwareBuffSize_);
std::lock_guard<std::mutex> lock(mutex_);
SLASSERT(slCreateEngine(&engineObject_, 0, nullptr, 0, nullptr, nullptr)); SLASSERT(slCreateEngine(&engineObject_, 0, nullptr, 0, nullptr, nullptr));
SLASSERT((*engineObject_)->Realize(engineObject_, SL_BOOLEAN_FALSE)); SLASSERT((*engineObject_)->Realize(engineObject_, SL_BOOLEAN_FALSE));
SLASSERT((*engineObject_)->GetInterface(engineObject_, SL_IID_ENGINE, &engineInterface_)); SLASSERT((*engineObject_)->GetInterface(engineObject_, SL_IID_ENGINE, &engineInterface_));
...@@ -254,10 +254,14 @@ OpenSLLayer::engineServicePlay() ...@@ -254,10 +254,14 @@ OpenSLLayer::engineServicePlay()
break; break;
} }
if (not dat->pointer()->data[0] or not buf->buf_) { if (not dat->pointer()->data[0] or not buf->buf_) {
JAMI_ERR("null bufer %p -> %p %d", dat->pointer()->data[0], buf->buf_, dat->pointer()->nb_samples); JAMI_ERR("null bufer %p -> %p %d",
dat->pointer()->data[0],
buf->buf_,
dat->pointer()->nb_samples);
break; break;
} }
//JAMI_ERR("std::copy_n %p -> %p %zu", dat->pointer()->data[0], buf->buf_, dat->pointer()->nb_samples); // JAMI_ERR("std::copy_n %p -> %p %zu", dat->pointer()->data[0], buf->buf_,
// dat->pointer()->nb_samples);
std::copy_n((const AudioSample*) dat->pointer()->data[0], std::copy_n((const AudioSample*) dat->pointer()->data[0],
dat->pointer()->nb_samples, dat->pointer()->nb_samples,
(AudioSample*) buf->buf_); (AudioSample*) buf->buf_);
...@@ -286,7 +290,10 @@ OpenSLLayer::engineServiceRing() ...@@ -286,7 +290,10 @@ OpenSLLayer::engineServiceRing()
break; break;
} }
if (not dat->pointer()->data[0] or not buf->buf_) { if (not dat->pointer()->data[0] or not buf->buf_) {
JAMI_ERR("null bufer %p -> %p %d", dat->pointer()->data[0], buf->buf_, dat->pointer()->nb_samples); JAMI_ERR("null bufer %p -> %p %d",
dat->pointer()->data[0],
buf->buf_,
dat->pointer()->nb_samples);
break; break;
} }
std::copy_n((const AudioSample*) dat->pointer()->data[0], std::copy_n((const AudioSample*) dat->pointer()->data[0],
......
...@@ -359,7 +359,8 @@ PulseLayer::getAudioDeviceName(int index, AudioDeviceType type) const ...@@ -359,7 +359,8 @@ PulseLayer::getAudioDeviceName(int index, AudioDeviceType type) const
} }
void void
PulseLayer::onStreamReady() { PulseLayer::onStreamReady()
{
if (--pendingStreams == 0) { if (--pendingStreams == 0) {
JAMI_DBG("All streams ready, starting audio"); JAMI_DBG("All streams ready, starting audio");
// Flush outside the if statement: every time start stream is // Flush outside the if statement: every time start stream is
...@@ -376,23 +377,31 @@ PulseLayer::onStreamReady() { ...@@ -376,23 +377,31 @@ PulseLayer::onStreamReady() {
} }
void void
PulseLayer::createStream(std::unique_ptr<AudioStream>& stream, AudioDeviceType type, const PaDeviceInfos& dev_infos, bool ec, std::function<void(size_t)>&& onData) PulseLayer::createStream(std::unique_ptr<AudioStream>& stream,
AudioDeviceType type,
const PaDeviceInfos& dev_infos,
bool ec,
std::function<void(size_t)>&& onData)
{ {
if (stream) { if (stream) {
JAMI_WARN("Stream already exists"); JAMI_WARN("Stream already exists");
return; return;
} }
pendingStreams++; pendingStreams++;
const char* name = type == AudioDeviceType::PLAYBACK ? "Playback" : const char* name = type == AudioDeviceType::PLAYBACK
(type == AudioDeviceType::CAPTURE ? "Record" : ? "Playback"
(type == AudioDeviceType::RINGTONE ? "Ringtone" : "?")); : (type == AudioDeviceType::CAPTURE
stream.reset(new AudioStream(context_, mainloop_.get(), ? "Record"
name, : (type == AudioDeviceType::RINGTONE ? "Ringtone" : "?"));
type, stream.reset(new AudioStream(context_,
audioFormat_.sample_rate, mainloop_.get(),
dev_infos, name,
ec, type,
std::bind(&PulseLayer::onStreamReady, this), std::move(onData))); audioFormat_.sample_rate,
dev_infos,
ec,
std::bind(&PulseLayer::onStreamReady, this),
std::move(onData)));
} }
void void
...@@ -407,8 +416,10 @@ PulseLayer::disconnectAudioStream() ...@@ -407,8 +416,10 @@ PulseLayer::disconnectAudioStream()
startedCv_.notify_all(); startedCv_.notify_all();
} }
void PulseLayer::startStream(AudioDeviceType type) void
PulseLayer::startStream(AudioDeviceType type) <