diff --git a/src/media/audio/alsa/alsalayer.cpp b/src/media/audio/alsa/alsalayer.cpp
index 928e20cbea97875984f212c2542d34e843820c0d..e0069c659b7e6320164707e17bef6bc84b367bc9 100644
--- a/src/media/audio/alsa/alsalayer.cpp
+++ b/src/media/audio/alsa/alsalayer.cpp
@@ -91,24 +91,25 @@ void AlsaThread::initAudioLayer(void)
     }
 
     if (not alsa_->is_capture_open_) {
-        alsa_->is_capture_open_ = alsa_->openDevice(&alsa_->captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);
+        alsa_->is_capture_open_ = alsa_->openDevice(&alsa_->captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE, alsa_->audioInputFormat_);
 
         if (not alsa_->is_capture_open_)
             emitSignal<DRing::ConfigurationSignal::Error>(ALSA_CAPTURE_DEVICE);
     }
 
     if (not alsa_->is_playback_open_) {
-        alsa_->is_playback_open_ = alsa_->openDevice(&alsa_->playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK);
+        alsa_->is_playback_open_ = alsa_->openDevice(&alsa_->playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK, alsa_->audioFormat_);
 
         if (not alsa_->is_playback_open_)
             emitSignal<DRing::ConfigurationSignal::Error>(ALSA_PLAYBACK_DEVICE);
 
         if (alsa_->getIndexPlayback() != alsa_->getIndexRingtone())
-            if (!alsa_->openDevice(&alsa_->ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK))
+            if (!alsa_->openDevice(&alsa_->ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK, alsa_->audioFormat_))
                 emitSignal<DRing::ConfigurationSignal::Error>(ALSA_PLAYBACK_DEVICE);
     }
 
     alsa_->hardwareFormatAvailable(alsa_->getFormat());
+    alsa_->hardwareInputFormatAvailable(alsa_->audioInputFormat_);
 
     alsa_->prepareCaptureStream();
     alsa_->preparePlaybackStream();
@@ -172,7 +173,7 @@ AlsaLayer::~AlsaLayer()
 }
 
 // Retry approach taken from pa_linux_alsa.c, part of PortAudio
-bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
+bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream, AudioFormat& format)
 {
     RING_DBG("Alsa: Opening %s",  dev.c_str());
 
@@ -199,7 +200,7 @@ bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stre
         return false;
     }
 
-    if (!alsa_set_params(*pcm)) {
+    if (!alsa_set_params(*pcm, format)) {
         snd_pcm_close(*pcm);
         return false;
     }
@@ -334,7 +335,7 @@ void AlsaLayer::preparePlaybackStream()
     is_playback_prepared_ = true;
 }
 
-bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
+bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle, AudioFormat& format)
 {
 #define TRY(call, error) do { \
     if (ALSA_CALL(call, error) < 0) \
@@ -364,11 +365,12 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
     TRY(snd_pcm_hw_params_set_format(HW, SND_PCM_FORMAT_S16_LE), "sample format");
 
     TRY(snd_pcm_hw_params_set_rate_resample(HW, 0), "hardware sample rate"); /* prevent software resampling */
-    TRY(snd_pcm_hw_params_set_rate_near(HW, &audioFormat_.sample_rate, nullptr), "sample rate");
+    TRY(snd_pcm_hw_params_set_rate_near(HW, &format.sample_rate, nullptr), "sample rate");
 
     // TODO: use snd_pcm_query_chmaps or similar to get hardware channel num
     audioFormat_.nb_channels = 2;
-    TRY(snd_pcm_hw_params_set_channels_near(HW, &audioFormat_.nb_channels), "channel count");
+    format.nb_channels = 2;
+    TRY(snd_pcm_hw_params_set_channels_near(HW, &format.nb_channels), "channel count");
 
     snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
     snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
@@ -388,8 +390,8 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
 
     snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
     snd_pcm_hw_params_get_period_size(hwparams, &period_size, nullptr);
-    snd_pcm_hw_params_get_rate(hwparams, &audioFormat_.sample_rate, nullptr);
-    snd_pcm_hw_params_get_channels(hwparams, &audioFormat_.nb_channels);
+    snd_pcm_hw_params_get_rate(hwparams, &format.sample_rate, nullptr);
+    snd_pcm_hw_params_get_channels(hwparams, &format.nb_channels);
     RING_DBG("Was set period_size = %lu", period_size);
     RING_DBG("Was set buffer_size = %lu", buffer_size);
 
@@ -402,7 +404,7 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
 
     RING_DBG("%s using format %s",
           (snd_pcm_stream(pcm_handle) == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
-          audioFormat_.toString().c_str() );
+          format.toString().c_str() );
 
     snd_pcm_sw_params_t *swparams = nullptr;
     snd_pcm_sw_params_alloca(&swparams);
@@ -421,13 +423,9 @@ bool AlsaLayer::alsa_set_params(snd_pcm_t *pcm_handle)
 // TODO first frame causes broken pipe (underrun) because not enough data is sent
 // we should wait until the handle is ready
 void
-AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
+AlsaLayer::write(const AudioFrame& buffer, snd_pcm_t * handle)
 {
-    // Skip empty buffers
-    if (!frames)
-        return;
-
-    int err = snd_pcm_writei(handle, (const void*)buffer, frames);
+    int err = snd_pcm_writei(handle, (const void*)buffer.pointer()->data[0], buffer.pointer()->nb_samples);
 
     if (err < 0)
         snd_pcm_recover(handle, err, 0);
@@ -450,7 +448,7 @@ AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
                     startPlaybackStream();
                 }
 
-            ALSA_CALL(snd_pcm_writei(handle, (const void*)buffer, frames), "XRUN handling failed");
+            ALSA_CALL(snd_pcm_writei(handle, (const void*)buffer.pointer()->data[0], buffer.pointer()->nb_samples), "XRUN handling failed");
             break;
         }
 
@@ -481,18 +479,21 @@ AlsaLayer::write(AudioSample* buffer, int frames, snd_pcm_t * handle)
     }
 }
 
