diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp
index c5b427342a917b8970d91c53590a422c2b7aeaec..bc1669055cc83bb733d73143763651b01581f1d6 100644
--- a/src/media/audio/audio_input.cpp
+++ b/src/media/audio/audio_input.cpp
@@ -179,7 +179,6 @@ AudioInput::readFromFile()
 bool
 AudioInput::initDevice(const std::string& device)
 {
-    JAMI_WARN("AudioInput::initDevice %s", device.c_str());
     devOpts_ = {};
     devOpts_.input = device;
     devOpts_.channel = format_.nb_channels;
diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp
index b4ed1e4eb8681512e0afcfc990bab6e5cc0ee365..dddd552bd1e76a5489bc9f720ee4119042c4cf70 100644
--- a/src/media/audio/audiolayer.cpp
+++ b/src/media/audio/audiolayer.cpp
@@ -130,6 +130,7 @@ void
 AudioLayer::hardwareFormatAvailable(AudioFormat playback, size_t 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);
     urgentRingBuffer_.setFormat(audioFormat_);
     nativeFrameSize_ = bufSize;
@@ -150,7 +151,6 @@ AudioLayer::devicesChanged()
 void
 AudioLayer::flushMain()
 {
-    std::lock_guard<std::mutex> lock(mutex_);
     Manager::instance().getRingBufferPool().flushAllBuffers();
 }
 
diff --git a/src/media/audio/pulseaudio/audiostream.cpp b/src/media/audio/pulseaudio/audiostream.cpp
index 576da07c8d570e2a7ce1ad80899e674e82557ad8..ac1f1b571cd726ee052853abf46e4fc62c0a8e42 100644
--- a/src/media/audio/pulseaudio/audiostream.cpp
+++ b/src/media/audio/pulseaudio/audiostream.cpp
@@ -31,29 +31,28 @@ namespace jami {
 AudioStream::AudioStream(pa_context* c,
                          pa_threaded_mainloop* m,
                          const char* desc,
-                         StreamType type,
+                         AudioDeviceType type,
                          unsigned samplrate,
-                         const PaDeviceInfos* infos,
+                         const PaDeviceInfos& infos,
                          bool ec,
-                         OnReady onReady)
+                         OnReady onReady, OnData onData)
     : onReady_(std::move(onReady))
-    , audiostream_(0)
+    , onData_(std::move(onData))
+    , audiostream_(nullptr)
     , mainloop_(m)
 {
-    const pa_channel_map channel_map = infos->channel_map;
-
     pa_sample_spec sample_spec = {PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE,
                                   samplrate,
-                                  channel_map.channels};
+                                  infos.channel_map.channels};
 
-    JAMI_DBG("%s: trying to create stream with device %s (%dHz, %d channels)",
+    JAMI_DBG("%s: Creating stream with device %s (%dHz, %d channels)",
              desc,
-             infos->name.c_str(),
+             infos.name.c_str(),
              samplrate,
-             channel_map.channels);
+             infos.channel_map.channels);
 
     assert(pa_sample_spec_valid(&sample_spec));
-    assert(pa_channel_map_valid(&channel_map));
+    assert(pa_channel_map_valid(&infos.channel_map));
 
     std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl(pa_proplist_new(),
                                                                  pa_proplist_free);
@@ -62,7 +61,7 @@ AudioStream::AudioStream(pa_context* c,
     audiostream_ = pa_stream_new_with_proplist(c,
                                                desc,
                                                &sample_spec,
-                                               &channel_map,
+                                               &infos.channel_map,
                                                ec ? pl.get() : nullptr);
     if (!audiostream_) {
         JAMI_ERR("%s: pa_stream_new() failed : %s", desc, pa_strerror(pa_context_errno(c)));
@@ -85,44 +84,53 @@ AudioStream::AudioStream(pa_context* c,
         [](pa_stream* s, void* user_data) { static_cast<AudioStream*>(user_data)->moved(s); },
         this);
 
-    {
-        PulseMainLoopLock lock(mainloop_);
-        const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
-            PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED);
-
-        if (type == StreamType::Playback || type == StreamType::Ringtone) {
-            pa_stream_connect_playback(audiostream_,
-                                       infos->name.empty() ? nullptr : infos->name.c_str(),
-                                       &attributes,
-                                       flags,
-                                       nullptr,
-                                       nullptr);
-        } else if (type == StreamType::Capture) {
-            pa_stream_connect_record(audiostream_,
-                                     infos->name.empty() ? nullptr : infos->name.c_str(),
-                                     &attributes,
-                                     flags);
-        }
+    constexpr pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
+        PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED);
+
+    if (type == AudioDeviceType::PLAYBACK || type == AudioDeviceType::RINGTONE) {
+        pa_stream_set_write_callback(audiostream_, [](pa_stream* /*s*/, size_t bytes, void* userdata) {
+            static_cast<AudioStream*>(userdata)->onData_(bytes);
+        }, this);
+
+        pa_stream_connect_playback(audiostream_,
+                                    infos.name.empty() ? nullptr : infos.name.c_str(),
+                                    &attributes,
+                                    flags,
+                                    nullptr,
+                                    nullptr);
+    } else if (type == AudioDeviceType::CAPTURE) {
+        pa_stream_set_read_callback(audiostream_, [](pa_stream* /*s*/, size_t bytes, void* userdata) {
+            static_cast<AudioStream*>(userdata)->onData_(bytes);
+        }, this);
+
+        pa_stream_connect_record(audiostream_,
+                                    infos.name.empty() ? nullptr : infos.name.c_str(),
+                                    &attributes,
+                                    flags);
     }
 }
 
-AudioStream::~AudioStream()
-{
-    PulseMainLoopLock lock(mainloop_);
+void disconnectStream(pa_stream* s) {
+    // make sure we don't get any further callback
+    pa_stream_set_write_callback(s, nullptr, nullptr);
+    pa_stream_set_read_callback(s, nullptr, nullptr);
+    pa_stream_set_moved_callback(s, nullptr, nullptr);
+    pa_stream_set_underflow_callback(s, nullptr, nullptr);
+    pa_stream_set_overflow_callback(s, nullptr, nullptr);
+    pa_stream_set_suspended_callback(s, nullptr, nullptr);
+    pa_stream_set_started_callback(s, nullptr, nullptr);
+}
 
-    pa_stream_disconnect(audiostream_);
+void destroyStream(pa_stream* s) {
+    pa_stream_disconnect(s);
+    pa_stream_set_state_callback(s, nullptr, nullptr);
+    disconnectStream(s);
+    pa_stream_unref(s);
+}
 
-    // make sure we don't get any further callback
-    pa_stream_set_state_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_write_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_read_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_moved_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_underflow_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_overflow_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_suspended_callback(audiostream_, nullptr, nullptr);
-    pa_stream_set_started_callback(audiostream_, nullptr, nullptr);
-
-    pa_stream_unref(audiostream_);
+AudioStream::~AudioStream()
+{
+    stop();
 }
 
 void
@@ -131,6 +139,23 @@ AudioStream::start()
     pa_stream_cork(audiostream_, 0, nullptr, nullptr);
 }
 
+void
+AudioStream::stop()
+{
+    if (not audiostream_)
+        return;
+    JAMI_DBG("Destroying stream with device %s", pa_stream_get_device_name(audiostream_));
+    if (pa_stream_get_state(audiostream_) == PA_STREAM_CREATING) {
+        disconnectStream(audiostream_);
+        pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void*){
+            destroyStream(s);
+        }, nullptr);
+    } else {
+        destroyStream(audiostream_);
+    }
+    audiostream_ = nullptr;
+}
+
 void
 AudioStream::moved(pa_stream* s)
 {
@@ -141,7 +166,7 @@ AudioStream::moved(pa_stream* s)
 void
 AudioStream::stateChanged(pa_stream* s)
 {
-    UNUSED char str[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    //UNUSED char str[PA_SAMPLE_SPEC_SNPRINT_MAX];
 
     switch (pa_stream_get_state(s)) {
     case PA_STREAM_CREATING:
diff --git a/src/media/audio/pulseaudio/audiostream.h b/src/media/audio/pulseaudio/audiostream.h
index 2f8e8ff4e7bd7ab2a57aa093cd54aecdbc4257a0..f8aaadbf83c963671c645f3565f594b198cab58e 100644
--- a/src/media/audio/pulseaudio/audiostream.h
+++ b/src/media/audio/pulseaudio/audiostream.h
@@ -28,15 +28,11 @@
 
 namespace jami {
 
-/**
- * This data structure contains the different king of audio streams available
- */
-enum class StreamType { Playback, Capture, Ringtone };
-
 class AudioStream
 {
 public:
     using OnReady = std::function<void()>;
+    using OnData = std::function<void(size_t)>;
 
     /**
      * Constructor
@@ -52,15 +48,16 @@ public:
     AudioStream(pa_context*,
                 pa_threaded_mainloop*,
                 const char*,
-                StreamType,
+                AudioDeviceType,
                 unsigned,
-                const PaDeviceInfos*,
+                const PaDeviceInfos&,
                 bool,
-                OnReady onReady);
+                OnReady onReady, OnData onData);
 
     ~AudioStream();
 
     void start();
+    void stop();
 
     /**
      * Accessor: Get the pulseaudio stream object
@@ -95,6 +92,7 @@ private:
     NON_COPYABLE(AudioStream);
 
     OnReady onReady_;
+    OnData onData_;
 
     /**
      * Mandatory asynchronous callback on the audio stream state
diff --git a/src/media/audio/pulseaudio/pulselayer.cpp b/src/media/audio/pulseaudio/pulselayer.cpp
index b54fa3113077e27eab9a324f0161ec8bd519a667..c3908747f9470da32e848534250996aebaf303af 100644
--- a/src/media/audio/pulseaudio/pulselayer.cpp
+++ b/src/media/audio/pulseaudio/pulselayer.cpp
@@ -359,141 +359,97 @@ PulseLayer::getAudioDeviceName(int index, AudioDeviceType type) const
 }
 
 void
-PulseLayer::createStreams(pa_context* c)
-{
-    hardwareFormatAvailable(defaultAudioFormat_);
-
-    auto onReady = [this] {
-        bool playbackReady = not playback_ or playback_->isReady();
-        bool ringtoneReady = not ringtone_ or ringtone_->isReady();
-        bool recordReady = not record_ or record_->isReady();
-        if (playbackReady and recordReady and ringtoneReady) {
-            JAMI_DBG("All streams ready, starting !");
-            if (playback_)
-                playback_->start();
-            if (ringtone_)
-                ringtone_->start();
-            if (record_)
-                record_->start();
-        }
-    };
-
-    // Create playback stream
-    if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
-        bool ec = preference_.getEchoCanceller() == "system";
-        playback_.reset(new AudioStream(c,
-                                        mainloop_.get(),
-                                        "Playback",
-                                        StreamType::Playback,
-                                        audioFormat_.sample_rate,
-                                        dev_infos,
-                                        ec,
-                                        onReady));
-        pa_stream_set_write_callback(
-            playback_->stream(),
-            [](pa_stream* /*s*/, size_t /*bytes*/, void* userdata) {
-                static_cast<PulseLayer*>(userdata)->writeToSpeaker();
-            },
-            this);
-    }
-
-    // Create ringtone stream
-    // Echo canceling is not enabled for ringtone, because PA can only cancel a single output source
-    // with an input source
-    if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice())) {
-        ringtone_.reset(new AudioStream(c,
-                                        mainloop_.get(),
-                                        "Ringtone",
-                                        StreamType::Ringtone,
-                                        audioFormat_.sample_rate,
-                                        dev_infos,
-                                        false,
-                                        onReady));
-        pa_stream_set_write_callback(
-            ringtone_->stream(),
-            [](pa_stream* /*s*/, size_t /*bytes*/, void* userdata) {
-                static_cast<PulseLayer*>(userdata)->ringtoneToSpeaker();
-            },
-            this);
+PulseLayer::onStreamReady() {
+    if (--pendingStreams == 0) {
+        JAMI_DBG("All streams ready, starting audio");
+        // Flush outside the if statement: every time start stream is
+        // called is to notify a new event
+        flushUrgent();
+        flushMain();
+        if (playback_)
+            playback_->start();
+        if (ringtone_)
+            ringtone_->start();
+        if (record_)
+            record_->start();
     }
+}
 
-    // Create capture stream
-    if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
-        record_.reset(new AudioStream(c,
-                                      mainloop_.get(),
-                                      "Capture",
-                                      StreamType::Capture,
-                                      audioFormat_.sample_rate,
-                                      dev_infos,
-                                      true,
-                                      onReady));
-        pa_stream_set_read_callback(
-            record_->stream(),
-            [](pa_stream* /*s*/, size_t /*bytes*/, void* userdata) {
-                static_cast<PulseLayer*>(userdata)->readFromMic();
-            },
-            this);
+void
+PulseLayer::createStream(std::unique_ptr<AudioStream>& stream, AudioDeviceType type, const PaDeviceInfos& dev_infos, bool ec, std::function<void(size_t)>&& onData)
+{
+    if (stream) {
+        JAMI_WARN("Stream already exists");
+        return;
     }
-
-    pa_threaded_mainloop_signal(mainloop_.get(), 0);
-
-    flushMain();
-    flushUrgent();
+    pendingStreams++;
+    const char* name = type == AudioDeviceType::PLAYBACK ? "Playback" :
+                      (type == AudioDeviceType::CAPTURE  ? "Record" :
+                      (type == AudioDeviceType::RINGTONE ? "Ringtone" : "?"));
+    stream.reset(new AudioStream(context_, mainloop_.get(),
+                                    name,
+                                    type,
+                                    audioFormat_.sample_rate,
+                                    dev_infos,
+                                    ec,
+                                    std::bind(&PulseLayer::onStreamReady, this), std::move(onData)));
 }
 
 void
 PulseLayer::disconnectAudioStream()
 {
+    PulseMainLoopLock lock(mainloop_.get());
     playback_.reset();
     ringtone_.reset();
     record_.reset();
+    pendingStreams = 0;
+    status_ = Status::Idle;
+    startedCv_.notify_all();
 }
 
-void PulseLayer::startStream(AudioDeviceType)
+void PulseLayer::startStream(AudioDeviceType type)
 {
-    std::unique_lock<std::mutex> lk(readyMtx_);
-    readyCv_.wait(lk, [this] {
-        return !(enumeratingSinks_ or enumeratingSources_ or gettingServerInfo_);
-    });
-    if (status_ != Status::Idle)
-        return;
-    status_ = Status::Starting;
+    waitForDevices();
+    PulseMainLoopLock lock(mainloop_.get());
 
     // Create Streams
-    if (!playback_ or !record_)
-        createStreams(context_);
-
-    // Flush outside the if statement: every time start stream is
-    // called is to notify a new event
-    flushUrgent();
-    flushMain();
+    if (type == AudioDeviceType::PLAYBACK) {
+        if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice()))
+            createStream(playback_, type, *dev_infos, true, std::bind(&PulseLayer::writeToSpeaker, this));
+    } else if (type == AudioDeviceType::RINGTONE) {
+        if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice()))
+            createStream(ringtone_, type, *dev_infos, false, std::bind(&PulseLayer::ringtoneToSpeaker, this));
+    } else if (type == AudioDeviceType::CAPTURE) {
+        if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice()))
+            createStream(record_, type, *dev_infos, true, std::bind(&PulseLayer::readFromMic, this));
+    }
+    pa_threaded_mainloop_signal(mainloop_.get(), 0);
 
