diff --git a/ChangeLog b/ChangeLog
index 642e3492c5f75c1c0648f9258a693f779c35bf2e..157ded7cb9c8843ce2a343eead42d9e86b902600 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,2 @@
-2.0.0 (2015-03-31)
-------------------
-
-First DRing release (formely SFLPhone - released as 1.4.1)
+This file is not used for logging changes but to satisfies autotool requirements.
+Please, read NEWS file for changes.
diff --git a/NEWS b/NEWS
index f761319d22ef461aad85c176359550712e279f42..091ae3d3357551db25e910ce28f46b1b79d2dd4e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,12 @@
-DRing (2.0.0) / 2015-03-31
+DRing (2.0.1) / 2015-03-27
+	* Add FPS calculation for streamed files
+	* Set defaults video bitrate to 800 Kb/s
+	* Use RGBA for direct rendering (MacOSX)
+	* MacOSX: force video device format on supported values
+	* SecureSIP: fix race condition and incoming packet handling
+	* minor documentation fixes
+
+DRing (2.0.0) / 2015-03-25
 	* Project renaming
 	* First release under this name
 	* Ring account (DHT) introduced
diff --git a/configure.ac b/configure.ac
index fb75c5fab0df58bb49d83d260ce1530e79c8a89c..885a4e4ad1169c01ebb532e003ccfa4ded4f856a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Ring - configure.ac for automake 1.9 and autoconf 2.59
 
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.65])
-AC_INIT([Ring],[2.0.0],[ring@lists.savoirfairelinux.net],[ring])
+AC_INIT([Ring],[2.0.1],[ring@lists.savoirfairelinux.net],[ring])
 
 AC_COPYRIGHT([[Copyright (c) Savoir-Faire Linux 2004-2015]])
 AC_REVISION([$Revision$])
diff --git a/contrib/src/libav/osx.patch b/contrib/src/libav/osx.patch
index 4f885fd6d14fe20309ff7304a90618ad39dac25d..b2632429d224bc90b2e6d4b90c427cefe02d2c3f 100644
--- a/contrib/src/libav/osx.patch
+++ b/contrib/src/libav/osx.patch
@@ -242,10 +242,10 @@ index 5dbe277..8439b5b 100644
      REGISTER_INDEV   (FBDEV,            fbdev);
 diff --git a/libavdevice/avfoundation_dec.m b/libavdevice/avfoundation_dec.m
 new file mode 100644
-index 0000000..f1b9c03
+index 0000000..6b60782
 --- /dev/null
 +++ b/libavdevice/avfoundation_dec.m