-int
-AlsaLayer::read(AudioSample* buffer, int frames)
+std::unique_ptr<AudioFrame>
+AlsaLayer::read(unsigned frames)
 {
     if (snd_pcm_state(captureHandle_) == SND_PCM_STATE_XRUN) {
         prepareCaptureStream();
         startCaptureStream();
     }
 
-    int err = snd_pcm_readi(captureHandle_, (void*)buffer, frames);
+    auto ret = std::make_unique<AudioFrame>(audioInputFormat_, frames);
+    int err = snd_pcm_readi(captureHandle_, ret->pointer()->data[0], frames);
 
-    if (err >= 0)
-        return err;
+    if (err >= 0) {
+        ret->pointer()->nb_samples = err;
+        return ret;
+    }
 
     switch (err) {
         case -EPIPE:
@@ -537,7 +538,7 @@ AlsaLayer::buildDeviceTopo(const std::string &plugin, int card)
 }
 
 static bool
-safeUpdate(snd_pcm_t *handle, int &samples)
+safeUpdate(snd_pcm_t *handle, long &samples)
 {
     samples = snd_pcm_avail_update(handle);
 
@@ -557,6 +558,7 @@ static std::vector<std::string>
 getValues(const std::vector<HwIDPair> &deviceMap)
 {
     std::vector<std::string> audioDeviceList;
+    audioDeviceList.reserve(deviceMap.size());
 
     for (const auto & dev : deviceMap)
         audioDeviceList.push_back(dev.second);
@@ -634,18 +636,14 @@ AlsaLayer::getAudioDeviceIndexMap(bool getCapture) const
 bool
 AlsaLayer::soundCardIndexExists(int card, DeviceType stream)
 {
-    snd_pcm_info_t *pcminfo;
-    snd_pcm_info_alloca(&pcminfo);
-    std::string name("hw:");
-    std::stringstream ss;
-    ss << card;
-    name.append(ss.str());
+    const std::string name("hw:" + std::to_string(card));
 
     snd_ctl_t* handle;
-
     if (snd_ctl_open(&handle, name.c_str(), 0) != 0)
         return false;
 
+    snd_pcm_info_t* pcminfo;
+    snd_pcm_info_alloca(&pcminfo);
     snd_pcm_info_set_stream(pcminfo, stream == DeviceType::PLAYBACK ?  SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE);
     bool ret = snd_ctl_pcm_info(handle, pcminfo) >= 0;
     snd_ctl_close(handle);
@@ -690,65 +688,36 @@ void AlsaLayer::capture()
     if (!captureHandle_ or !is_capture_running_)
         return;
 
-    AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-
     int toGetFrames = snd_pcm_avail_update(captureHandle_);
-
     if (toGetFrames < 0)
         RING_ERR("Audio: Mic error: %s", snd_strerror(toGetFrames));
-
     if (toGetFrames <= 0)
         return;
 
     const int framesPerBufferAlsa = 2048;
     toGetFrames = std::min(framesPerBufferAlsa, toGetFrames);
-    captureIBuff_.resize(toGetFrames * audioFormat_.nb_channels);
-
-    if (read(captureIBuff_.data(), toGetFrames) != toGetFrames) {
+    if (auto r = read(toGetFrames)) {
+        //captureBuff_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
+        //dcblocker_.process(captureBuff_);
+        mainRingBuffer_->put(std::move(r));
+    } else
         RING_ERR("ALSA MIC : Couldn't read!");
-        return;
-    }
-
-    captureBuff_.deinterleave(captureIBuff_, audioFormat_);
-    captureBuff_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
-
-    if (audioFormat_.nb_channels != mainBufferFormat.nb_channels) {
-        captureBuff_.setChannelNum(mainBufferFormat.nb_channels, true);
-    }
-    if (audioFormat_.sample_rate != mainBufferFormat.sample_rate) {
-        int outFrames = toGetFrames * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
-        AudioBuffer rsmpl_in(outFrames, mainBufferFormat);
-        resampler_->resample(captureBuff_, rsmpl_in);
-        dcblocker_.process(rsmpl_in);
-        mainRingBuffer_->put(rsmpl_in);
-    } else {
-        dcblocker_.process(captureBuff_);
-        mainRingBuffer_->put(captureBuff_);
-    }
 }
 
 void AlsaLayer::playback()
 {
-
     if (!playbackHandle_)
         return;
 
     snd_pcm_wait(playbackHandle_, 20);
 
-    int maxFrames = 0;
-
+    long maxFrames = 0;
     if (not safeUpdate(playbackHandle_, maxFrames))
         return;
 
-    auto& ringBuff = getToRing(audioFormat_, maxFrames);
-    auto& playBuff = getToPlay(audioFormat_, maxFrames);
-    auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
-
-    if (!(toPlay.frames() > 0))
-        return;
-
-    toPlay.interleave(playbackIBuff_);
-    write(playbackIBuff_.data(), toPlay.frames(), playbackHandle_);
+    if (auto toPlay = getToPlay(audioFormat_, maxFrames)) {
+        write(*toPlay, playbackHandle_);
+    }
 }
 
 void AlsaLayer::ringtone()
@@ -756,22 +725,13 @@ void AlsaLayer::ringtone()
     if (!ringtoneHandle_)
         return;
 
-    auto file_tone = Manager::instance().getTelephoneFile();
-    int ringtoneAvailFrames = 0;
-
+    long ringtoneAvailFrames = 0;
     if (not safeUpdate(ringtoneHandle_, ringtoneAvailFrames))
         return;
 
-    playbackBuff_.setFormat(audioFormat_);
-    playbackBuff_.resize(ringtoneAvailFrames);
-
-    if (file_tone) {
-        RING_DBG("playback gain %.3f", playbackGain_);
-        file_tone->getNext(playbackBuff_, playbackGain_);
+    if (auto toRing = getToRing(audioFormat_, ringtoneAvailFrames)) {
+        write(*toRing, ringtoneHandle_);
     }
-
-    playbackBuff_.interleave(playbackIBuff_);
-    write(playbackIBuff_.data(), ringtoneAvailFrames, ringtoneHandle_);
 }
 
 void AlsaLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
diff --git a/src/media/audio/alsa/alsalayer.h b/src/media/audio/alsa/alsalayer.h
index 795929a648268ff6b11753d5c8d3679242840cdb..4d61007843e64dbb9f4c7074f6ac6641d17cce8d 100644
--- a/src/media/audio/alsa/alsalayer.h
+++ b/src/media/audio/alsa/alsalayer.h
@@ -154,7 +154,7 @@ class AlsaLayer : public AudioLayer {
          * will often hold on to a device temporarily after it has been opened
          * and closed.
          */
-        bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream);
+        bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream, AudioFormat& format);
 
         /**
          * Number of audio cards on which capture stream has been opened
@@ -187,7 +187,7 @@ class AlsaLayer : public AudioLayer {
         void startPlaybackStream();
         void preparePlaybackStream();
 
-        bool alsa_set_params(snd_pcm_t *pcm_handle);
+        bool alsa_set_params(snd_pcm_t *pcm_handle, AudioFormat& format);
 
         /**
          * Copy a data buffer in the internal ring buffer
@@ -195,7 +195,7 @@ class AlsaLayer : public AudioLayer {
          * @param buffer The non-interleaved data to be copied
          * @param frames Frames in the buffer
          */
-        void write(AudioSample* buffer, int frames, snd_pcm_t *handle);
+        void write(const AudioFrame& buffer, snd_pcm_t *handle);
 
         /**
          * Read data from the internal ring buffer
@@ -204,7 +204,7 @@ class AlsaLayer : public AudioLayer {
          * @param frames  The number of frames to get
          * @return int The number of frames actually read
          */
-        int read(AudioSample* buffer, int frames);
+        std::unique_ptr<AudioFrame> read(unsigned frames);
 
         virtual void updatePreference(AudioPreference &pref, int index, DeviceType type);
 
diff --git a/src/media/audio/audio_frame_resizer.cpp b/src/media/audio/audio_frame_resizer.cpp
index 429bef071570c531bac388dc6c658c6ad6f7a3d8..8766d81255dc3682c8d6712c8203e15c45ee624f 100644
--- a/src/media/audio/audio_frame_resizer.cpp
+++ b/src/media/audio/audio_frame_resizer.cpp
@@ -64,8 +64,8 @@ void
 AudioFrameResizer::setFormat(const AudioFormat& format, int size)
 {
     if (format != format_) {
-        if (auto discaded = samples())
-            RING_WARN("Discarding %d samples", discaded);
+        if (auto discarded = samples())
+            RING_WARN("Discarding %d samples", discarded);
         av_audio_fifo_free(queue_);
         format_ = format;
         queue_ = av_audio_fifo_alloc(format.sampleFormat, format.nb_channels, frameSize_);
diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp
index 517372bf6119b1628b35abc4f133fc5ca11526ca..a3f397fec79af7b54848a8c61f18c7d35443a187 100644
--- a/src/media/audio/audio_input.cpp
+++ b/src/media/audio/audio_input.cpp
@@ -92,36 +92,22 @@ AudioInput::nextFromDevice()
     auto& mainBuffer = Manager::instance().getRingBufferPool();
     auto bufferFormat = mainBuffer.getInternalAudioFormat();
 
-    // compute number of samples contained in a frame with duration MS_PER_PACKET
-    const auto samplesPerPacket = MS_PER_PACKET * bufferFormat.sample_rate;
-    const std::size_t samplesToGet = std::chrono::duration_cast<std::chrono::seconds>(samplesPerPacket).count();
-
-    if (mainBuffer.availableForGet(id_) < samplesToGet
-        && not mainBuffer.waitForDataAvailable(id_, samplesToGet, MS_PER_PACKET)) {
+    if (not mainBuffer.waitForDataAvailable(id_, MS_PER_PACKET)) {
         return;
     }
 
-    // getData resets the format to internal hardware format, will have to be resampled
-    micData_.setFormat(bufferFormat);
-    micData_.resize(samplesToGet);
-    const auto samples = mainBuffer.getData(micData_, id_);
-    if (samples != samplesToGet)
+    auto samples = mainBuffer.getData(id_);
+    if (not samples)
         return;
 
-    if (muteState_) // audio is muted, set samples to 0
-        micData_.reset();
+    //if (muteState_) // audio is muted, set samples to 0
+    //    micData_.reset();
+    // TODO handle mute
 
-    std::lock_guard<std::mutex> lk(fmtMutex_);
-    AudioBuffer resampled;
-    resampled.setFormat(format_);
-    if (bufferFormat != format_) {
-        resampler_->resample(micData_, resampled);
-    } else {
-        resampled = micData_;
+    {
+        std::lock_guard<std::mutex> lk(fmtMutex_);
+        resizer_->enqueue(resampler_->resample(std::move(samples), format_));
     }
-
-    auto audioFrame = resampled.toAVFrame();
-    resizer_->enqueue(std::move(audioFrame));
 }
 
 void
@@ -145,16 +131,7 @@ AudioInput::nextFromFile()
         createDecoder();
         break;
     case MediaDecoder::Status::FrameFinished:
-        if (inFmt != format_) {
-            AudioFrame out;
-            out.pointer()->format = format_.sampleFormat;
-            out.pointer()->sample_rate = format_.sample_rate;
-            out.pointer()->channel_layout = av_get_default_channel_layout(format_.nb_channels);
-            out.pointer()->channels = format_.nb_channels;
-            resampler_->resample(frame->pointer(), out.pointer());
-            frame->copyFrom(out);
-        }
-        resizer_->enqueue(std::move(frame));
+        resizer_->enqueue(resampler_->resample(std::move(frame), format_));
         break;
     case MediaDecoder::Status::Success:
     default:
@@ -298,9 +275,7 @@ AudioInput::setFormat(const AudioFormat& fmt)
 {
     std::lock_guard<std::mutex> lk(fmtMutex_);
     format_ = fmt;
-    frameSize_ = format_.sample_rate * MS_PER_PACKET.count() / 1000;
-    resizer_.reset(new AudioFrameResizer(format_, frameSize_,
-       [this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); }));
+    resizer_->setFormat(format_, format_.sample_rate * MS_PER_PACKET.count() / 1000);
 }
 
 void
diff --git a/src/media/audio/audio_receive_thread.cpp b/src/media/audio/audio_receive_thread.cpp
index 9865b5a357c0844013373606620c539f74cb7f6d..7fd31bb2c7abba68fa3319a54937427a451fd551 100644
--- a/src/media/audio/audio_receive_thread.cpp
+++ b/src/media/audio/audio_receive_thread.cpp
@@ -93,12 +93,13 @@ void
 AudioReceiveThread::process()
 {
     AudioFormat mainBuffFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    auto decodedFrame = std::make_shared<AudioFrame>();
+    auto decodedFrame = std::make_unique<AudioFrame>();
+    auto sharedFrame = std::make_shared<AudioFrame>();
     switch (audioDecoder_->decode(*decodedFrame)) {
         case MediaDecoder::Status::FrameFinished:
-            audioDecoder_->writeToRingBuffer(*decodedFrame, *ringbuffer_,
-                                             mainBuffFormat);
-            notify(std::static_pointer_cast<MediaFrame>(decodedFrame));
+            sharedFrame->copyFrom(*decodedFrame);
+            audioDecoder_->writeToRingBuffer(std::move(decodedFrame), *ringbuffer_, mainBuffFormat);
+            notify(std::static_pointer_cast<MediaFrame>(sharedFrame));
             return;
         case MediaDecoder::Status::DecodeError:
             RING_WARN("decoding failure, trying to reset decoder...");
diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp
index 58ad403a86280a5efb3c75dbd33df35f42cccafd..6e3326788dee82cc0c0d9de17962f48635c092fe 100644
--- a/src/media/audio/audio_rtp_session.cpp
+++ b/src/media/audio/audio_rtp_session.cpp
@@ -48,8 +48,6 @@
 
 namespace ring {
 
-constexpr static auto NEWPARAMS_TIMEOUT = std::chrono::milliseconds(1000);
-
 AudioRtpSession::AudioRtpSession(const std::string& id)
     : RtpSession(id)
 {
diff --git a/src/media/audio/audiobuffer.h b/src/media/audio/audiobuffer.h
index a79f4cfe4b74524eb51454445c6a53c32c63c67d..10353a08b2d0c3fcaa246488000b3239316e4e6c 100644
--- a/src/media/audio/audiobuffer.h
+++ b/src/media/audio/audiobuffer.h
@@ -96,10 +96,10 @@ struct AudioFormat {
     }
 
     static const constexpr unsigned DEFAULT_SAMPLE_RATE = 48000;
-    static const AudioFormat DEFAULT() { return AudioFormat{16000, 1}; }
-    static const AudioFormat NONE() { return AudioFormat{0, 0}; }
-    static const AudioFormat MONO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 1}; }
-    static const AudioFormat STEREO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 2}; }
+    static const constexpr AudioFormat DEFAULT() { return AudioFormat{16000, 1}; }
+    static const constexpr AudioFormat NONE() { return AudioFormat{0, 0}; }
+    static const constexpr AudioFormat MONO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 1}; }
+    static const constexpr AudioFormat STEREO() { return AudioFormat{DEFAULT_SAMPLE_RATE, 2}; }
 };
 
 std::ostream& operator <<(std::ostream& stream, const AudioFormat& f);
diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp
index 63759fbf58f6e2977b4cbe16b569ab8b54cd7cfe..82383161a88ac17f4b29c9c59f567e847d83158c 100644
--- a/src/media/audio/audiolayer.cpp
+++ b/src/media/audio/audiolayer.cpp
@@ -56,7 +56,7 @@ void AudioLayer::hardwareFormatAvailable(AudioFormat playback)
     std::lock_guard<std::mutex> lock(mutex_);
     RING_DBG("Hardware audio format available : %s", playback.toString().c_str());
     audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback);
-    urgentRingBuffer_.setFormat(audioFormat_);
+    urgentRingBuffer_.setFormat(playback);
 }
 
 void AudioLayer::hardwareInputFormatAvailable(AudioFormat capture)
@@ -85,7 +85,7 @@ void AudioLayer::flushUrgent()
 void AudioLayer::putUrgent(AudioBuffer& buffer)
 {
     std::lock_guard<std::mutex> lock(mutex_);
-    urgentRingBuffer_.put(buffer);
+    urgentRingBuffer_.put(buffer.toAVFrame());
 }
 
 // Notify (with a beep) an incoming call when there is already a call in progress
@@ -107,7 +107,7 @@ void AudioLayer::notifyIncomingCall()
         return;
 
     Tone tone("440/160", getSampleRate());
-    unsigned int nbSample = tone.getSize();
+    size_t nbSample = tone.getSize();
     AudioBuffer buf(nbSample, AudioFormat::MONO());
     tone.getNext(buf, 1.0);
 
@@ -116,99 +116,56 @@ void AudioLayer::notifyIncomingCall()
     putUrgent(buf);
 }
 
-
-const AudioBuffer& AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
+std::shared_ptr<AudioFrame>
+AudioLayer::getToRing(AudioFormat format, size_t writableSamples)
 {
     ringtoneBuffer_.resize(0);
-    auto fileToPlay = Manager::instance().getTelephoneFile();
-    if (fileToPlay) {
+    if (auto fileToPlay = Manager::instance().getTelephoneFile()) {
         auto fileformat = fileToPlay->getFormat();
-        bool resample = format.sample_rate != fileformat.sample_rate;
+        bool resample = format != fileformat;
 
         size_t readableSamples = resample
-                ? fileformat.sample_rate * (double) writableSamples / (double) audioFormat_.sample_rate
+                ? (rational<size_t>(writableSamples, audioFormat_.sample_rate) * (size_t)fileformat.sample_rate).real<size_t>()
                 : writableSamples;
 
         ringtoneBuffer_.setFormat(fileformat);
         ringtoneBuffer_.resize(readableSamples);
         fileToPlay->getNext(ringtoneBuffer_, isRingtoneMuted_ ? 0. : 1.);
-        ringtoneBuffer_.setChannelNum(format.nb_channels, true);
-        AudioBuffer* out;
-        if (resample) {
-            ringtoneResampleBuffer_.setSampleRate(format.sample_rate);
-            resampler_->resample(ringtoneBuffer_, ringtoneResampleBuffer_);
-            out = &ringtoneResampleBuffer_;
-        } else {
-            out = &ringtoneBuffer_;
-        }
-        return *out;
+        return resampler_->resample(ringtoneBuffer_.toAVFrame(), format);
     }
-    return ringtoneBuffer_;
+    return {};
 }
 
-const AudioBuffer& AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
+std::shared_ptr<AudioFrame>
+AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
 {
-    playbackBuffer_.resize(0);
-    playbackResampleBuffer_.resize(0);
-
     notifyIncomingCall();
+    auto& bufferPool = Manager::instance().getRingBufferPool();
 
-    size_t urgentSamples = std::min(urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID), writableSamples);
-
-    if (urgentSamples) {
-        playbackBuffer_.setFormat(format);
-        playbackBuffer_.resize(urgentSamples);
-        urgentRingBuffer_.get(playbackBuffer_, RingBufferPool::DEFAULT_ID); // retrive only the first sample_spec->channels channels
-        playbackBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-        // Consume the regular one as well (same amount of samples)
-        Manager::instance().getRingBufferPool().discard(urgentSamples, RingBufferPool::DEFAULT_ID);
-        return playbackBuffer_;
-    }
-
-    if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
-        playbackBuffer_.setFormat(format);
-        playbackBuffer_.resize(writableSamples);
-        toneToPlay->getNext(playbackBuffer_, playbackGain_); // retrive only n_channels
-        return playbackBuffer_;
+    if (auto urgentSamples = urgentRingBuffer_.get(RingBufferPool::DEFAULT_ID)) {
+        bufferPool.discard(1, RingBufferPool::DEFAULT_ID);
+        return urgentSamples;
     }
+    // flush remaining samples in _urgentRingBuffer
+    flushUrgent();
 
-    flushUrgent(); // flush remaining samples in _urgentRingBuffer
-
-    size_t availSamples = Manager::instance().getRingBufferPool().availableForGet(RingBufferPool::DEFAULT_ID);
-    if (not availSamples)
-        return playbackBuffer_;
-
-    // how many samples we want to read from the buffer
-    size_t readableSamples = writableSamples;
-
-    AudioFormat mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-
-    bool resample = audioFormat_.sample_rate != mainBufferAudioFormat.sample_rate;
-    double resampleFactor = 1.;
-    if (resample) {
-        resampleFactor = (double) audioFormat_.sample_rate / mainBufferAudioFormat.sample_rate;
-        readableSamples = (double) readableSamples / resampleFactor;
+    if (not playbackQueue_)
+        playbackQueue_.reset(new AudioFrameResizer(format, writableSamples));
+    else
+        playbackQueue_->setFrameSize(writableSamples);
+
+    std::shared_ptr<AudioFrame> playbackBuf {};
+    while (!(playbackBuf = playbackQueue_->dequeue())) {
+        if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
+            playbackQueue_->enqueue(resampler_->resample(toneToPlay->getNext(), format));
+        } else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
+            playbackQueue_->enqueue(resampler_->resample(std::move(buf), format));
+        } else {
+            break;
+        }
     }
 