+    std::lock_guard<std::mutex> lk(mutex_);
     status_ = Status::Started;
     startedCv_.notify_all();
 }
 
 void
-PulseLayer::stopStream(AudioDeviceType stream)
+PulseLayer::stopStream(AudioDeviceType type)
 {
-    std::unique_lock<std::mutex> lk(readyMtx_);
-    readyCv_.wait(lk, [this] {
-        return !(enumeratingSinks_ or enumeratingSources_ or gettingServerInfo_);
-    });
-
-    {
-        PulseMainLoopLock lock(mainloop_.get());
+    waitForDevices();
+    PulseMainLoopLock lock(mainloop_.get());
+    auto& stream(getStream(type));
+    if (not stream)
+        return;
 
-        if (playback_)
-            pa_stream_flush(playback_->stream(), nullptr, nullptr);
+    if (not stream->isReady())
+        pendingStreams--;
+    stream->stop();
+    stream.reset();
 
-        if (record_)
-            pa_stream_flush(record_->stream(), nullptr, nullptr);
+    std::unique_lock<std::mutex> lk(mutex_);
+    if (not playback_ and not ringtone_ and not record_) {
+        pendingStreams = 0;
+        status_ = Status::Idle;
+        startedCv_.notify_all();
     }
-
-    disconnectAudioStream();
-
-    status_ = Status::Idle;
-    startedCv_.notify_all();
 }
 
 void
@@ -628,50 +584,52 @@ PulseLayer::contextChanged(pa_context* c UNUSED,
     }
 }
 