-@@ -0,0 +1,447 @@
+@@ -0,0 +1,517 @@
 +/*
 + * AVFoundation input device
 + * Copyright (c) 2015 Luca Barbato
@@ -281,13 +281,47 @@ index 0000000..f1b9c03
 +
 +#include "avdevice.h"
 +
++struct AVFPixelFormatSpec {
++    enum AVPixelFormat ff_id;
++    OSType avf_id;
++};
++
++static const struct AVFPixelFormatSpec avf_pixel_formats[] = {
++    { AV_PIX_FMT_MONOBLACK,    kCVPixelFormatType_1Monochrome },
++    { AV_PIX_FMT_RGB555BE,     kCVPixelFormatType_16BE555 },
++    { AV_PIX_FMT_RGB555LE,     kCVPixelFormatType_16LE555 },
++    { AV_PIX_FMT_RGB565BE,     kCVPixelFormatType_16BE565 },
++    { AV_PIX_FMT_RGB565LE,     kCVPixelFormatType_16LE565 },
++    { AV_PIX_FMT_RGB24,        kCVPixelFormatType_24RGB },
++    { AV_PIX_FMT_BGR24,        kCVPixelFormatType_24BGR },
++    { AV_PIX_FMT_ARGB,         kCVPixelFormatType_32ARGB },
++    { AV_PIX_FMT_BGRA,         kCVPixelFormatType_32BGRA },
++    { AV_PIX_FMT_ABGR,         kCVPixelFormatType_32ABGR },
++    { AV_PIX_FMT_RGBA,         kCVPixelFormatType_32RGBA },
++    { AV_PIX_FMT_BGR48BE,      kCVPixelFormatType_48RGB },
++    { AV_PIX_FMT_UYVY422,      kCVPixelFormatType_422YpCbCr8 },
++    { AV_PIX_FMT_YUVA444P,     kCVPixelFormatType_4444YpCbCrA8R },
++    { AV_PIX_FMT_YUVA444P16LE, kCVPixelFormatType_4444AYpCbCr16 },
++    { AV_PIX_FMT_YUV444P,      kCVPixelFormatType_444YpCbCr8 },
++    { AV_PIX_FMT_YUV422P16,    kCVPixelFormatType_422YpCbCr16 },
++    { AV_PIX_FMT_YUV422P10,    kCVPixelFormatType_422YpCbCr10 },
++    { AV_PIX_FMT_YUV444P10,    kCVPixelFormatType_444YpCbCr10 },
++    { AV_PIX_FMT_YUV420P,      kCVPixelFormatType_420YpCbCr8Planar },
++    { AV_PIX_FMT_NV12,         kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange },
++    { AV_PIX_FMT_YUYV422,      kCVPixelFormatType_422YpCbCr8_yuvs },
++#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
++    { AV_PIX_FMT_GRAY8,        kCVPixelFormatType_OneComponent8 },
++#endif
++    { AV_PIX_FMT_NONE, 0 }
++};
++
 +typedef struct AVFoundationCaptureContext {
 +    AVClass         *class;
 +    int             list_devices;
 +    CFTypeRef       session;        /** AVCaptureSession*/
 +    char*           video_size;     /**< String describing video size,
 +                                        set by a private option. */
-+    char*           pixel_format;   /**< Set by a private option. */
++    enum AVPixelFormat pixel_format;   /**< Set by a private option. */
 +    int             list_format;    /**< Set by a private option. */
 +    char*           framerate;      /**< Set by a private option. */
 +
@@ -442,9 +476,46 @@ index 0000000..f1b9c03
 +        }
 +
 +        [out setAlwaysDiscardsLateVideoFrames:YES];
-+        out.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)};
 +
-+        //[out setVideoSettings:nil];
++
++        // select pixel format
++        struct AVFPixelFormatSpec pxl_fmt_spec;
++        pxl_fmt_spec.ff_id = AV_PIX_FMT_NONE;
++
++        av_log(s, AV_LOG_ERROR, "Supported pixel formats:\n");
++        for (NSNumber *pxl_fmt in [out availableVideoCVPixelFormatTypes]) {
++            struct AVFPixelFormatSpec pxl_fmt_dummy;
++            pxl_fmt_dummy.ff_id = AV_PIX_FMT_NONE;
++            for (int i = 0; avf_pixel_formats[i].ff_id != AV_PIX_FMT_NONE; i++) {
++                if ([pxl_fmt intValue] == avf_pixel_formats[i].avf_id) {
++                    pxl_fmt_dummy = avf_pixel_formats[i];
++                    break;
++                }
++            }
++
++            if (pxl_fmt_dummy.ff_id != AV_PIX_FMT_NONE) {
++                av_log(s, AV_LOG_ERROR, "  %s: %d \n", av_get_pix_fmt_name(pxl_fmt_dummy.ff_id),
++                                                    pxl_fmt_dummy.avf_id);
++
++                // select first supported pixel format instead of user selected (or default) pixel format
++                if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
++                    pxl_fmt_spec = pxl_fmt_dummy;
++                }
++            }
++        }
++
++        // fail if there is no appropriate pixel format or print a warning about overriding the pixel format
++        if (pxl_fmt_spec.ff_id == AV_PIX_FMT_NONE) {
++            return 1;
++        } else {
++            av_log(s, AV_LOG_WARNING, "Overriding selected pixel format to use %s instead.\n",
++                   av_get_pix_fmt_name(pxl_fmt_spec.ff_id));
++        }
++        ctx->pixel_format          = pxl_fmt_spec.ff_id;
++        NSNumber     *pixel_format = [NSNumber numberWithUnsignedInt:pxl_fmt_spec.avf_id];
++        NSDictionary *capture_dict = [NSDictionary dictionaryWithObject:pixel_format
++                                                   forKey:(id)kCVPixelBufferPixelFormatTypeKey];
++        [out setVideoSettings:capture_dict];
 +
 +        AVFFrameReceiver* delegate = [[AVFFrameReceiver alloc] initWithContext:ctx];
 +