-    readableSamples = std::min(readableSamples, availSamples);
-    size_t nResampled = (double) readableSamples * resampleFactor;
-
-    playbackBuffer_.setFormat(mainBufferAudioFormat);
-    playbackBuffer_.resize(readableSamples);
-    Manager::instance().getRingBufferPool().getData(playbackBuffer_, RingBufferPool::DEFAULT_ID);
-    playbackBuffer_.setChannelNum(format.nb_channels, true);
-
-    if (resample) {
-        playbackResampleBuffer_.setFormat(format);
-        playbackResampleBuffer_.resize(nResampled);
-        resampler_->resample(playbackBuffer_, playbackResampleBuffer_);
-        playbackResampleBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-        return playbackResampleBuffer_;
-    } else {
-        playbackBuffer_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-        return playbackBuffer_;
-    }
+    return playbackBuf;
 }
 
-
 } // namespace ring
diff --git a/src/media/audio/audiolayer.h b/src/media/audio/audiolayer.h
index 181885d49385fadfb3626dc2e0beaff7110ea732..10da826ac3a503b19033f6b8915cf71cdc2039c6 100644
--- a/src/media/audio/audiolayer.h
+++ b/src/media/audio/audiolayer.h
@@ -28,6 +28,7 @@
 #include "ringbuffer.h"
 #include "dcblocker.h"
 #include "noncopyable.h"
+#include "audio_frame_resizer.h"
 
 #include <chrono>
 #include <mutex>
@@ -228,9 +229,15 @@ class AudioLayer {
 
         void devicesChanged();
 
-        const AudioBuffer& getToPlay(AudioFormat format, size_t writableSamples);
+        std::shared_ptr<AudioFrame> getToPlay(AudioFormat format, size_t writableSamples);
 
-        const AudioBuffer& getToRing(AudioFormat format, size_t writableSamples);
+        std::shared_ptr<AudioFrame> getToRing(AudioFormat format, size_t writableSamples);
+
+        std::shared_ptr<AudioFrame> getPlayback(AudioFormat format, size_t samples) {
+            const auto& ringBuff = getToRing(format, samples);
+            const auto& playBuff = getToPlay(format, samples);
+            return ringBuff ? ringBuff : playBuff;
+        }
 
         /**
          * True if capture is not to be used
@@ -264,6 +271,7 @@ class AudioLayer {
         AudioBuffer playbackResampleBuffer_;
         AudioBuffer ringtoneBuffer_;
         AudioBuffer ringtoneResampleBuffer_;
+        std::unique_ptr<AudioFrameResizer> playbackQueue_;
 
         /**
          * Whether or not the audio layer stream is started
diff --git a/src/media/audio/audioloop.cpp b/src/media/audio/audioloop.cpp
index 395e6bf67e73c22ed53ea6af1761d172344c6dbc..9998095c98430a6505cd55066b2b1a79cf9b61fd 100644
--- a/src/media/audio/audioloop.cpp
+++ b/src/media/audio/audioloop.cpp
@@ -71,22 +71,28 @@ AudioLoop::getNext(AudioBuffer& output, double gain)
 
     while (total_samples != 0) {
         size_t samples = std::min(total_samples, buf_samples - pos);
-
         output.copy(*buffer_, samples, pos, output_pos);
-
         output_pos += samples;
         pos = (pos + samples) % buf_samples;
-
         total_samples -= samples;
     }
 
     output.applyGain(gain);
-
     pos_ = pos;
-
     onBufferFinish();
 }
 
 void AudioLoop::onBufferFinish() {}
 
+std::unique_ptr<AudioFrame>
+AudioLoop::getNext(size_t samples)
+{
+    if (samples == 0) {
+        samples = buffer_->getSampleRate() / 50;
+    }
+    AudioBuffer buff(samples, buffer_->getFormat());
+    getNext(buff, 1);
+    return buff.toAVFrame();
+}
+
 } // namespace ring
diff --git a/src/media/audio/audioloop.h b/src/media/audio/audioloop.h
index ef0b2ae13b110a73fa340845ecb553d7b149d102..eff5388d4ec380d04d2f833006a8077af3544587 100644
--- a/src/media/audio/audioloop.h
+++ b/src/media/audio/audioloop.h
@@ -56,6 +56,7 @@ class AudioLoop {
          * @param gain The gain [-1.0, 1.0]
          */
         void getNext(AudioBuffer& output, double gain);
+        std::unique_ptr<AudioFrame> getNext(size_t samples = 0);
 
         void seek(double relative_position);
 
diff --git a/src/media/audio/coreaudio/ios/corelayer.cpp b/src/media/audio/coreaudio/ios/corelayer.cpp
index fe826a7b655b422af8d7f663d802d5df3d542ea2..f0b1e0ace59c50bcbcc5249aa5a18a90fe787966 100644
--- a/src/media/audio/coreaudio/ios/corelayer.cpp
+++ b/src/media/audio/coreaudio/ios/corelayer.cpp
@@ -156,9 +156,7 @@ CoreLayer::setupOutputBus() {
                             &size,
                             &outSampleRate);
     outputASBD.mSampleRate = outSampleRate;
-
-    audioFormat_ = {static_cast<unsigned int>(outputASBD.mSampleRate),
-                    static_cast<unsigned int>(outputASBD.mChannelsPerFrame)};
+    outSampleRate_ = outputASBD.mSampleRate;
 
     size = sizeof(outputASBD);
     checkErr(AudioUnitGetProperty(ioUnit_,
@@ -169,7 +167,7 @@ CoreLayer::setupOutputBus() {
                                   &size));
 
     // Only change sample rate.
-    outputASBD.mSampleRate = audioFormat_.sample_rate;
+    outputASBD.mSampleRate = outSampleRate_;
     outputASBD.mFormatID = kAudioFormatLinearPCM;
     outputASBD.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
 
@@ -184,7 +182,8 @@ CoreLayer::setupOutputBus() {
                                   &outputASBD,
                                   size));
 
-    hardwareFormatAvailable(audioFormat_);
+    hardwareFormatAvailable({static_cast<unsigned int>(outputASBD.mSampleRate),
+                            static_cast<unsigned int>(outputASBD.mChannelsPerFrame)});
 }
 
 void
@@ -223,13 +222,6 @@ CoreLayer::setupInputBus() {
     inputASBD.mFormatID = kAudioFormatLinearPCM;
     inputASBD.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
 
-    audioInputFormat_ = {static_cast<unsigned int>(inputASBD.mSampleRate),
-                         static_cast<unsigned int>(inputASBD.mChannelsPerFrame)};
-    hardwareInputFormatAvailable(audioInputFormat_);
-
-    // Keep some values to not ask them every time the read callback is fired up
-    inSampleRate_ = inputASBD.mSampleRate;
-    inChannelsPerFrame_ = inputASBD.mChannelsPerFrame;
 
     // Set format on output *SCOPE* in input *BUS*.
     checkErr(AudioUnitGetProperty(ioUnit_,
@@ -239,8 +231,13 @@ CoreLayer::setupInputBus() {
                                   &inputASBD,
                                   &size));
 
-    // Keep everything else and change only sample rate (or else SPLOSION!!!)
-    inputASBD.mSampleRate = audioInputFormat_.sample_rate;
+    audioInputFormat_ = {static_cast<unsigned int>(inputASBD.mSampleRate),
+                         static_cast<unsigned int>(inputASBD.mChannelsPerFrame)};
+    hardwareInputFormatAvailable(audioInputFormat_);
+
+    // Keep some values to not ask them every time the read callback is fired up
+    inSampleRate_ = inputASBD.mSampleRate;
+    inChannelsPerFrame_ = inputASBD.mChannelsPerFrame;
 
     size = sizeof(inputASBD);
     checkErr(AudioUnitSetProperty(ioUnit_,
@@ -265,7 +262,7 @@ CoreLayer::setupInputBus() {
     AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
                             &size,
                             &bufferDuration);
-    UInt32 bufferSizeFrames = audioInputFormat_.sample_rate * bufferDuration;
+    UInt32 bufferSizeFrames = inSampleRate_ * bufferDuration;
     UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
     size = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * inputASBD.mChannelsPerFrame);
     rawBuff_.reset(new Byte[size + bufferSizeBytes * inputASBD.mChannelsPerFrame]);
@@ -380,73 +377,14 @@ CoreLayer::write(AudioUnitRenderActionFlags* ioActionFlags,
     (void) inTimeStamp;
     (void) inBusNumber;
 
-    auto& manager = Manager::instance();
-    auto& bufferPool = manager.getRingBufferPool();
-
-    auto mainBufferFormat = bufferPool.getInternalAudioFormat();
-    const AudioFormat currentOutFormat = {  static_cast<unsigned int>(outSampleRate_),
-                                            static_cast<unsigned int>(outChannelsPerFrame_)};
-
-    auto resample = currentOutFormat.sample_rate != mainBufferFormat.sample_rate;
-
-    auto normalFramesToGet = bufferPool.availableForGet(RingBufferPool::DEFAULT_ID);
-    auto urgentFramesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
-
-    double resampleFactor;
-    decltype(normalFramesToGet) readableSamples;
-    decltype(urgentFramesToGet) readableUrgentSamples;
+    AudioFormat currentOutFormat {  static_cast<unsigned>(outSampleRate_),
+                                    static_cast<unsigned>(outChannelsPerFrame_),
+                                    AV_SAMPLE_FMT_FLTP};
 
-    if (resample) {
-        resampleFactor = mainBufferFormat.sample_rate / static_cast<double>(currentOutFormat.sample_rate);
-        readableSamples = std::ceil(inNumberFrames * resampleFactor);
-    } else {
-        readableSamples = inNumberFrames;
-    }
-
-    // incoming call during call
-    if (urgentFramesToGet > 0) {
-        readableUrgentSamples = std::min(readableSamples, urgentFramesToGet);
-
-        playbackBuff_.setFormat(currentOutFormat);
-        playbackBuff_.resize(readableUrgentSamples);
-        urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
-        playbackBuff_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-
-        for (unsigned i = 0; i < currentOutFormat.nb_channels; ++i) {
-            playbackBuff_.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
-        }
-        manager.getRingBufferPool().discard(readableUrgentSamples, RingBufferPool::DEFAULT_ID);
-    }
-
-    if (normalFramesToGet > 0) {
-        readableSamples = std::min(readableSamples, normalFramesToGet);
-    }
-
-    auto& ringBuff = getToRing(audioFormat_, readableSamples);
-    auto& playBuff = getToPlay(audioFormat_, readableSamples);
-
-    auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
-
-    if (toPlay.frames() == 0) {
-        // clear buffer
-        for (unsigned i = 0; i < currentOutFormat.nb_channels; ++i) {
-            std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData),
-                        ioData->mBuffers[i].mDataByteSize / sizeof(Float32), 0);
-        }
-    } else if (resample) {
-        // resample
-        playbackBuff_.setFormat(currentOutFormat);
-        playbackBuff_.resize(readableSamples);
-        resampler_->resample(toPlay, playbackBuff_);
-        playbackBuff_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-        for (unsigned i = 0; i < audioFormat_.nb_channels; ++i) {
-            playbackBuff_.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
-        }
-    } else {
-        // normal play
-        const_cast<AudioBuffer&>(toPlay).applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-        for (unsigned i = 0; i < audioFormat_.nb_channels; ++i) {
-            toPlay.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
+    if (auto toPlay = getPlayback(currentOutFormat, inNumberFrames)) {
+        const auto& frame = *toPlay->pointer();
+        for (unsigned i = 0; i < frame.channels; ++i) {
+            std::copy_n((Float32*)frame.extended_data[i], inNumberFrames, (Float32*)ioData->mBuffers[i].mData);
         }
     }
 }
@@ -485,31 +423,13 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
             inNumberFrames,
             captureBuff_));
 
-    // Add them to Ring ringbuffer.
-    const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    bool resample = inSampleRate_ != mainBufferFormat.sample_rate;
-
-    // FIXME: Performance! There *must* be a better way. This is testing only.
-    auto inBuff = AudioBuffer {inNumberFrames, audioInputFormat_};
-
-    for (unsigned i = 0; i < inChannelsPerFrame_; ++i) {
-        auto data = reinterpret_cast<Float32*>(captureBuff_->mBuffers[i].mData);
-        for (unsigned j = 0; j < inNumberFrames; ++j) {
-            (*inBuff.getChannel(i))[j] = static_cast<AudioSample>(data[j] * 32768);
-        }
-    }
-
-    if (resample) {
-        //FIXME: May be a multiplication, check alsa vs pulse implementation.
-        UInt32 outSamples = inNumberFrames * (mainBufferFormat.sample_rate / static_cast<double>(audioInputFormat_.sample_rate));
-        auto out = AudioBuffer {outSamples, mainBufferFormat};
-        inputResampler_->resample(inBuff, out);
-        dcblocker_.process(out);
-        mainRingBuffer_->put(out);
-    } else {
-        dcblocker_.process(inBuff);
-        mainRingBuffer_->put(inBuff);
-    }
+    auto format = audioInputFormat_;
+    format.sampleFormat = AV_SAMPLE_FMT_FLTP;
+    auto inBuff = std::make_unique<AudioFrame>(audioInputFormat_, inNumberFrames);
+    auto& in = *inBuff->pointer();
+    for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
+        std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
+    mainRingBuffer_->put(std::move(inBuff));
 }
 
 void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