+void
+PulseLayer::waitForDevices()
+{
+    std::unique_lock<std::mutex> lk(readyMtx_);
+    readyCv_.wait(lk, [this] {
+        return !(enumeratingSinks_ or enumeratingSources_ or gettingServerInfo_);
+    });
+}
+
 void
 PulseLayer::waitForDeviceList()
 {
     std::unique_lock<std::mutex> lock(readyMtx_);
-    if (waitingDeviceList_)
+    if (waitingDeviceList_.exchange(true))
         return;
-    waitingDeviceList_ = true;
     if (streamStarter_.joinable())
         streamStarter_.join();
     streamStarter_ = std::thread([this]() mutable {
-        {
-            std::unique_lock<std::mutex> lock(readyMtx_);
-            readyCv_.wait(lock, [&]() {
-                return not enumeratingSources_ and not enumeratingSinks_ and not gettingServerInfo_;
-            });
-        }
-        devicesChanged();
+        bool playbackDeviceChanged, recordDeviceChanged;
+
+        waitForDevices();
         waitingDeviceList_ = false;
-        if (status_ != Status::Started)
-            return;
 
         // If a current device changed, restart streams
+        devicesChanged();
         auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
-        bool playbackDeviceChanged = !playback_
-                                     or (!playbackInfo->name.empty()
-                                         and playbackInfo->name
-                                                 != stripEchoSufix(playback_->getDeviceName()));
+        playbackDeviceChanged = playback_
+                                and (!playbackInfo->name.empty() and playbackInfo->name
+                                            != stripEchoSufix(playback_->getDeviceName()));
 
         auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
-        bool recordDeviceChanged = !record_
-                                   or (!recordInfo->name.empty()
-                                       and recordInfo->name
-                                               != stripEchoSufix(record_->getDeviceName()));
-
-        if (playbackDeviceChanged or recordDeviceChanged) {
-            JAMI_WARN("Audio devices changed, restarting streams.");
-            stopStream();
-            startStream();
-        } else {
-            JAMI_WARN("Staying on \n %s \n %s",
-                      playback_->getDeviceName().c_str(),
-                      record_->getDeviceName().c_str());
-            status_ = Status::Started;
-            startedCv_.notify_all();
+        recordDeviceChanged = record_
+                                and (!recordInfo->name.empty() and recordInfo->name
+                                            != stripEchoSufix(record_->getDeviceName()));
+
+        if (status_ != Status::Started)
+            return;
+        if (playbackDeviceChanged) {
+            JAMI_WARN("Playback devices changed, restarting streams.");
+            stopStream(AudioDeviceType::PLAYBACK);
+            startStream(AudioDeviceType::PLAYBACK);
+        }
+        if (recordDeviceChanged) {
+            JAMI_WARN("Record devices changed, restarting streams.");
+            stopStream(AudioDeviceType::CAPTURE);
+            startStream(AudioDeviceType::CAPTURE);
         }
     });
 }
@@ -697,19 +655,18 @@ PulseLayer::server_info_callback(pa_context*, const pa_server_info* i, void* use
              pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map));
 
     PulseLayer* context = static_cast<PulseLayer*>(userdata);