@@ -507,8 +578,7 @@ index 0000000..f1b9c03
 +    stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
 +    stream->codec->width      = (int)image_buffer_size.width;
 +    stream->codec->height     = (int)image_buffer_size.height;
-+    // No support for pixel formats for now using default one
-+    stream->codec->pix_fmt    = AV_PIX_FMT_BGR32;
++    stream->codec->pix_fmt    = ctx->pixel_format;
 +
 +    CFRelease(ctx->current_frame);
 +    ctx->current_frame = nil;
@@ -527,7 +597,6 @@ index 0000000..f1b9c03
 +
 +    ctx->session = NULL;
 +
-+
 +    pthread_mutex_destroy(&ctx->frame_lock);
 +    pthread_cond_destroy(&ctx->frame_wait_cond);
 +
@@ -585,7 +654,8 @@ index 0000000..f1b9c03
 +        if (matches.count > 0) {
 +            for (NSTextCheckingResult *match in matches) {
 +                NSRange range = [match rangeAtIndex:0];
-+                NSString *uniqueID = [filename substringWithRange:NSMakeRange(range.location + 1, range.length-2)];                av_log(s, AV_LOG_INFO, "opening device with ID: %s\n",[uniqueID UTF8String]);
++                NSString *uniqueID = [filename substringWithRange:NSMakeRange(range.location + 1, range.length-2)];
++                av_log(s, AV_LOG_INFO, "opening device with ID: %s\n",[uniqueID UTF8String]);
 +                if (!(device = [AVCaptureDevice deviceWithUniqueID:uniqueID])) {
 +                    // report error
 +                    av_log(s, AV_LOG_ERROR, "Device with name %s not found",[filename UTF8String]);
diff --git a/doc/doxygen/core-doc.cfg.in b/doc/doxygen/core-doc.cfg.in
index d25075a3ab5da7617e494b104b7d5127607cea23..764a06e92425c230d4b8c596bba7f9aa8627e2f5 100644
--- a/doc/doxygen/core-doc.cfg.in
+++ b/doc/doxygen/core-doc.cfg.in
@@ -31,7 +31,7 @@ PROJECT_NAME           = "Ring Daemon"
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 2.0.0
+PROJECT_NUMBER         = 2.0.1
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer
diff --git a/src/account.cpp b/src/account.cpp
index b042077de4e856bedda4e3dea9789ae135752ca6..2d713a86ba764d26bb5d7da19845081cd28a23be 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -335,11 +335,6 @@ Account::setActiveCodecs(const std::vector<unsigned>& list)
               });
 
     allCodecStr_ = join_string(getActiveAccountCodecInfoIdList(MEDIA_ALL));
-
-    for (const auto& item : accountCodecInfoList_)
-        RING_DBG("[%s] order:%d,  isActive=%s, codec=%s", accountID_.c_str(),
-                 item->order, (item->isActive ? "true" : "false"),
-                 item->systemCodecInfo.to_string().c_str());
 }
 
 std::string
diff --git a/src/media/libav_utils.cpp b/src/media/libav_utils.cpp
index 95af5d2080336c1a4c80c54ab2d4eec8f5f5bbc6..e77cdf7a8534588f9aadfd758c9637d1bd470543 100644
--- a/src/media/libav_utils.cpp
+++ b/src/media/libav_utils.cpp
@@ -107,6 +107,7 @@ int libav_pixel_format(int fmt)
 {
     switch (fmt) {
         case video::VIDEO_PIXFMT_BGRA: return PIXEL_FORMAT(BGRA);
+        case video::VIDEO_PIXFMT_RGBA: return PIXEL_FORMAT(RGBA);
         case video::VIDEO_PIXFMT_YUV420P: return PIXEL_FORMAT(YUV420P);
     }
     return fmt;
diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index c92564146730f82e7a0d931c525e0e1e3b948977..e24b368a2fa5854b5401facf7a48849787a59285 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -250,7 +250,8 @@ int MediaDecoder::setupFromVideoData()
     }
 
     // Get a pointer to the codec context for the video stream