diff --git a/src/media/audio/coreaudio/osx/corelayer.cpp b/src/media/audio/coreaudio/osx/corelayer.cpp
index 33e47b05b8669cc6f3970c02feb7dabc0fc7ffc5..721037317a63e90b2eef39d5f44552a431a1e89b 100644
--- a/src/media/audio/coreaudio/osx/corelayer.cpp
+++ b/src/media/audio/coreaudio/osx/corelayer.cpp
@@ -91,7 +91,6 @@ CoreLayer::initAudioLayerIO()
 
     AudioUnitScope outputBus = 0;
     AudioUnitScope inputBus = 1;
-    UInt32 size = sizeof(UInt32);
     AudioComponentDescription desc = {0};
     desc.componentType = kAudioUnitType_Output;
     // kAudioOutputUnitProperty_EnableIO is ON and read-only
@@ -109,7 +108,7 @@ CoreLayer::initAudioLayerIO()
 
     // Set stream format
     AudioStreamBasicDescription info;
-    size = sizeof(info);
+    UInt32 size = sizeof(info);
     checkErr(AudioUnitGetProperty(ioUnit_,
             kAudioUnitProperty_StreamFormat,
             kAudioUnitScope_Output,
@@ -291,18 +290,15 @@ CoreLayer::write(AudioUnitRenderActionFlags* ioActionFlags,
     UInt32 inNumberFrames,
     AudioBufferList* ioData)
 {
-    auto& ringBuff = getToRing(audioFormat_, inNumberFrames);
-    auto& playBuff = getToPlay(audioFormat_, inNumberFrames);
-
-    auto& toPlay = ringBuff.frames() > 0 ? ringBuff : playBuff;
-
-    if (toPlay.frames() == 0) {
-        for (int i = 0; i < audioFormat_.nb_channels; ++i)
-            std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData),
-                        ioData->mBuffers[i].mDataByteSize/sizeof(Float32), 0);
+    auto format = audioFormat_;
+    format.sampleFormat = AV_SAMPLE_FMT_FLTP;
+    if (auto toPlay = getPlayback(format, inNumberFrames)) {
+        for (int i = 0; i < format.nb_channels; ++i) {
+            std::copy_n((Float32*)toPlay->pointer()->extended_data[i], inNumberFrames, (Float32*)ioData->mBuffers[i].mData);
+        }
     } else {
-        for (int i = 0; i < audioFormat_.nb_channels; ++i)
-            toPlay.channelToFloat(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), i);
+        for (int i = 0; i < format.nb_channels; ++i)
+            std::fill_n(reinterpret_cast<Float32*>(ioData->mBuffers[i].mData), inNumberFrames, 0);
     }
 }
 
@@ -339,34 +335,13 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
             inNumberFrames,
             captureBuff_));
 
-    // Add them to Ring ringbuffer.
-    const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    bool resample = inSampleRate_ != mainBufferFormat.sample_rate;
-
-    // FIXME: Performance! There *must* be a better way. This is testing only.
-    auto inBuff = AudioBuffer {inNumberFrames, audioInputFormat_};
-
-    for (int i = 0; i < inChannelsPerFrame_; ++i) {
-        auto data = reinterpret_cast<Float32*>(captureBuff_->mBuffers[i].mData);
-        for (int j = 0; j < inNumberFrames; ++j) {
-            (*inBuff.getChannel(i))[j] = static_cast<AudioSample>(data[j] / .000030517578125f);
-        }
-    }
-
-    if (resample) {
-        //RING_WARN("Resampling Input.");
-
-        //FIXME: May be a multiplication, check alsa vs pulse implementation.
-
-        UInt32 outSamples = inNumberFrames / (static_cast<double>(audioInputFormat_.sample_rate) / mainBufferFormat.sample_rate);
-        auto out = AudioBuffer {outSamples, mainBufferFormat};
-        inputResampler_->resample(inBuff, out);
-        dcblocker_.process(out);
-        mainRingBuffer_->put(out);
-    } else {
-        dcblocker_.process(inBuff);
-        mainRingBuffer_->put(inBuff);
-    }
+    auto format = audioInputFormat_;
+    audioInputFormat_.sampleFormat = AV_SAMPLE_FMT_FLTP;
+    auto inBuff = std::make_unique<AudioFrame>(audioInputFormat_, inNumberFrames);
+    auto& in = *inBuff->pointer();
+    for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
+        std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
+    mainRingBuffer_->put(std::move(inBuff));
 }
 
 void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
diff --git a/src/media/audio/jack/jacklayer.cpp b/src/media/audio/jack/jacklayer.cpp
index 50f862001780c82829d6ba9b97f27393dd10a749..78a97d24f47d7e8390cbe8f5fc0e9a1d3374f603 100644
--- a/src/media/audio/jack/jacklayer.cpp
+++ b/src/media/audio/jack/jacklayer.cpp
@@ -23,8 +23,7 @@
 #endif
 
 #include "jacklayer.h"
-#include <cassert>
-#include <climits>
+
 #include "logger.h"
 #include "audio/resampler.h"
 #include "audio/ringbufferpool.h"
@@ -35,6 +34,9 @@
 
 #include <unistd.h>
 
+#include <cassert>
+#include <climits>
+
 /* TODO
  * implement shutdown callback
  * auto connect optional
@@ -48,6 +50,8 @@ void connectPorts(jack_client_t *client, int portType, const std::vector<jack_po
 {
     const char **physical_ports = jack_get_ports(client, NULL, NULL, portType | JackPortIsPhysical);
     for (unsigned i = 0; physical_ports[i]; ++i) {
+        if (i >= ports.size())
+            break;
         const char *port = jack_port_name(ports[i]);
         if (portType & JackPortIsInput) {
             if (jack_connect(client, port, physical_ports[i])) {
@@ -61,7 +65,7 @@ void connectPorts(jack_client_t *client, int portType, const std::vector<jack_po
             }
         }
     }
-    free(physical_ports);
+    jack_free(physical_ports);
 }
 
 bool ringbuffer_ready_for_read(const jack_ringbuffer_t *rb)
@@ -71,167 +75,68 @@ bool ringbuffer_ready_for_read(const jack_ringbuffer_t *rb)
 }
 }
 
-void JackLayer::fillWithUrgent(AudioBuffer &buffer, size_t samplesToGet)
-{
-    // Urgent data (dtmf, incoming call signal) come first.
-    samplesToGet = std::min(samplesToGet, hardwareBufferSize_);
-    buffer.resize(samplesToGet);
-    urgentRingBuffer_.get(buffer, RingBufferPool::DEFAULT_ID);
-    buffer.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-
-    // Consume the regular one as well (same amount of samples)
-    Manager::instance().getRingBufferPool().discard(samplesToGet, RingBufferPool::DEFAULT_ID);
-}
-
-void JackLayer::fillWithVoice(AudioBuffer &buffer, size_t samplesAvail)
-{
-    RingBufferPool &mainBuffer = Manager::instance().getRingBufferPool();
-
-    buffer.resize(samplesAvail);
-    mainBuffer.getData(buffer, RingBufferPool::DEFAULT_ID);
-    buffer.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-
-    if (audioFormat_.sample_rate != (unsigned) mainBuffer.getInternalSamplingRate()) {
-        RING_DBG("fillWithVoice sample_rate != mainBuffer.getInternalSamplingRate() \n");
-        AudioBuffer out(buffer, false);
-        out.setSampleRate(audioFormat_.sample_rate);
-        resampler_->resample(buffer, out);
-        buffer = out;
-    }
-}
-
-void JackLayer::fillWithToneOrRingtone(AudioBuffer &buffer)
-{
-    buffer.resize(hardwareBufferSize_);
-    auto tone = Manager::instance().getTelephoneTone();
-    auto file_tone = Manager::instance().getTelephoneFile();
-
-    // In case of a dtmf, the pointers will be set to nullptr once the dtmf length is
-    // reached. For this reason we need to fill audio buffer with zeros if pointer is nullptr
-    if (tone) {
-        tone->getNext(buffer, playbackGain_);
-    } else if (file_tone) {
-        file_tone->getNext(buffer, playbackGain_);
-    } else {
-        buffer.reset();
-    }
-}
-
 void
 JackLayer::playback()
 {
     notifyIncomingCall();
-
-    const size_t samplesToGet = Manager::instance().getRingBufferPool().availableForGet(RingBufferPool::DEFAULT_ID);
-    const size_t urgentSamplesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
-
-    if (urgentSamplesToGet > 0) {
-        fillWithUrgent(playbackBuffer_, urgentSamplesToGet);
-    } else {
-        if (samplesToGet > 0) {
-            fillWithVoice(playbackBuffer_, samplesToGet);
-        } else {
-            fillWithToneOrRingtone(playbackBuffer_);
-        }
+    auto format = audioFormat_;
+    format.sampleFormat = AV_SAMPLE_FMT_FLTP;
+    if (auto toPlay = getPlayback(format, writeSpace())) {
+        write(*toPlay);
     }
-
-    playbackFloatBuffer_.resize(playbackBuffer_.frames());
-    write(playbackBuffer_, playbackFloatBuffer_);
 }
 
 void
 JackLayer::capture()
 {
-    // get audio from jack ringbuffer
-    read(captureBuffer_);
-
-    const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    const bool resample = mainBufferFormat.sample_rate != audioFormat_.sample_rate;
-
-    captureBuffer_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
-
-    if (resample) {
-        int outSamples = captureBuffer_.frames() * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
-        AudioBuffer out(outSamples, mainBufferFormat);
-        resampler_->resample(captureBuffer_, out);
-        dcblocker_.process(out);
-        mainRingBuffer_->put(out);
-    } else {
-        dcblocker_.process(captureBuffer_);
-        mainRingBuffer_->put(captureBuffer_);
-    }
-}
-
-static void
-convertToFloat(const std::vector<AudioSample> &src, std::vector<float> &dest)
-{
-    static const float INV_SHORT_MAX = 1 / (float) SHRT_MAX;
-    if (dest.size() != src.size()) {
-        RING_ERR("MISMATCH");
-        return;
-    }
-    for (size_t i = 0; i < dest.size(); ++i)
-        dest[i] = src[i] * INV_SHORT_MAX;
+    if (auto buf = read())
+        mainRingBuffer_->put(std::move(buf));
 }
 
-static void
-convertFromFloat(std::vector<float> &src, std::vector<AudioSample> &dest)
+size_t
+JackLayer::writeSpace()
 {
-    if (dest.size() != src.size()) {
-        RING_ERR("MISMATCH");
-        return;
+    if (out_ringbuffers_.empty())
+        return 0;
+    size_t toWrite {std::numeric_limits<size_t>::max()};
+    for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) {
+        toWrite = std::min(toWrite, jack_ringbuffer_write_space(out_ringbuffers_[i]));
     }
-    for (size_t i = 0; i < dest.size(); ++i)
-        dest[i] = src[i] * SHRT_MAX;
+    return std::min<size_t>(toWrite / sizeof(float), audioFormat_.sample_rate / 25);
 }
 
 void
-JackLayer::write(AudioBuffer &buffer, std::vector<float> &floatBuffer)
+JackLayer::write(const AudioFrame& buffer)
 {
-    for (unsigned i = 0; i < out_ringbuffers_.size(); ++i) {
-        const unsigned inChannel = std::min(i, buffer.channels() - 1);
-        convertToFloat(*buffer.getChannel(inChannel), floatBuffer);
-
-        // write to output
-        const size_t to_ringbuffer = jack_ringbuffer_write_space(out_ringbuffers_[i]);
-        const size_t write_bytes = std::min(buffer.frames() * sizeof(floatBuffer[0]), to_ringbuffer);
-        // FIXME: while we have samples to write AND while we have space to write them
-        const size_t written_bytes = jack_ringbuffer_write(out_ringbuffers_[i],
-                (const char *) floatBuffer.data(), write_bytes);
-        if (written_bytes < write_bytes)
-            RING_WARN("Dropped %zu bytes for channel %u", write_bytes - written_bytes, i);
+    auto num_samples = buffer.pointer()->nb_samples;
+    auto num_bytes = num_samples * sizeof(float);
+    auto channels = std::min<size_t>(out_ringbuffers_.size(), buffer.pointer()->channels);
+    for (size_t i = 0; i < channels; ++i) {
+        jack_ringbuffer_write(out_ringbuffers_[i], (const char*)buffer.pointer()->extended_data[i], num_bytes);
     }
 }
 
-void
-JackLayer::read(AudioBuffer &buffer)
+std::unique_ptr<AudioFrame>
+JackLayer::read()
 {
-    for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
-
-        const size_t incomingSamples = jack_ringbuffer_read_space(in_ringbuffers_[i]) / sizeof(captureFloatBuffer_[0]);
-        if (!incomingSamples)
-            continue;
+    if (in_ringbuffers_.empty())
+        return {};
 
-        captureFloatBuffer_.resize(incomingSamples);
-        buffer.resize(incomingSamples);
+    size_t toRead {std::numeric_limits<size_t>::max()};
+    for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
+        toRead = std::min(toRead, jack_ringbuffer_read_space(in_ringbuffers_[i]));
+    }
+    if (not toRead)
+        return {};
 
-        // write to output
-        const size_t from_ringbuffer = jack_ringbuffer_read_space(in_ringbuffers_[i]);
-        const size_t expected_bytes = std::min(incomingSamples * sizeof(captureFloatBuffer_[0]), from_ringbuffer);
-        // FIXME: while we have samples to write AND while we have space to write them
-        const size_t read_bytes = jack_ringbuffer_read(in_ringbuffers_[i],
-                (char *) captureFloatBuffer_.data(), expected_bytes);
-        if (read_bytes < expected_bytes) {
-            RING_WARN("Dropped %zu bytes", expected_bytes - read_bytes);
-            break;
-        }
+    auto format = audioInputFormat_;
+    format.sampleFormat = AV_SAMPLE_FMT_FLTP;
+    auto buffer = std::make_unique<AudioFrame>(format, toRead / sizeof(jack_default_audio_sample_t));
 
-        /* Write the data one frame at a time.  This is
-         * inefficient, but makes things simpler. */
-        // FIXME: this is braindead, we should write blocks of samples at a time
-        // convert a vector of samples from 1 channel to a float vector
-        convertFromFloat(captureFloatBuffer_, *buffer.getChannel(i));
+    for (unsigned i = 0; i < in_ringbuffers_.size(); ++i) {
+        jack_ringbuffer_read(in_ringbuffers_[i], (char *) buffer->pointer()->extended_data[i], toRead);
     }