+    std::lock_guard<std::mutex> lk(context->readyMtx_);
     context->defaultSink_ = {};
     context->defaultSource_ = {};
     context->defaultAudioFormat_ = {i->sample_spec.rate, i->sample_spec.channels};
+    context->hardwareFormatAvailable(context->defaultAudioFormat_);
     if (not context->sinkList_.empty())
         context->sinkList_.front().channel_map.channels = std::min(i->sample_spec.channels,
                                                                    (uint8_t) 2);
     if (not context->sourceList_.empty())
         context->sourceList_.front().channel_map.channels = std::min(i->sample_spec.channels,
                                                                      (uint8_t) 2);
-    {
-        std::lock_guard<std::mutex> lk(context->readyMtx_);
-        context->gettingServerInfo_ = false;
-    }
+    context->gettingServerInfo_ = false;
     context->readyCv_.notify_all();
 }
 
@@ -722,10 +679,8 @@ PulseLayer::source_input_info_callback(pa_context* c UNUSED,
     PulseLayer* context = static_cast<PulseLayer*>(userdata);
 
     if (eol) {
-        {
-            std::lock_guard<std::mutex> lk(context->readyMtx_);
-            context->enumeratingSources_ = false;
-        }
+        std::lock_guard<std::mutex> lk(context->readyMtx_);
+        context->enumeratingSources_ = false;
         context->readyCv_.notify_all();
         return;
     }
@@ -768,12 +723,10 @@ PulseLayer::sink_input_info_callback(pa_context* c UNUSED,
                                      void* userdata)
 {
     PulseLayer* context = static_cast<PulseLayer*>(userdata);
+    std::lock_guard<std::mutex> lk(context->readyMtx_);
 
     if (eol) {
-        {
-            std::lock_guard<std::mutex> lk(context->readyMtx_);
-            context->enumeratingSinks_ = false;
-        }
+        context->enumeratingSinks_ = false;
         context->readyCv_.notify_all();
         return;
     }
@@ -858,28 +811,22 @@ PulseLayer::getIndexRingtone() const
 std::string
 PulseLayer::getPreferredPlaybackDevice() const
 {
-    std::string playbackDevice(preference_.getPulseDevicePlayback());
-    if (playbackDevice.empty())
-        playbackDevice = defaultSink_;
-    return stripEchoSufix(playbackDevice);
+    const std::string& device(preference_.getPulseDevicePlayback());
+    return stripEchoSufix(device.empty() ? defaultSink_ : device);
 }
 
 std::string
 PulseLayer::getPreferredRingtoneDevice() const
 {
-    std::string ringtoneDevice(preference_.getPulseDeviceRingtone());
-    if (ringtoneDevice.empty())
-        ringtoneDevice = defaultSink_;
-    return stripEchoSufix(ringtoneDevice);
+    const std::string& device(preference_.getPulseDeviceRingtone());
+    return stripEchoSufix(device.empty() ? defaultSink_ : device);
 }
 
 std::string
 PulseLayer::getPreferredCaptureDevice() const
 {
-    std::string captureDevice(preference_.getPulseDeviceRecord());
-    if (captureDevice.empty())
-        captureDevice = defaultSource_;
-    return stripEchoSufix(captureDevice);
+    const std::string& device(preference_.getPulseDeviceRecord());
+    return stripEchoSufix(device.empty() ? defaultSource_ : device);
 }
 
 } // namespace jami
diff --git a/src/media/audio/pulseaudio/pulselayer.h b/src/media/audio/pulseaudio/pulselayer.h
index 903b8a2fa10037bdeea5dcb78d93442cda8b9c6a..fe0c95599fbd4d4d22eaa281242e0f806156a908 100644
--- a/src/media/audio/pulseaudio/pulselayer.h
+++ b/src/media/audio/pulseaudio/pulselayer.h
@@ -22,17 +22,17 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#ifndef PULSE_LAYER_H_
-#define PULSE_LAYER_H_
+#pragma once
 
-#include <list>
-#include <string>
-#include <pulse/pulseaudio.h>
-#include <pulse/stream.h>
-#include "audio/audiolayer.h"
 #include "noncopyable.h"
 #include "logger.h"
+#include "audio/audiolayer.h"
 
+#include <pulse/pulseaudio.h>
+#include <pulse/stream.h>
+
+#include <list>
+#include <string>
 #include <memory>
 #include <thread>
 
@@ -167,6 +167,7 @@ private:
     virtual int getIndexPlayback() const;
     virtual int getIndexRingtone() const;
 
+    void waitForDevices();
     void waitForDeviceList();
 
     std::string getPreferredPlaybackDevice() const;
@@ -176,15 +177,27 @@ private:
     NON_COPYABLE(PulseLayer);
 
     /**
-     * Create the audio streams into the given context
-     * @param c	The pulseaudio context
+     * Create the audio stream
      */
-    void createStreams(pa_context* c);
+    void createStream(std::unique_ptr<AudioStream>& stream, AudioDeviceType type, const PaDeviceInfos& dev_infos, bool ec, std::function<void(size_t)>&& onData);
+
+    std::unique_ptr<AudioStream>& getStream(AudioDeviceType type)
+    {
+        if (type == AudioDeviceType::PLAYBACK)
+            return playback_;
+        else if (type == AudioDeviceType::RINGTONE)
+            return ringtone_;
+        else if (type == AudioDeviceType::CAPTURE)
+            return record_;
+        else
+            return playback_;
+    }
 
     /**
      * Close the connection with the local pulseaudio server
      */
     void disconnectAudioStream();
+    void onStreamReady();
 
     /**
      * Returns a pointer to the PaEndpointInfos with the given name in sourceList_, or nullptr if
@@ -193,6 +206,8 @@ private:
     const PaDeviceInfos* getDeviceInfos(const std::vector<PaDeviceInfos>&,
                                         const std::string& name) const;
 
+    std::atomic_uint pendingStreams {0};
+
     /**
      * A stream object to handle the pulseaudio playback stream
      */
@@ -229,7 +244,7 @@ private:
     bool enumeratingSinks_ {false};
     bool enumeratingSources_ {false};
     bool gettingServerInfo_ {false};
-    bool waitingDeviceList_ {false};
+    std::atomic_bool waitingDeviceList_ {false};
     std::mutex readyMtx_ {};
     std::condition_variable readyCv_ {};
     std::thread streamStarter_ {};
@@ -241,5 +256,3 @@ private:
 };
 
 } // namespace jami