-    decoderCtx_ = inputCtx_->streams[streamIndex_]->codec;
+    avStream_ = inputCtx_->streams[streamIndex_];
+    decoderCtx_ = avStream_->codec;
     if (decoderCtx_ == 0) {
         RING_ERR("Decoder context is NULL");
         return -1;
@@ -428,6 +429,14 @@ int MediaDecoder::getWidth() const
 int MediaDecoder::getHeight() const
 { return decoderCtx_->height; }
 
+int  // TODO : use of float fps is more accurate
+MediaDecoder::getFps() const
+{
+    return (avStream_->avg_frame_rate.den != 0 ?
+            (int)(avStream_->avg_frame_rate.num / avStream_->avg_frame_rate.den)
+            : 0);
+}
+
 int MediaDecoder::getPixelFormat() const
 { return libav_utils::ring_pixel_format(decoderCtx_->pix_fmt); }
 
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index 91f65194f88bacb3240342358b23455f357017f2..0a20a3aa3c1b1e2b99e6db0f9efd0afeabbffb25 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -99,6 +99,7 @@ class MediaDecoder {
 
         int getWidth() const;
         int getHeight() const;
+        int getFps() const;
         int getPixelFormat() const;
 
         void setOptions(const std::map<std::string, std::string>& options);
@@ -109,6 +110,7 @@ class MediaDecoder {
         AVCodec *inputDecoder_ = nullptr;
         AVCodecContext *decoderCtx_ = nullptr;
         AVFormatContext *inputCtx_ = nullptr;
+        AVStream *avStream_ = nullptr;
         std::unique_ptr<Resampler> resampler_;
         int streamIndex_ = -1;
         bool emulateRate_ = false;
diff --git a/src/media/system_codec_container.cpp b/src/media/system_codec_container.cpp
index 66d41336cafeb5551a7743012fe998ddff9ad85c..4ce6b09f44a4f0d3a0efbd2486a1f6c8e84a562c 100644
--- a/src/media/system_codec_container.cpp
+++ b/src/media/system_codec_container.cpp
@@ -40,7 +40,7 @@ namespace ring {
 
 decltype(getGlobalInstance<SystemCodecContainer>)& getSystemCodecContainer = getGlobalInstance<SystemCodecContainer>;
 
-constexpr static auto DEFAULT_VIDEO_BITRATE = 400;
+constexpr static auto DEFAULT_VIDEO_BITRATE = 800; // in Kbits/second
 
 SystemCodecContainer::SystemCodecContainer()
 {
diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp
index 94664c3295812f8624d1d80acf4206023906242d..994d4d18023fb81f17ae2b3374e788a8a643ece8 100644
--- a/src/media/video/sinkclient.cpp
+++ b/src/media/video/sinkclient.cpp
@@ -316,7 +316,7 @@ SinkClient::update(Observable<std::shared_ptr<VideoFrame>>* /*obs*/,
         VideoScaler scaler;
         const int width = f->width();
         const int height = f->height();
-        const int format = VIDEO_PIXFMT_BGRA;
+        const int format = VIDEO_PIXFMT_RGBA;
         const auto bytes = videoFrameSize(format, width, height);
 
         targetData_.resize(bytes);
diff --git a/src/media/video/video_base.h b/src/media/video/video_base.h
index cd6dbad4e1923f87525f4f75f0c6b34480789484..e7299db06050f4526192e521fc3ed1696f6389a0 100644
--- a/src/media/video/video_base.h
+++ b/src/media/video/video_base.h
@@ -58,6 +58,7 @@ namespace ring { namespace video {
 enum VideoPixelFormat {
     VIDEO_PIXFMT_BGRA = -1,
     VIDEO_PIXFMT_YUV420P = -2,
+    VIDEO_PIXFMT_RGBA = -3,
 };
 
 template <typename T> class Observer;
diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp
index a3d5a7b4dcb322c0ce015b2fc9327d8254b2880d..c11f19524c8beb496eb1e7aaafaef7308874e5f1 100644
--- a/src/media/video/video_input.cpp
+++ b/src/media/video/video_input.cpp
@@ -185,6 +185,11 @@ VideoInput::createDecoder()
     }
     decOpts_.width = decoder_->getWidth();
     decOpts_.height = decoder_->getHeight();
+    decOpts_.framerate = decoder_->getFps();
+    RING_INFO("create decoder with video params : size=%dX%d, fps=%d",
+            decOpts_.width,
+            decOpts_.height,
+            decOpts_.framerate);
     foundDecOpts(decOpts_);
 }
 
@@ -266,9 +271,6 @@ VideoInput::initFile(std::string path)
         decOpts_.framerate = 1;
     } else {
         RING_WARN("Guessing file type for %s", path.c_str());
-        // FIXME: proper parsing of FPS etc. should be done in
-        // MediaDecoder, not here.
-        decOpts_.framerate = 25;
     }
 
     return true;
diff --git a/src/ringdht/sips_transport_ice.cpp b/src/ringdht/sips_transport_ice.cpp
index db3d54b7c6a7f38c4304ccb999fd35b2f8abbe68..c9de6027205bfda1d1ff781a491247dc6d7717c7 100644
--- a/src/ringdht/sips_transport_ice.cpp
+++ b/src/ringdht/sips_transport_ice.cpp
@@ -659,10 +659,14 @@ SipsIceTransport::setup()
 void
 SipsIceTransport::handleEvents()
 {
-    std::lock_guard<std::mutex> l(rxMtx_);
-    while (not rxPending_.empty()) {
-        auto pck_it = rxPending_.begin();
-        auto& pck = *pck_it;
+    decltype(rxPending_) rx;
+    {
+        std::lock_guard<std::mutex> l(rxMtx_);
+        rx = std::move(rxPending_);
+    }
+
+    for (auto it = rx.begin(); it != rx.end(); ++it) {
+        auto& pck = *it;
         pj_pool_reset(rdata_.tp_info.pool);
         pj_gettimeofday(&rdata_.pkt_info.timestamp);
         rdata_.pkt_info.len = pck.size();
@@ -670,21 +674,24 @@ SipsIceTransport::handleEvents()
         auto eaten = pjsip_tpmgr_receive_packet(trData_.base.tpmgr, &rdata_);
         if (eaten != rdata_.pkt_info.len) {
             // partial sip packet received
-            auto npck_it = std::next(pck_it);
-            if (npck_it != rxPending_.end()) {
+            auto npck_it = std::next(it);
+            if (npck_it != rx.end()) {
                 // drop current packet, merge reminder with next one
                 auto& npck = *npck_it;
                 npck.insert(npck.begin(), pck.begin()+eaten, pck.end());
-                rxPendingPool_.splice(rxPendingPool_.end(), rxPending_, pck_it);
             } else {
                 // erase eaten part, keep reminder
                 pck.erase(pck.begin(), pck.begin()+eaten);
+                {
+                    std::lock_guard<std::mutex> l(rxMtx_);
+                    rxPending_.splice(rxPending_.begin(), rx, it);
+                }
                 break;
             }
-        } else {
-            rxPendingPool_.splice(rxPendingPool_.end(), rxPending_, pck_it);
         }
     }
+    putBuff(std::move(rx));
+    rxCv_.notify_all();
 }
 
 void
@@ -753,20 +760,22 @@ SipsIceTransport::loop()
         if (state_ != TlsConnectionState::ESTABLISHED and not getTransportBase()->is_shutdown)
             return;
 
-        while (canRead_) {
-            std::lock_guard<std::mutex> l(rxMtx_);
-            if (rxPendingPool_.empty())
-                rxPendingPool_.emplace_back(PJSIP_MAX_PKT_LEN);
-            auto& buf = rxPendingPool_.front();
-            buf.resize(PJSIP_MAX_PKT_LEN);
+        decltype(rxPending_) rx;
+        while (canRead_ or gnutls_record_check_pending(session_)) {
+            if (rx.empty())
+                getBuff(rx, PJSIP_MAX_PKT_LEN);
+            auto& buf = rx.front();
             const auto decrypted_size = gnutls_record_recv(session_, buf.data(), buf.size());
 
             if (decrypted_size > 0/* || transport error */) {
                 buf.resize(decrypted_size);
-                rxPending_.splice(rxPending_.end(), rxPendingPool_, rxPendingPool_.begin());
+                {
+                    std::lock_guard<std::mutex> l(rxMtx_);
+                    rxPending_.splice(rxPending_.end(), rx, rx.begin());
+                }
             } else if (decrypted_size == 0) {
                 /* EOF */
-                shutdown();
+                tlsThread_.stop();
                 break;
             } else if (decrypted_size == GNUTLS_E_AGAIN or
                        decrypted_size == GNUTLS_E_INTERRUPTED) {
@@ -795,6 +804,7 @@ SipsIceTransport::loop()
                 break;
             }
         }
+        putBuff(std::move(rx));
         flushOutputBuff();
     }
 }
@@ -821,7 +831,17 @@ SipsIceTransport::clean()
         cookie_key_.data = nullptr;
         cookie_key_.size = 0;
     }
-    rxPendingPool_.clear();
+    {
+        // make sure all incoming packets are reported before closing
+        std::unique_lock<std::mutex> l(rxMtx_);
+        rxCv_.wait(l, [&](){
+            return rxPending_.empty();
+        });
+    }
+    {
+        std::lock_guard<std::mutex> lk(buffPoolMtx_);
+        buffPool_.clear();
+    }
 
     bool event = state_ == TlsConnectionState::ESTABLISHED;
     closeTlsSession();
@@ -923,19 +943,30 @@ SipsIceTransport::send(pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr,
                       addr_len==sizeof(pj_sockaddr_in6)),
                      PJ_EINVAL);
 
-    tdata->op_key.tdata = tdata;
     tdata->op_key.token = token;
     tdata->op_key.callback = callback;
-    {
+    if (state_ == TlsConnectionState::ESTABLISHED) {
+        decltype(txBuff_) tx;
+        size_t size = tdata->buf.cur - tdata->buf.start;
+        getBuff(tx, (uint8_t*)tdata->buf.start, (uint8_t*)tdata->buf.cur);
+        {
+            std::lock_guard<std::mutex> l(outputBuffMtx_);
+            txBuff_.splice(txBuff_.end(), std::move(tx));
+        }
+        tdata->op_key.tdata = nullptr;
+        if (tdata->op_key.callback)
+            tdata->op_key.callback(getTransportBase(), token, size);
+    } else {
         std::lock_guard<std::mutex> l(outputBuffMtx_);
+        tdata->op_key.tdata = tdata;
         outputBuff_.emplace_back(DelayedTxData{&tdata->op_key, {}});
         if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
             auto& dtd = outputBuff_.back();
             dtd.timeout = clock::now();
             dtd.timeout += std::chrono::milliseconds(pjsip_cfg()->tsx.td);
         }
-        canWrite_ = true;
     }
+    canWrite_ = true;
     cv_.notify_all();
     return PJ_EPENDING;
 }
@@ -944,14 +975,14 @@ pj_status_t
 SipsIceTransport::flushOutputBuff()
 {
     ssize_t status = PJ_SUCCESS;
+
+    // send delayed data first
     while (true) {
         DelayedTxData f;
         {
             std::lock_guard<std::mutex> l(outputBuffMtx_);
-            if (outputBuff_.empty()) {
-                canWrite_ = false;
+            if (outputBuff_.empty())
                 break;
-            }
             else {
                 f = outputBuff_.front();
                 outputBuff_.pop_front();
@@ -967,6 +998,30 @@ SipsIceTransport::flushOutputBuff()
         if (status < 0)
             break;
     }
+    if (status < 0)
+        return status;
+
+    decltype(txBuff_) tx;
+    {
+        std::lock_guard<std::mutex> l(outputBuffMtx_);
+        tx = std::move(txBuff_);
+        canWrite_ = false;
+    }
+    for (auto it = tx.begin(); it != tx.end(); ++it) {
+        const auto& msg = *it;
+        const auto nwritten = gnutls_record_send(session_, msg.data(), msg.size());
+        if (nwritten <= 0) {
+            RING_ERR("gnutls_record_send: %s", gnutls_strerror(nwritten));
+            status = tls_status_from_err(nwritten);
+            {
+                std::lock_guard<std::mutex> l(outputBuffMtx_);
+                txBuff_.splice(txBuff_.begin(), tx, it, tx.end());
+                canWrite_ = true;
+            }
+            break;
+        }
+    }
+    putBuff(std::move(tx));
     return status > 0 ? PJ_SUCCESS : (pj_status_t)status;
 }
 
@@ -1009,6 +1064,40 @@ SipsIceTransport::shutdown()
     cv_.notify_all();
 }
 
+void
+SipsIceTransport::getBuff(decltype(buffPool_)& l, const uint8_t* b, const uint8_t* e)
+{
+    std::lock_guard<std::mutex> lk(buffPoolMtx_);
+    if (buffPool_.empty())
+        l.emplace_back(b, e);
+    else {
+        l.splice(l.end(), buffPool_, buffPool_.begin());
+        auto& buf = l.back();
+        buf.resize(std::distance(b, e));
+        std::copy(b, e, buf.begin());
+    }
+}
+
+void
+SipsIceTransport::getBuff(decltype(buffPool_)& l, const size_t s)
+{
+    std::lock_guard<std::mutex> lk(buffPoolMtx_);
+    if (buffPool_.empty())
+        l.emplace_back(s);
+    else {
+        l.splice(l.end(), buffPool_, buffPool_.begin());
+        auto& buf = l.back();
+        buf.resize(s);
+    }
+}
+
+void
+SipsIceTransport::putBuff(decltype(buffPool_)&& l)
+{
+    std::lock_guard<std::mutex> lk(buffPoolMtx_);
+    buffPool_.splice(buffPool_.end(), l);
+}
+
 pj_status_t
 SipsIceTransport::tls_status_from_err(int err)
 {
diff --git a/src/ringdht/sips_transport_ice.h b/src/ringdht/sips_transport_ice.h
index 1517b2e6c5ae059062346402229cd89fdd390c56..f6d5af410d26e2e9ac301526631fac11266bddf7 100644
--- a/src/ringdht/sips_transport_ice.h
+++ b/src/ringdht/sips_transport_ice.h
@@ -161,13 +161,21 @@ private:
     ssize_t trySend(pjsip_tx_data_op_key* tdata);
     pj_status_t flushOutputBuff();
     std::list<DelayedTxData> outputBuff_;
+    std::list<std::vector<uint8_t>> txBuff_;
     std::mutex outputBuffMtx_;
 
     std::mutex rxMtx_;
+    std::condition_variable_any rxCv_;
     std::list<std::vector<uint8_t>> rxPending_;
-    std::list<std::vector<uint8_t>> rxPendingPool_;
     pjsip_rx_data rdata_;
 
+    // data buffer pool
+    std::list<std::vector<uint8_t>> buffPool_;
+    std::mutex buffPoolMtx_;
+    void getBuff(decltype(buffPool_)& l, const uint8_t* b, const uint8_t* e);
+    void getBuff(decltype(buffPool_)& l, const size_t s);
+    void putBuff(decltype(buffPool_)&& l);
+
     // GnuTLS <-> ICE
     ssize_t tlsSend(const void*, size_t);
     ssize_t tlsRecv(void* d , size_t s);