+    return buffer;
 }
 
 /* This thread can lock, do whatever it wants, and read from/write to the jack
@@ -262,8 +167,6 @@ JackLayer::ringbuffer_worker()
         // is rather arbitrary. We should wait until ring has/needs data
         // and jack has/needs data.
         data_ready_.wait(lock, [&] {
-            // Note: lock is released while waiting, and held when woken
-            // up, so this predicate is called while holding the lock
             return status_ != Status::Started
             or ringbuffer_ready_for_read(in_ringbuffers_[0]);
         });
@@ -274,10 +177,11 @@ void
 createPorts(jack_client_t *client, std::vector<jack_port_t *> &ports,
             bool playback, std::vector<jack_ringbuffer_t *> &ringbuffers)
 {
-
     const char **physical_ports = jack_get_ports(client, NULL, NULL,
             playback ? JackPortIsInput : JackPortIsOutput | JackPortIsPhysical);
     for (unsigned i = 0; physical_ports[i]; ++i) {
+        if (i == 2)
+            break;
         char port_name[32] = {0};
         if (playback)
             snprintf(port_name, sizeof(port_name), "out_%d", i + 1);
@@ -298,7 +202,7 @@ createPorts(jack_client_t *client, std::vector<jack_port_t *> &ports,
             throw std::runtime_error("Could not lock JACK ringbuffer in memory");
         ringbuffers.push_back(rb);
     }
-    free(physical_ports);
+    jack_free(physical_ports);
 }
 
 
@@ -306,19 +210,6 @@ JackLayer::JackLayer(const AudioPreference &p) :
     AudioLayer(p),
     captureClient_(nullptr),
     playbackClient_(nullptr),
-    out_ports_(),
-    in_ports_(),
-    out_ringbuffers_(),
-    in_ringbuffers_(),
-    ringbuffer_thread_(),
-    //workerAlive_(false),
-    ringbuffer_thread_mutex_(),
-    data_ready_(),
-    playbackBuffer_(0, audioFormat_),
-    playbackFloatBuffer_(),
-    captureBuffer_(0, audioFormat_),
-    captureFloatBuffer_(),
-    hardwareBufferSize_(0),
     mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
 {
     playbackClient_ = jack_client_open(PACKAGE_NAME,
@@ -339,20 +230,10 @@ JackLayer::JackLayer(const AudioPreference &p) :
 
     const auto playRate = jack_get_sample_rate(playbackClient_);
     const auto captureRate = jack_get_sample_rate(captureClient_);
-    if (playRate != captureRate)
-        RING_ERR("Mismatch between capture rate %u and playback rate %u", playRate, captureRate);
-
-    hardwareBufferSize_ = jack_get_buffer_size(playbackClient_);
-
-    auto update_buffer = [] (AudioBuffer &buf, size_t size, unsigned rate, unsigned nbChannels) {
-        buf.setSampleRate(rate);
-        buf.resize(size);
-        buf.setChannelNum(nbChannels);
-    };
-
-    update_buffer(playbackBuffer_, hardwareBufferSize_, playRate, out_ports_.size());
-    update_buffer(captureBuffer_, hardwareBufferSize_, captureRate, in_ports_.size());
 
+    audioInputFormat_ = {captureRate, in_ringbuffers_.size()};
+    hardwareFormatAvailable(AudioFormat(playRate, out_ringbuffers_.size()));
+    hardwareInputFormatAvailable(audioInputFormat_);
     jack_on_shutdown(playbackClient_, onShutdown, this);
 }
 
@@ -476,15 +357,11 @@ JackLayer::startStream()
     }
 
     dcblocker_.reset();
-    const auto hardwareFormat = AudioFormat(playbackBuffer_.getSampleRate(), out_ports_.size());
-    hardwareFormatAvailable(hardwareFormat);
-
     if (jack_activate(playbackClient_) or jack_activate(captureClient_)) {
         RING_ERR("Could not activate JACK client");
         return;
     }
     ringbuffer_thread_ = std::thread(&JackLayer::ringbuffer_worker, this);
-
     connectPorts(playbackClient_, JackPortIsInput, out_ports_);
     connectPorts(captureClient_, JackPortIsOutput, in_ports_);
 }
diff --git a/src/media/audio/jack/jacklayer.h b/src/media/audio/jack/jacklayer.h
index 6c3e0795e97bd33e38181c3b4a1722fd7077b65d..c8167a2830692f9c20967e24ed849248f2d3e0ec 100644
--- a/src/media/audio/jack/jacklayer.h
+++ b/src/media/audio/jack/jacklayer.h
@@ -1,35 +1,36 @@
 /*
- *  Copyright (C) 2014-2018 Savoir-faire Linux Inc.
- *
- *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#ifndef JACK_LAYER_H_
-#define JACK_LAYER_H_
+*  Copyright (C) 2014-2018 Savoir-faire Linux Inc.
+*
+*  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
+*          Adrien Beraud <adrien.beraud@savoirfairelinux.com>
+*
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 3 of the License, or
+*  (at your option) any later version.
+*
+*  This program is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  You should have received a copy of the GNU General Public License
+*  along with this program; if not, write to the Free Software
+*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+*/
+
+#pragma once
+
+#include "noncopyable.h"
+#include "audio/audiolayer.h"
 
 #include <jack/jack.h>
 #include <jack/ringbuffer.h>
+
 #include <thread>
 #include <mutex>
 #include <vector>
 #include <condition_variable>