-
-#endif // PULSE_LAYER_H_
diff --git a/src/preferences.h b/src/preferences.h
index 6ef611226da84e844507dc8fe963faaf32aea3ad..7d1d1e8a606865c2ef4d44ee7e2fcd84c65f56e3 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -156,7 +156,7 @@ public:
 
     static std::vector<std::string> getSupportedAudioManagers();
 
-    std::string getAudioApi() const { return audioApi_; }
+    const std::string& getAudioApi() const { return audioApi_; }
 
     void setAudioApi(const std::string& api) { audioApi_ = api; }
 
@@ -175,7 +175,7 @@ public:
 
     void setAlsaCardring(int c) { alsaCardring_ = c; }
 
-    std::string getAlsaPlugin() const { return alsaPlugin_; }
+    const std::string& getAlsaPlugin() const { return alsaPlugin_; }
 
     void setAlsaPlugin(const std::string& p) { alsaPlugin_ = p; }
 
@@ -183,19 +183,19 @@ public:
     void setAlsaSmplrate(int r) { alsaSmplrate_ = r; }
 
     // pulseaudio preference
-    std::string getPulseDevicePlayback() const { return pulseDevicePlayback_; }
+    const std::string& getPulseDevicePlayback() const { return pulseDevicePlayback_; }
 
     void setPulseDevicePlayback(const std::string& p) { pulseDevicePlayback_ = p; }
 
-    std::string getPulseDeviceRecord() const { return pulseDeviceRecord_; }
+    const std::string& getPulseDeviceRecord() const { return pulseDeviceRecord_; }
     void setPulseDeviceRecord(const std::string& r) { pulseDeviceRecord_ = r; }
 
-    std::string getPulseDeviceRingtone() const { return pulseDeviceRingtone_; }
+    const std::string& getPulseDeviceRingtone() const { return pulseDeviceRingtone_; }
 
     void setPulseDeviceRingtone(const std::string& r) { pulseDeviceRingtone_ = r; }
 
     // general preference
-    std::string getRecordPath() const { return recordpath_; }
+    const std::string& getRecordPath() const { return recordpath_; }
 
     // Returns true if directory is writeable
     bool setRecordPath(const std::string& r);
@@ -226,7 +226,7 @@ public:
 
     void setPlaybackMuted(bool muted) { playbackMuted_ = muted; }
 
-    std::string getEchoCanceller() const { return echoCanceller_; }
+    const std::string& getEchoCanceller() const { return echoCanceller_; }
 
     void setEchoCanceller(const std::string& ec) { echoCanceller_ = ec; }