-#include "noncopyable.h"
-#include "audio/audiolayer.h"
-
 #include <memory>
 
 namespace ring {
@@ -37,69 +38,55 @@ namespace ring {
 class RingBuffer;
 
 class JackLayer : public AudioLayer {
-
-    private:
-        NON_COPYABLE(JackLayer);
-        jack_client_t *captureClient_;
-        jack_client_t *playbackClient_;
-        std::vector<jack_port_t *> out_ports_;
-        std::vector<jack_port_t *> in_ports_;
-        std::vector<jack_ringbuffer_t *> out_ringbuffers_;
-        std::vector<jack_ringbuffer_t *> in_ringbuffers_;
-        std::thread ringbuffer_thread_;
-        std::mutex ringbuffer_thread_mutex_;
-        std::condition_variable data_ready_;
-        AudioBuffer playbackBuffer_;
-        std::vector<float> playbackFloatBuffer_;
-        AudioBuffer captureBuffer_;
-        std::vector<float> captureFloatBuffer_;
-        size_t hardwareBufferSize_;
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
-
-        static int process_capture(jack_nframes_t frames, void *arg);
-        static int process_playback(jack_nframes_t frames, void *arg);
-
-        // separate thread
-        void ringbuffer_worker();
-        // called from ringbuffer_worker()
-        void playback();
-        void capture();
-        void fillWithUrgent(AudioBuffer &buffer, size_t samplesToGet);
-        void fillWithVoice(AudioBuffer &buffer, size_t samplesAvail);
-        void fillWithToneOrRingtone(AudioBuffer &buffer);
-
-        void read(AudioBuffer &buffer);
-        void write(AudioBuffer &buffer, std::vector<float> &floatBuffer);
-
-
-        std::vector<std::string> getCaptureDeviceList() const;
-        std::vector<std::string> getPlaybackDeviceList() const;
-
-        int getAudioDeviceIndex(const std::string& name, DeviceType type) const;
-        std::string getAudioDeviceName(int index, DeviceType type) const;
-        int getIndexCapture() const;
-        int getIndexPlayback() const;
-        int getIndexRingtone() const;
-        void updatePreference(AudioPreference &pref, int index, DeviceType type);
-
-        /**
-         * Start the capture and playback.
-         */
-        void startStream();
-
-        /**
-         * Stop playback and capture.
-         */
-        void stopStream();
-
-        static void onShutdown(void *data);
-
-    public:
-
-        JackLayer(const AudioPreference &);
-        ~JackLayer();
+private:
+    NON_COPYABLE(JackLayer);
+    jack_client_t *captureClient_;
+    jack_client_t *playbackClient_;
+    std::vector<jack_port_t *> out_ports_;
+    std::vector<jack_port_t *> in_ports_;
+    std::vector<jack_ringbuffer_t *> out_ringbuffers_;
+    std::vector<jack_ringbuffer_t *> in_ringbuffers_;
+    std::thread ringbuffer_thread_;
+    std::mutex ringbuffer_thread_mutex_;
+    std::condition_variable data_ready_;
+    std::shared_ptr<RingBuffer> mainRingBuffer_;
+
+    static int process_capture(jack_nframes_t frames, void *arg);
+    static int process_playback(jack_nframes_t frames, void *arg);
+
+    void ringbuffer_worker();
+    void playback();
+    void capture();
+
+    std::unique_ptr<AudioFrame> read();
+    void write(const AudioFrame& buffer);
+    size_t writeSpace();
+
+    std::vector<std::string> getCaptureDeviceList() const;
+    std::vector<std::string> getPlaybackDeviceList() const;
+
+    int getAudioDeviceIndex(const std::string& name, DeviceType type) const;
+    std::string getAudioDeviceName(int index, DeviceType type) const;
+    int getIndexCapture() const;
+    int getIndexPlayback() const;
+    int getIndexRingtone() const;
+    void updatePreference(AudioPreference &pref, int index, DeviceType type);
+
+    /**
+     * Start the capture and playback.
+     */
+    void startStream();
+
+    /**
+     * Stop playback and capture.
+     */
+    void stopStream();
+
+    static void onShutdown(void *data);
+
+public:
+    JackLayer(const AudioPreference &);
+    ~JackLayer();
 };
 
 }
-
-#endif // JACK_LAYER_H_
diff --git a/src/media/audio/opensl/opensllayer.cpp b/src/media/audio/opensl/opensllayer.cpp
index 51e0283c579e29bbd0b6d60e2be9ff5e0acd6ac9..913c3b063fe006839091058cdcd31803ad02157e 100644
--- a/src/media/audio/opensl/opensllayer.cpp
+++ b/src/media/audio/opensl/opensllayer.cpp
@@ -215,9 +215,9 @@ OpenSLLayer::engineServicePlay(bool waiting) {
     }
     sample_buf* buf;
     while (player_ and freePlayBufQueue_.front(&buf)) {
-        const AudioBuffer& dat = getToPlay(hardwareFormat_, hardwareBuffSize_);
-        if (dat.frames() != 0) {
-            buf->size_ = dat.interleave((AudioSample*)buf->buf_) * sizeof(AudioSample);
+        if (auto dat = getToPlay(hardwareFormat_, hardwareBuffSize_)) {
+            buf->size_ = dat->pointer()->nb_samples * dat->pointer()->channels * sizeof(AudioSample);
+            std::copy_n((const AudioSample*)dat->pointer()->data[0], dat->pointer()->nb_samples, (AudioSample*)buf->buf_);
             if (!playBufQueue_.push(buf)) {
                 RING_WARN("playThread player_ PLAY_KICKSTART_BUFFER_COUNT 1");
                 break;
@@ -237,9 +237,9 @@ OpenSLLayer::engineServiceRing(bool waiting) {
     sample_buf* buf;
     while (ringtone_ and freeRingBufQueue_.front(&buf)) {
         freeRingBufQueue_.pop();
-        const AudioBuffer& dat = getToRing(hardwareFormat_, hardwareBuffSize_);
-        if (dat.frames() != 0) {
-            buf->size_ = dat.interleave((AudioSample*)buf->buf_) * sizeof(AudioSample);
+        if (auto dat = getToRing(hardwareFormat_, hardwareBuffSize_)) {
+            buf->size_ = dat->pointer()->nb_samples * dat->pointer()->channels * sizeof(AudioSample);
+            std::copy_n((const AudioSample*)dat->pointer()->data[0], dat->pointer()->nb_samples, (AudioSample*)buf->buf_);
             if (!ringBufQueue_.push(buf)) {
                 RING_WARN("playThread ringtone_ PLAY_KICKSTART_BUFFER_COUNT 1");
                 freeRingBufQueue_.push(buf);
@@ -348,8 +348,10 @@ OpenSLLayer::startAudioCapture()
                     break;
                 recBufQueue_.pop();
                 if (buf->size_ > 0) {
-                    AudioBuffer dat {(const AudioSample*)buf->buf_, buf->size_ / hardwareFormat_.getBytesPerFrame(), hardwareFormat_};
-                    audioCaptureFillBuffer(dat);
+                    auto nb_samples = buf->size_ / hardwareFormat_.getBytesPerFrame();
+                    auto out = std::make_unique<AudioFrame>(hardwareFormat_, nb_samples);
+                    std::copy_n((const AudioSample*)buf->buf_, nb_samples, (AudioSample*)out->pointer()->data[0]);
+                    audioCaptureFillBuffer(std::move(out));
                 }
                 buf->size_ = 0;
                 freeRecBufQueue_.push(buf);
@@ -474,24 +476,13 @@ void
 OpenSLLayer::updatePreference(AudioPreference& /*preference*/, int /*index*/, DeviceType /*type*/)
 {}
 
-void OpenSLLayer::audioCaptureFillBuffer(AudioBuffer &buffer)
+void OpenSLLayer::audioCaptureFillBuffer(std::unique_ptr<AudioFrame>&& frame)
 {
     RingBufferPool &mbuffer = Manager::instance().getRingBufferPool();
-    const AudioFormat mainBufferFormat = mbuffer.getInternalAudioFormat();
-    const bool resample = mainBufferFormat.sample_rate != audioFormat_.sample_rate;
-
-    buffer.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
-
-    if (resample) {
-        int outSamples = buffer.frames() * (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
-        AudioBuffer out(outSamples, mainBufferFormat);
-        resampler_->resample(buffer, out);
-        dcblocker_.process(out);
-        mainRingBuffer_->put(out);
-    } else {
-        dcblocker_.process(buffer);
-        mainRingBuffer_->put(buffer);
-    }
+
+    // buffer.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
+    // dcblocker_.process(buffer);
+    mainRingBuffer_->put(resampler_->resample(std::move(frame), mbuffer.getInternalAudioFormat()));
 }
 
 void dumpAvailableEngineInterfaces()
diff --git a/src/media/audio/opensl/opensllayer.h b/src/media/audio/opensl/opensllayer.h
index 03da9ca85dc2129faffe89a119f53a732fb0ca95..d45c7ff5e36d287036b551db60c042abf854795d 100644
--- a/src/media/audio/opensl/opensllayer.h
+++ b/src/media/audio/opensl/opensllayer.h
@@ -118,7 +118,7 @@ class OpenSLLayer : public AudioLayer {
         void engineServiceRec(bool waiting);
 
     private:
-        void audioCaptureFillBuffer(AudioBuffer &buffer);
+        void audioCaptureFillBuffer(std::unique_ptr<AudioFrame>&& buffer);
 
         /**
          * Get the index of the audio card for capture
diff --git a/src/media/audio/portaudio/portaudiolayer.cpp b/src/media/audio/portaudio/portaudiolayer.cpp
index 5bb64c257bf51c60d3004fb7bbcdb8c9a0d9e699..52c350f536dd2af5c4035cdf7fa23e24a94794d7 100644
--- a/src/media/audio/portaudio/portaudiolayer.cpp
+++ b/src/media/audio/portaudio/portaudiolayer.cpp
@@ -268,71 +268,13 @@ PortAudioLayer::PortAudioLayerImpl::paOutputCallback(PortAudioLayer& parent,
     (void) timeInfo;
     (void) statusFlags;
 
-    auto& manager = Manager::instance();
-    auto& buffer_pool = manager.getRingBufferPool();
-
-    auto mainbuffer_format = buffer_pool.getInternalAudioFormat();
-    auto layer_format = parent.audioFormat_;
-    auto resample = layer_format.sample_rate != mainbuffer_format.sample_rate;
-
-    auto urgentFramesToGet = parent.urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
-    if (urgentFramesToGet > 0) {
-        RING_WARN("Getting urgent frames");
-        auto totSample = std::min(framesPerBuffer, (unsigned long)urgentFramesToGet);
-
-        playbackBuff_.setFormat(layer_format);
-        playbackBuff_.resize(totSample);
-        parent.urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
-
-        playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
-        playbackBuff_.interleave(outputBuffer);
-
-        manager.getRingBufferPool().discard(totSample, RingBufferPool::DEFAULT_ID);
-    }
-
-    auto normalFramesToGet = buffer_pool.availableForGet(RingBufferPool::DEFAULT_ID);
-    if (normalFramesToGet > 0) {
-        double resampleFactor;
-        decltype(normalFramesToGet) readableSamples;
-
-        if (resample) {
-            resampleFactor = static_cast<double>(layer_format.sample_rate) / mainbuffer_format.sample_rate;
-            readableSamples = std::ceil(framesPerBuffer / resampleFactor);
-        } else {
-            resampleFactor = 1.0;
-            readableSamples = framesPerBuffer;
-        }
-        readableSamples = std::min(readableSamples, normalFramesToGet);
-
-        playbackBuff_.setFormat(parent.audioFormat_);
-        playbackBuff_.resize(readableSamples);
-        buffer_pool.getData(playbackBuff_, RingBufferPool::DEFAULT_ID);
-        playbackBuff_.applyGain(parent.isPlaybackMuted_ ? 0.0 : parent.playbackGain_);
-
-        if (resample) {
-            AudioBuffer resampledOutput(readableSamples, parent.audioFormat_);
-            parent.resampler_->resample(playbackBuff_, resampledOutput);
-
-            resampledOutput.interleave(outputBuffer);
-        } else {
-            playbackBuff_.interleave(outputBuffer);
-        }
-    } else {
-        auto tone = manager.getTelephoneTone();
-        auto file_tone = manager.getTelephoneFile();
+    const auto& ringBuff = parent.getToRing(parent.audioFormat_, framesPerBuffer);
+    const auto& playBuff = parent.getToPlay(parent.audioFormat_, framesPerBuffer);
+    auto toPlay = ringBuff ? ringBuff : playBuff;
+    if (!toPlay)
+        return paContinue;
 
-        playbackBuff_.setFormat(parent.audioFormat_);
-        playbackBuff_.resize(framesPerBuffer);
-
-        if (tone) {
-            tone->getNext(playbackBuff_, parent.playbackGain_);
-        } else if (file_tone) {
-            file_tone->getNext(playbackBuff_, parent.playbackGain_);
-        } else {
-            playbackBuff_.reset();
-        }
-        playbackBuff_.interleave(outputBuffer);
-    }
+    std::copy_n((AudioSample*)toPlay->pointer()->extended_data[0], toPlay->pointer()->nb_samples, outputBuffer);
 
     return paContinue;
 }
@@ -355,25 +297,10 @@ PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
         return paContinue;
     }
 
-    const auto mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    bool resample = parent.audioInputFormat_.sample_rate != mainBufferFormat.sample_rate;
-    AudioBuffer inBuff(framesPerBuffer, parent.audioInputFormat_);
-
-    inBuff.deinterleave(inputBuffer, framesPerBuffer, parent.audioInputFormat_.nb_channels);
-
-    inBuff.applyGain(parent.isCaptureMuted_ ? 0.0 : parent.captureGain_);
-
-    if (resample) {
-        auto sample_factor = static_cast<double>(parent.audioInputFormat_.sample_rate) / mainBufferFormat.sample_rate;
-        auto outSamples = framesPerBuffer / sample_factor;
-        AudioBuffer out(outSamples, mainBufferFormat);
-        parent.inputResampler_->resample(inBuff, out);
-        parent.dcblocker_.process(out);
-        mainRingBuffer_->put(out);
-    } else {
-        parent.dcblocker_.process(inBuff);
-        mainRingBuffer_->put(inBuff);
-    }
+    auto inBuff = std::make_unique<AudioFrame>(parent.audioInputFormat_, framesPerBuffer);
+    std::copy_n(inputBuffer, framesPerBuffer, (AudioSample*)inBuff->pointer()->extended_data[0]);
+    //inBuff.applyGain(parent.isCaptureMuted_ ? 0.0 : parent.captureGain_);
+    mainRingBuffer_->put(std::move(inBuff));
     return paContinue;
 }
 
diff --git a/src/media/audio/pulseaudio/pulselayer.cpp b/src/media/audio/pulseaudio/pulselayer.cpp
index 83b4ae660f42f9db1582324dd910e42b7195f0b5..f248818a523a7250d44af07e72a6862d28da3882 100644
--- a/src/media/audio/pulseaudio/pulselayer.cpp
+++ b/src/media/audio/pulseaudio/pulselayer.cpp
@@ -26,7 +26,6 @@
 #include "pulselayer.h"
 #include "audio/resampler.h"
 #include "audio/dcblocker.h"
-#include "audio/resampler.h"
 #include "audio/ringbufferpool.h"
 #include "audio/ringbuffer.h"
 #include "logger.h"
@@ -38,6 +37,7 @@
 #include <unistd.h>
 #include <cstdlib>
 #include <fstream>
+#include <cstring>
 
 // Std-C++11 regex feature implemented only since GCC 4.9
 // Using pcre library as replacement
@@ -435,15 +435,15 @@ void PulseLayer::writeToSpeaker()
     if (writableBytes == 0)
         return;
 
-    auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
+    const auto& buff = getToPlay(playback_->format(), writableBytes / playback_->frameSize());
 
     AudioSample* data = nullptr;
     pa_stream_begin_write(playback_->stream(), (void**)&data, &writableBytes);
 
-    if (buff.frames() == 0)
+    if (!buff)
         memset(data, 0, writableBytes);
     else
-        buff.interleave(data);
+        std::memcpy(data, buff->pointer()->data[0], buff->pointer()->nb_samples * playback_->frameSize());
 
     pa_stream_write(playback_->stream(), data, writableBytes, nullptr, 0, PA_SEEK_RELATIVE);
 }
@@ -458,37 +458,22 @@ void PulseLayer::readFromMic()
     if (pa_stream_peek(record_->stream() , (const void**) &data , &bytes) < 0 or !data)
         return;
 
+    if (bytes == 0)
+        return;
+
     size_t sample_size = record_->frameSize();
-    const AudioFormat format = record_->format();
-    assert(format.nb_channels);
-    assert(sample_size);
     const size_t samples = bytes / sample_size;
 
-    micBuffer_.setFormat(format);
-    micBuffer_.resize(samples);
-    micBuffer_.deinterleave((AudioSample*)data, samples, format.nb_channels);
-    micBuffer_.applyGain(isCaptureMuted_ ? 0.0 : captureGain_);
-
-    auto mainBufferAudioFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
-    bool resample = format.sample_rate != mainBufferAudioFormat.sample_rate;
-
-    AudioBuffer* out;
-    if (resample) {
-        micResampleBuffer_.setSampleRate(mainBufferAudioFormat.sample_rate);
-        resampler_->resample(micBuffer_, micResampleBuffer_);
-        out = &micResampleBuffer_;
-    } else {
-        out = &micBuffer_;
-    }
-
-    dcblocker_.process(*out);
-    out->applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
-    mainRingBuffer_->put(*out);
+    auto out = std::make_unique<AudioFrame>(record_->format(), samples);
+    std::memcpy(out->pointer()->data[0], data, bytes);
 
     if (pa_stream_drop(record_->stream()) < 0)
         RING_ERR("Capture stream drop failed: %s" , pa_strerror(pa_context_errno(context_)));
-}
 
+    //dcblocker_.process(*out);
+    //out->applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
+    mainRingBuffer_->put(std::move(out));
+}
 
 void PulseLayer::ringtoneToSpeaker()
 {
@@ -499,15 +484,15 @@ void PulseLayer::ringtoneToSpeaker()
     if (bytes == 0)
         return;
 
-    auto& buff = getToRing(ringtone_->format(), bytes / ringtone_->frameSize());
+    const auto& buff = getToRing(ringtone_->format(), bytes / ringtone_->frameSize());
 
     AudioSample* data;
     pa_stream_begin_write(ringtone_->stream(), (void**)&data, &bytes);
 
-    if (buff.frames() == 0)
+    if (!buff)
         memset(data, 0, bytes);
     else
-        buff.interleave(data);
+        std::memcpy(data, buff->pointer()->data[0], buff->pointer()->nb_samples * playback_->frameSize());
 
     pa_stream_write(ringtone_->stream(), data, bytes, nullptr, 0, PA_SEEK_RELATIVE);
 }
diff --git a/src/media/audio/ringbuffer.cpp b/src/media/audio/ringbuffer.cpp
index e21415f54c5e699b66a32a37b7d4a2f72a445ed1..137549275baad38207fa90629b7c29a468889cf4 100644
--- a/src/media/audio/ringbuffer.cpp
+++ b/src/media/audio/ringbuffer.cpp
@@ -26,8 +26,10 @@
 #include "ringbuffer.h"
 #include "logger.h"
 
+#include "media_buffer.h"
+#include "libav_deps.h"
+
 #include <chrono>
-#include <utility> // for std::pair
 #include <cstdlib>
 #include <cstring>
 #include <algorithm>
@@ -37,15 +39,16 @@ namespace ring {
 // corresponds to 160 ms (about 5 rtp packets)
 static const size_t MIN_BUFFER_SIZE = 1024;
 
-// Create  a ring buffer with 'size' bytes
-RingBuffer::RingBuffer(const std::string& rbuf_id, size_t size,
-                       AudioFormat format /* = MONO */)
+RingBuffer::RingBuffer(const std::string& rbuf_id, size_t size, AudioFormat format)
     : id(rbuf_id)
     , endPos_(0)
-    , buffer_(std::max(size, MIN_BUFFER_SIZE), format)
     , lock_()
     , not_empty_()
     , readoffsets_()
+    , format_(format)
+    , resizer_(format_, format_.sample_rate / 50, [this](std::shared_ptr<AudioFrame>&& frame){
+        putToBuffer(std::move(frame));
+    })
 {}
 
 void
@@ -54,29 +57,26 @@ RingBuffer::flush(const std::string &call_id)
     storeReadOffset(endPos_, call_id);
 }
 
-
 void
 RingBuffer::flushAll()
 {
-    ReadOffset::iterator iter;
-
-    for (iter = readoffsets_.begin(); iter != readoffsets_.end(); ++iter)
-        iter->second = endPos_;
+    for (auto& offset : readoffsets_)
+        offset.second.offset = endPos_;
 }
 
 size_t
 RingBuffer::putLength() const
 {
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
         return 0;
-    const size_t startPos = (not readoffsets_.empty()) ? getSmallestReadOffset() : 0;
+    const size_t startPos = getSmallestReadOffset();
     return (endPos_ + buffer_size - startPos) % buffer_size;
 }
 
 size_t RingBuffer::getLength(const std::string &call_id) const
 {
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
         return 0;
     return (endPos_ + buffer_size - getReadOffset(call_id)) % buffer_size;
@@ -85,13 +85,13 @@ size_t RingBuffer::getLength(const std::string &call_id) const
 void
 RingBuffer::debug()
 {
-    RING_DBG("Start=%zu; End=%zu; BufferSize=%zu", getSmallestReadOffset(), endPos_, buffer_.frames());
+    RING_DBG("Start=%zu; End=%zu; BufferSize=%zu", getSmallestReadOffset(), endPos_, buffer_.size());
 }
 
 size_t RingBuffer::getReadOffset(const std::string &call_id) const
 {
-    ReadOffset::const_iterator iter = readoffsets_.find(call_id);
-    return (iter != readoffsets_.end()) ? iter->second : 0;
+    auto iter = readoffsets_.find(call_id);
+    return (iter != readoffsets_.end()) ? iter->second.offset : 0;
 }
 
 size_t
@@ -99,30 +99,29 @@ RingBuffer::getSmallestReadOffset() const
 {
     if (hasNoReadOffsets())
         return 0;
-    size_t smallest = buffer_.frames();
+    size_t smallest = buffer_.size();
     for(auto const& iter : readoffsets_)
-        smallest = std::min(smallest, iter.second);
+        smallest = std::min(smallest, iter.second.offset);
     return smallest;
 }
 
 void
 RingBuffer::storeReadOffset(size_t offset, const std::string &call_id)
 {
-    ReadOffset::iterator iter = readoffsets_.find(call_id);
+    ReadOffsetMap::iterator iter = readoffsets_.find(call_id);
 
     if (iter != readoffsets_.end())
-        iter->second = offset;
+        iter->second.offset = offset;
     else
         RING_ERR("RingBuffer::storeReadOffset() failed: unknown call '%s'", call_id.c_str());
 }
 
-
 void
 RingBuffer::createReadOffset(const std::string &call_id)
 {
     std::lock_guard<std::mutex> l(lock_);
     if (!hasThisReadOffset(call_id))
-        readoffsets_.insert(std::pair<std::string, int> (call_id, endPos_));
+        readoffsets_.emplace(call_id, ReadOffset {endPos_});
 }
 
 
@@ -130,13 +129,11 @@ void
 RingBuffer::removeReadOffset(const std::string &call_id)
 {
     std::lock_guard<std::mutex> l(lock_);
-    ReadOffset::iterator iter = readoffsets_.find(call_id);
-
+    auto iter = readoffsets_.find(call_id);
     if (iter != readoffsets_.end())
         readoffsets_.erase(iter);
 }
 
-
 bool
 RingBuffer::hasThisReadOffset(const std::string &call_id) const
 {
@@ -153,40 +150,36 @@ bool RingBuffer::hasNoReadOffsets() const
 // For the writer only:
 //
 
+void RingBuffer::put(std::shared_ptr<AudioFrame>&& data)
+{
+    resizer_.enqueue(resampler_.resample(std::move(data), format_));
+}
+
 // This one puts some data inside the ring buffer.
-void RingBuffer::put(AudioBuffer& buf)
+void RingBuffer::putToBuffer(std::shared_ptr<AudioFrame>&& data)
 {
     std::lock_guard<std::mutex> l(lock_);
-    const size_t sample_num = buf.frames();
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
         return;
 
-    size_t len = putLength();
-    if (buffer_size - len < sample_num)
-        discard(sample_num);
-    size_t toCopy = sample_num;
-
-    // Add more channels if the input buffer holds more channels than the ring.
-    if (buffer_.channels() < buf.channels())
-        buffer_.setChannelNum(buf.channels());
+    size_t len = buffer_size - putLength();
+    if (len == 0)
+        discard(1);
 
-    size_t in_pos = 0;
     size_t pos = endPos_;
 
-    while (toCopy) {
-        size_t block = toCopy;
+    buffer_[pos] = std::move(data);
+    const auto& newBuf = buffer_[pos];
+    pos = (pos + 1) % buffer_size;
 
-        if (block > buffer_size - pos) // Wrap block around ring ?
-            block = buffer_size - pos; // Fill in to the end of the buffer
+    endPos_ = pos;
 
-        buffer_.copy(buf, block, in_pos, pos);
-        in_pos += block;
-        pos = (pos + block) % buffer_size;
-        toCopy -= block;
+    for (auto& offset : readoffsets_) {
+        if (offset.second.callback)
+            offset.second.callback(newBuf);
     }
 
-    endPos_ = pos;
     not_empty_.notify_all();
 }
 
@@ -201,64 +194,45 @@ RingBuffer::availableForGet(const std::string &call_id) const
     return getLength(call_id);
 }
 
-size_t RingBuffer::get(AudioBuffer& buf, const std::string &call_id)
+std::shared_ptr<AudioFrame>
+RingBuffer::get(const std::string& call_id)
 {
     std::lock_guard<std::mutex> l(lock_);
 
-    if (not hasThisReadOffset(call_id))
-        return 0;
+    auto offset = readoffsets_.find(call_id);
+    if (offset == readoffsets_.end())
+        return {};
 
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
-        return 0;
-
-    size_t len = getLength(call_id);
-    const size_t sample_num = buf.frames();
-    size_t toCopy = std::min(sample_num, len);
-    if (toCopy and toCopy != sample_num) {
-        RING_DBG("Partial get: %zu/%zu", toCopy, sample_num);
-    }
-
-    const size_t copied = toCopy;
-
-    size_t dest = 0;
-    size_t startPos = getReadOffset(call_id);
-
-    while (toCopy > 0) {
-        size_t block = toCopy;
+        return {};
 
-        if (block > buffer_size - startPos)
-            block = buffer_size - startPos;
+    size_t startPos = offset->second.offset;
+    size_t len = (endPos_ + buffer_size - startPos) % buffer_size;
+    if (len == 0)
+        return {};
 
-        buf.copy(buffer_, block, startPos, dest);
-
-        dest += block;
-        startPos = (startPos + block) % buffer_size;
-        toCopy -= block;
-    }
-
-    storeReadOffset(startPos, call_id);
-    return copied;
+    auto ret = buffer_[startPos];
+    offset->second.offset = (startPos + 1) % buffer_size;
+    return ret;
 }
 
-
-size_t RingBuffer::waitForDataAvailable(const std::string &call_id, size_t min_data_length, const time_point& deadline) const
+size_t RingBuffer::waitForDataAvailable(const std::string &call_id, const time_point& deadline) const
 {
     std::unique_lock<std::mutex> l(lock_);
 
-    const size_t buffer_size = buffer_.frames();
-    if (buffer_size < min_data_length) return 0;
-    ReadOffset::const_iterator read_ptr = readoffsets_.find(call_id);
-    if (read_ptr == readoffsets_.end()) return 0;
+    if (buffer_.empty()) return 0;
+    if (readoffsets_.find(call_id) == readoffsets_.end()) return 0;
 
     size_t getl = 0;
     auto check = [=, &getl] {
         // Re-find read_ptr: it may be destroyed during the wait
+        const size_t buffer_size = buffer_.size();
         const auto read_ptr = readoffsets_.find(call_id);
-        if (read_ptr == readoffsets_.end())
+        if (buffer_size == 0 || read_ptr == readoffsets_.end())
             return true;
-        getl = (endPos_ + buffer_size - read_ptr->second) % buffer_size;
-        return getl >= min_data_length;
+        getl = (endPos_ + buffer_size - read_ptr->second.offset) % buffer_size;
+        return getl != 0;
     };
 
     if (deadline == time_point::max()) {
@@ -272,34 +246,36 @@ size_t RingBuffer::waitForDataAvailable(const std::string &call_id, size_t min_d
 }
 
 size_t
-RingBuffer::discard(size_t toDiscard, const std::string &call_id)
+RingBuffer::discard(size_t toDiscard, const std::string& call_id)
 {
     std::lock_guard<std::mutex> l(lock_);
 
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
         return 0;
 
-    size_t len = getLength(call_id);
-    if (toDiscard > len)
-        toDiscard = len;
+    auto offset = readoffsets_.find(call_id);
+    if (offset == readoffsets_.end())
+        return 0;
+
+    size_t len = (endPos_ + buffer_size - offset->second.offset) % buffer_size;
+    toDiscard = std::min(toDiscard, len);
 
-    size_t startPos = (getReadOffset(call_id) + toDiscard) % buffer_size;
-    storeReadOffset(startPos, call_id);
+    offset->second.offset = (offset->second.offset + toDiscard) % buffer_size;
     return toDiscard;
 }
 
 size_t
 RingBuffer::discard(size_t toDiscard)
 {
-    const size_t buffer_size = buffer_.frames();
+    const size_t buffer_size = buffer_.size();
     if (buffer_size == 0)
         return 0;
 
-    for (auto & r : readoffsets_) {
-        size_t dst = (r.second + buffer_size - endPos_) % buffer_size;
+    for (auto& r : readoffsets_) {
+        size_t dst = (r.second.offset + buffer_size - endPos_) % buffer_size;
         if (dst < toDiscard)
-            r.second = (r.second + toDiscard - dst) % buffer_size;
+            r.second.offset = (r.second.offset + toDiscard - dst) % buffer_size;
     }
     return toDiscard;
 }
diff --git a/src/media/audio/ringbuffer.h b/src/media/audio/ringbuffer.h
index 5db1ec18e5b16983fc5b266bcac11436ebbc9bda..28bf28f0e84c270ae7fb4f1bf6c3081ec36185f3 100644
--- a/src/media/audio/ringbuffer.h
+++ b/src/media/audio/ringbuffer.h
@@ -25,6 +25,8 @@
 
 #include "audiobuffer.h"
 #include "noncopyable.h"
+#include "audio_frame_resizer.h"
+#include "resampler.h"
 
 #include <condition_variable>
 #include <mutex>
@@ -43,6 +45,7 @@ class RingBuffer {
     public:
         using clock = std::chrono::high_resolution_clock;
         using time_point = clock::time_point;
+        using FrameCallback = std::function<void(const std::shared_ptr<AudioFrame>&)>;
 
         /**
          * Constructor
@@ -51,6 +54,8 @@ class RingBuffer {
         RingBuffer(const std::string& id, size_t size,
                    AudioFormat format=AudioFormat::MONO());
 
+        const std::string& getId() const { return id; }
+
         /**
          * Reset the counters to 0 for this read offset
          */
@@ -58,12 +63,13 @@ class RingBuffer {
 
         void flushAll();
 
-        inline  AudioFormat getFormat() const {
-            return buffer_.getFormat();
+        inline AudioFormat getFormat() const {
+            return format_;
         }
 
-        inline void setFormat(AudioFormat format) {
-            buffer_.setFormat(format);
+        inline void setFormat(const AudioFormat& format) {
+            format_ = format;
+            resizer_.setFormat(format, format.sample_rate / 50);
         }
 
         /**
@@ -71,6 +77,8 @@ class RingBuffer {
          */
         void createReadOffset(const std::string &call_id);
 
+        void createReadOffset(const std::string &call_id, FrameCallback cb);
+
         /**
          * Remove a readoffset for this ringbuffer
          */
@@ -83,7 +91,7 @@ class RingBuffer {
          * @param buffer Data to copied
          * @param toCopy Number of bytes to copy
          */
-         void put(AudioBuffer& buf);
+         void put(std::shared_ptr<AudioFrame>&& data);
 
         /**
          * To get how much samples are available in the buffer to read in
@@ -97,7 +105,7 @@ class RingBuffer {
          * @param toCopy Number of bytes to copy
          * @return size_t Number of bytes copied
          */
-         size_t get(AudioBuffer& buf, const std::string &call_id);
+        std::shared_ptr<AudioFrame> get(const std::string &call_id);
 
         /**
          * Discard data from the buffer
@@ -115,7 +123,7 @@ class RingBuffer {
         size_t getLength(const std::string &call_id) const;
 
         inline bool isFull() const {
-            return putLength() == buffer_.frames();
+            return putLength() == buffer_.size();
         }
 
         inline bool isEmpty() const {
@@ -130,19 +138,23 @@ class RingBuffer {
          * @param deadline The call is guaranteed to end after this time point. If no deadline is provided, the call blocks indefinitely.
          * @return available data for call_id after the call returned (same as calling getLength(call_id) ).
          */
-        size_t waitForDataAvailable(const std::string& call_id, size_t min_data_length, const time_point& deadline = time_point::max()) const;
+        size_t waitForDataAvailable(const std::string& call_id, const time_point& deadline = time_point::max()) const;
 
         /**
          * Debug function print mEnd, mStart, mBufferSize
          */
         void debug();
 
-        const std::string id;
-
     private:
-        using ReadOffset = std::map<std::string, size_t>;
+        struct ReadOffset {
+            size_t offset;
+            FrameCallback callback;
+        };
+        using ReadOffsetMap = std::map<std::string, ReadOffset>;
         NON_COPYABLE(RingBuffer);
 
+        void putToBuffer(std::shared_ptr<AudioFrame>&& data);
+
         bool hasNoReadOffsets() const;
 
         /**
@@ -170,16 +182,22 @@ class RingBuffer {
          */
         size_t discard(size_t toDiscard);
 
+        const std::string id;
+
         /** Offset on the last data */
         size_t endPos_;
 
         /** Data */
-        AudioBuffer buffer_;
+        AudioFormat format_ {AudioFormat::DEFAULT()};
+        std::vector<std::shared_ptr<AudioFrame>> buffer_ {8};
 
         mutable std::mutex lock_;
         mutable std::condition_variable not_empty_;
 
-        ReadOffset readoffsets_;
+        ReadOffsetMap readoffsets_;
+
+        Resampler resampler_;
+        AudioFrameResizer resizer_;
 };
 
 } // namespace ring
diff --git a/src/media/audio/ringbufferpool.cpp b/src/media/audio/ringbufferpool.cpp
index 2d644d92910b0464874624ddfa644b9276bb914c..8817fb1bb551ba44cc11b7ccc8efc35ca5b4cf47 100644
--- a/src/media/audio/ringbufferpool.cpp
+++ b/src/media/audio/ringbufferpool.cpp
@@ -111,7 +111,7 @@ RingBufferPool::createRingBuffer(const std::string& id)
         return rbuf;
     }
 
-    rbuf.reset(new RingBuffer(id, SIZEBUF));
+    rbuf.reset(new RingBuffer(id, SIZEBUF, internalAudioFormat_));
     RING_DBG("Ringbuffer created with id '%s'", id.c_str());
     ringBufferMap_.insert(std::make_pair(id, std::weak_ptr<RingBuffer>(rbuf)));
     return rbuf;
@@ -145,12 +145,12 @@ void
 RingBufferPool::addReaderToRingBuffer(const std::shared_ptr<RingBuffer>& rbuf,
                                   const std::string& call_id)
 {
-    if (call_id != DEFAULT_ID and rbuf->id == call_id)
+    if (call_id != DEFAULT_ID and rbuf->getId() == call_id)
         RING_WARN("RingBuffer has a readoffset on itself");
 
     rbuf->createReadOffset(call_id);
     readBindingsMap_[call_id].insert(rbuf); // bindings list created if not existing
-    RING_DBG("Bind rbuf '%s' to callid '%s'", rbuf->id.c_str(), call_id.c_str());
+    RING_DBG("Bind rbuf '%s' to callid '%s'", rbuf->getId().c_str(), call_id.c_str());
 }
 
 void
@@ -251,12 +251,12 @@ RingBufferPool::unBindAll(const std::string& call_id)
     const auto bindings_copy = *bindings; // temporary copy
     for (const auto& rbuf : bindings_copy) {
         removeReaderFromRingBuffer(rbuf, call_id);
-        removeReaderFromRingBuffer(rb_call, rbuf->id);
+        removeReaderFromRingBuffer(rb_call, rbuf->getId());
     }
 }
 
-size_t
-RingBufferPool::getData(AudioBuffer& buffer, const std::string& call_id)
+std::shared_ptr<AudioFrame>
+RingBufferPool::getData(const std::string& call_id)
 {
     std::lock_guard<std::recursive_mutex> lk(stateLock_);
 
@@ -266,27 +266,21 @@ RingBufferPool::getData(AudioBuffer& buffer, const std::string& call_id)
 
     // No mixing
     if (bindings->size() == 1)
-        return (*bindings->cbegin())->get(buffer, call_id);
-
-    buffer.reset();
-    buffer.setFormat(internalAudioFormat_);
+        return (*bindings->cbegin())->get(call_id);
 
     size_t size = 0;
-    AudioBuffer mixBuffer(buffer);
-
+    auto mixBuffer = std::make_unique<AudioFrame>(internalAudioFormat_);
     for (const auto& rbuf : *bindings) {
-        // XXX: is it normal to only return the last positive size?
-        size = rbuf->get(mixBuffer, call_id);
-        if (size > 0)
-            buffer.mix(mixBuffer);
+        if (auto b = rbuf->get(call_id)) {
+            mixBuffer->mix(*b);
+        }
     }
 
-    return size;
+    return mixBuffer;
 }
 
 bool
 RingBufferPool::waitForDataAvailable(const std::string& call_id,
-                                     size_t min_frames,
                                      const std::chrono::microseconds& max_wait) const
 {
     std::unique_lock<std::recursive_mutex> lk(stateLock_);
@@ -301,15 +295,15 @@ RingBufferPool::waitForDataAvailable(const std::string& call_id,
     const auto bindings_copy = *bindings; // temporary copy
     for (const auto& rbuf : bindings_copy) {
         lk.unlock();
-        if (rbuf->waitForDataAvailable(call_id, min_frames, deadline) < min_frames)
+        if (rbuf->waitForDataAvailable(call_id, deadline) == 0)
             return false;
         lk.lock();
     }
     return true;
 }
 
-size_t
-RingBufferPool::getAvailableData(AudioBuffer& buffer, const std::string& call_id)
+std::shared_ptr<AudioFrame>
+RingBufferPool::getAvailableData(const std::string& call_id)
 {
     std::lock_guard<std::recursive_mutex> lk(stateLock_);
 
@@ -319,32 +313,24 @@ RingBufferPool::getAvailableData(AudioBuffer& buffer, const std::string& call_id
 
     // No mixing
     if (bindings->size() == 1) {
-        return (*bindings->cbegin())->get(buffer, call_id);
+        return (*bindings->cbegin())->get(call_id);
     }
 
-    size_t availableSamples = std::numeric_limits<size_t>::max();
+    size_t availableFrames = 0;
 
     for (const auto& rbuf : *bindings)
-        availableSamples = std::min(availableSamples,
-                                    rbuf->availableForGet(call_id));
-
-    if (availableSamples == std::numeric_limits<size_t>::max())
-        return 0;
-
-    availableSamples = std::min(availableSamples, buffer.frames());
-
-    buffer.resize(availableSamples);
-    buffer.reset();
-    buffer.setFormat(internalAudioFormat_);
+        availableFrames = std::min(availableFrames, rbuf->availableForGet(call_id));
 
-    AudioBuffer mixBuffer(buffer);
+    if (availableFrames == 0)
+        return {};
 
+    auto buf = std::make_unique<AudioFrame>(internalAudioFormat_);
     for (const auto &rbuf : *bindings) {
-        if (rbuf->get(mixBuffer, call_id) > 0)
-            buffer.mix(mixBuffer);
+        if (auto b = rbuf->get(call_id))
+            buf->mix(*b);
     }
 
-    return availableSamples;
+    return buf;
 }
 
 size_t
@@ -365,7 +351,7 @@ RingBufferPool::availableForGet(const std::string& call_id) const
 
     for (const auto& rbuf : *bindings) {
         const size_t nbSamples = rbuf->availableForGet(call_id);
-        if (nbSamples > 0)
+        if (nbSamples != 0)
             availableSamples = std::min(availableSamples, nbSamples);
     }
 
diff --git a/src/media/audio/ringbufferpool.h b/src/media/audio/ringbufferpool.h
index 3a5713579254d39b82aed6be540aaa758e87858a..171f0b711f2c2ea594dcceb13418897ae24c5116 100644
--- a/src/media/audio/ringbufferpool.h
+++ b/src/media/audio/ringbufferpool.h
@@ -19,8 +19,7 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#ifndef RING_BUFFER_POOL_H_
-#define RING_BUFFER_POOL_H_
+#pragma once
 
 #include "audiobuffer.h"
 #include "noncopyable.h"
@@ -36,7 +35,6 @@ namespace ring {
 class RingBuffer;
 
 class RingBufferPool {
-
     public:
         static const char * const DEFAULT_ID;
 
@@ -86,12 +84,11 @@ class RingBufferPool {
         void unBindAll(const std::string& call_id);
 
         bool waitForDataAvailable(const std::string& call_id,
-                                  size_t min_data_length,
                                   const std::chrono::microseconds& max_wait) const;
 
-        size_t getData(AudioBuffer& buffer, const std::string& call_id);
+        std::shared_ptr<AudioFrame> getData(const std::string& call_id);
 
-        size_t getAvailableData(AudioBuffer& buffer, const std::string& call_id);
+        std::shared_ptr<AudioFrame> getAvailableData(const std::string& call_id);
 
         size_t availableForGet(const std::string& call_id) const;
 
@@ -155,5 +152,3 @@ class RingBufferPool {
 };
 
 } // namespace ring
-
-#endif  // RING_BUFFER_POOL_H_
diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index 670f204371bd544b485d2300c20864a964097468..ccdaf2ddf1053171d27867e4d9a409895bb0b30b 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -467,36 +467,18 @@ int MediaDecoder::getPixelFormat() const
 { return decoderCtx_->pix_fmt; }
 
 void
-MediaDecoder::writeToRingBuffer(const AudioFrame& decodedFrame,
+MediaDecoder::writeToRingBuffer(std::unique_ptr<AudioFrame>&& decodedFrame,
                                 RingBuffer& rb, const AudioFormat outFormat)
 {
-    const auto frame = decodedFrame.pointer();
-    const auto inFormat = AudioFormat((unsigned)frame->sample_rate,
-                                      (unsigned)frame->channels,
-                                      (AVSampleFormat)frame->format);
-
-    AudioFrame output;
-    if (inFormat != outFormat) {
-        if (!resampler_) {
+    AudioFormat format (decoderCtx_->sample_rate, decoderCtx_->channels, decoderCtx_->sample_fmt);
+    if (format != outFormat) {
+        if (not resampler_) {
             RING_DBG("Creating audio resampler");
             resampler_.reset(new Resampler);
         }
-        auto out = output.pointer();
-        out->format = (int)outFormat.sampleFormat;
-        out->channel_layout = av_get_default_channel_layout((int)outFormat.nb_channels);
-        out->channels = (int)outFormat.nb_channels;
-        out->sample_rate = (int)outFormat.sample_rate;
-        if (resampler_->resample(frame, out) < 0) {
-            RING_ERR() << "Failed to resample audio";
-            return;
-        }
-    } else {
-        output.copyFrom(decodedFrame);
+        decodedFrame = resampler_->resample(std::move(decodedFrame), outFormat);
     }
-    // let buf resize itself
-    auto buf = AudioBuffer(0, outFormat);
-    buf.append(output);
-    rb.put(buf);
+    rb.put(std::move(decodedFrame));
 }
 
 int
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index 329924266bd2822fcec2512af440feb7825259f0..5781777275e6d495bf39c4912c117da73d7ecf05 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -90,7 +90,7 @@ class MediaDecoder {
 
         int setupFromAudioData();
         Status decode(AudioFrame&);
-        void writeToRingBuffer(const AudioFrame&, RingBuffer&, const AudioFormat);
+        void writeToRingBuffer(std::unique_ptr<AudioFrame>&& decodedFrame, RingBuffer&, const AudioFormat);
 
         int getWidth() const;
         int getHeight() const;
diff --git a/src/media/media_device.h b/src/media/media_device.h
index b8dea845fbc1d96a7cd2bbe6d4aa7f863ee62a19..f4d5b613db69f01296fa2dda4767a81ced45b880 100644
--- a/src/media/media_device.h
+++ b/src/media/media_device.h
@@ -24,9 +24,12 @@
 #include "rational.h"
 
 #include <string>
+#include <chrono>
 
 namespace ring {
 
+constexpr static auto NEWPARAMS_TIMEOUT = std::chrono::milliseconds(1000);
+
 /**
  * DeviceParams
  * Parameters used by MediaDecoder and MediaEncoder
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index 332ceb71fbbfaf7123bfcb7fc22492fd3a86206d..60b4338b9fd5ab764b87a6df56320c8e9a4e0a15 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -46,8 +46,6 @@ namespace ring { namespace video {
 using std::map;
 using std::string;
 
-constexpr static auto NEWPARAMS_TIMEOUT = std::chrono::milliseconds(1000);
-
 // how long (in seconds) to wait before rechecking for packet loss
 static constexpr auto RTCP_PACKET_LOSS_INTERVAL = std::chrono::milliseconds(1000);