diff --git a/src/dring/account_const.h b/src/dring/account_const.h
index 379b4d4b4914a003cd41f9c4b293c18d9d1cb0ad..cce6bc96795cc9a54ec11642a66386fcbbbbd23f 100644
--- a/src/dring/account_const.h
+++ b/src/dring/account_const.h
@@ -223,6 +223,9 @@ constexpr static const char FRAME_RATE         [] = "CodecInfo.frameRate";
 constexpr static const char BITRATE            [] = "CodecInfo.bitrate";
 constexpr static const char MIN_BITRATE        [] = "CodecInfo.min_bitrate";
 constexpr static const char MAX_BITRATE        [] = "CodecInfo.max_bitrate";
+constexpr static const char QUALITY            [] = "CodecInfo.quality";
+constexpr static const char MIN_QUALITY        [] = "CodecInfo.min_quality";
+constexpr static const char MAX_QUALITY        [] = "CodecInfo.max_quality";
 constexpr static const char CHANNEL_NUMBER     [] = "CodecInfo.channelNumber";
 
 } //namespace DRing::Account::ConfProperties::CodecInfo
diff --git a/src/media/media_codec.cpp b/src/media/media_codec.cpp
index d34075377168e55ee804b046acc2fe76f69d3f5c..b1939b204c34a76aaccdfb28ead5380b2a7df54c 100644
--- a/src/media/media_codec.cpp
+++ b/src/media/media_codec.cpp
@@ -42,7 +42,10 @@ generateId()
 SystemCodecInfo::SystemCodecInfo(unsigned avcodecId, const std::string name,
                                  std::string libName,
                                  MediaType mediaType, CodecType codecType,
-                                 unsigned bitrate, unsigned payloadType)
+                                 unsigned bitrate,
+                                 unsigned payloadType,
+                                 unsigned minQuality,
+                                 unsigned maxQuality)
     : id(generateId())
     , avcodecId(avcodecId)
     , name(name)
@@ -51,6 +54,8 @@ SystemCodecInfo::SystemCodecInfo(unsigned avcodecId, const std::string name,
     , mediaType(mediaType)
     , payloadType(payloadType)
     , bitrate(bitrate)
+    , minQuality(minQuality)
+    , maxQuality(maxQuality)
 {}
 
 SystemCodecInfo::~SystemCodecInfo()
@@ -94,11 +99,13 @@ SystemVideoCodecInfo::SystemVideoCodecInfo(unsigned m_avcodecId,
                                            std::string m_libName,
                                            CodecType m_type,
                                            unsigned m_bitrate,
+                                           unsigned m_minQuality,
+                                           unsigned m_maxQuality,
                                            unsigned m_payloadType,
                                            unsigned m_frameRate,
                                            unsigned m_profileId)
     : SystemCodecInfo(m_avcodecId, m_name, m_libName, MEDIA_VIDEO,
-                      m_type, m_bitrate, m_payloadType)
+                      m_type, m_bitrate, m_payloadType, m_minQuality, m_maxQuality)
     , frameRate(m_frameRate), profileId(m_profileId)
 {}
 
@@ -124,7 +131,10 @@ AccountCodecInfo::AccountCodecInfo(const SystemCodecInfo& sysCodecInfo)
     , isActive(true)
     , payloadType(sysCodecInfo.payloadType)
     , bitrate(sysCodecInfo.bitrate)
-{}
+{
+    if (sysCodecInfo.minQuality != SystemCodecInfo::DEFAULT_NO_QUALITY)
+        quality = SystemCodecInfo::DEFAULT_CODEC_QUALITY;
+}
 
 AccountCodecInfo::~AccountCodecInfo()
 {}
@@ -187,6 +197,9 @@ AccountVideoCodecInfo::getCodecSpecifications()
         {DRing::Account::ConfProperties::CodecInfo::BITRATE, to_string(bitrate)},
         {DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE, to_string(systemCodecInfo.maxBitrate)},
         {DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE, to_string(systemCodecInfo.minBitrate)},
+        {DRing::Account::ConfProperties::CodecInfo::QUALITY, to_string(quality)},
+        {DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY, to_string(systemCodecInfo.maxQuality)},
+        {DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY, to_string(systemCodecInfo.minQuality)},
         {DRing::Account::ConfProperties::CodecInfo::FRAME_RATE, to_string(frameRate)}
         };
 }
@@ -201,6 +214,10 @@ AccountVideoCodecInfo::setCodecSpecifications(const std::map<std::string, std::s
     it = details.find(DRing::Account::ConfProperties::CodecInfo::FRAME_RATE);
     if (it != details.end())
         frameRate = ring::stoi(it->second);
+
+    it = details.find(DRing::Account::ConfProperties::CodecInfo::QUALITY);
+    if (it != details.end())
+        quality = ring::stoi(it->second);
 }
 
 AccountVideoCodecInfo::~AccountVideoCodecInfo()
diff --git a/src/media/media_codec.h b/src/media/media_codec.h
index 5f2d6a3f36b89264a85e921a1542ef3f3befcd9c..46ed7896ea7211bc3ebdfa0fc6deb1ce49b19660 100644
--- a/src/media/media_codec.h
+++ b/src/media/media_codec.h
@@ -59,16 +59,29 @@ enum MediaType : unsigned {
  */
 struct SystemCodecInfo
 {
+    static constexpr unsigned DEFAULT_CODEC_QUALITY {35};
+#ifdef RING_VIDEO
+    static constexpr unsigned DEFAULT_H264_MIN_QUALITY {50};
+    static constexpr unsigned DEFAULT_H264_MAX_QUALITY {25};
+    static constexpr unsigned DEFAULT_VP8_MIN_QUALITY {50};
+    static constexpr unsigned DEFAULT_VP8_MAX_QUALITY {20};
+#endif
+
+    // indicates that the codec does not use quality factor
+    static constexpr unsigned DEFAULT_NO_QUALITY {0};
+
+    static constexpr unsigned DEFAULT_MIN_BITRATE {250};
+    static constexpr unsigned DEFAULT_MAX_BITRATE {2000};
+
     SystemCodecInfo(unsigned avcodecId, const std::string name,
                     std::string libName, MediaType mediaType,
                     CodecType codecType = CODEC_NONE, unsigned bitrate = 0,
-                    unsigned payloadType = 0);
+                    unsigned payloadType = 0,
+                    unsigned m_minQuality = DEFAULT_NO_QUALITY,
+                    unsigned m_maxQuality = DEFAULT_NO_QUALITY);
 
     virtual ~SystemCodecInfo();
 
-    static constexpr unsigned DEFAULT_MIN_BITRATE {250};
-    static constexpr unsigned DEFAULT_MAX_BITRATE {2000};
-
     /* generic codec information */
     unsigned id; /* id of the codec used with dbus */
     unsigned  avcodecId;  /* read as AVCodecID libav codec identifier */
@@ -82,6 +95,8 @@ struct SystemCodecInfo
     unsigned bitrate;
     unsigned minBitrate = DEFAULT_MIN_BITRATE;
     unsigned maxBitrate = DEFAULT_MAX_BITRATE;
+    unsigned minQuality = DEFAULT_NO_QUALITY;
+    unsigned maxQuality = DEFAULT_NO_QUALITY;
 };
 
 /*
@@ -114,7 +129,10 @@ struct SystemVideoCodecInfo : SystemCodecInfo
     SystemVideoCodecInfo(unsigned avcodecId, const std::string name,
                          std::string libName, CodecType type = CODEC_NONE,
                          unsigned bitrate = 0,
-                         unsigned payloadType = 0, unsigned frameRate = 0,
+                         unsigned m_minQuality = 0,
+                         unsigned m_maxQuality = 0,
+                         unsigned payloadType = 0,
+                         unsigned frameRate = 0,
                          unsigned profileId = 0);
 
     ~SystemVideoCodecInfo();
@@ -142,6 +160,7 @@ struct AccountCodecInfo
     /* account custom values */
     unsigned payloadType;
     unsigned bitrate;
+    unsigned quality;
     std::map<std::string, std::string>  getCodecSpecifications();
 
 };
diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index 6705bdeac1221f96b2a659404c0fec3f6550e875..ea93b19bf9d8324e15371fa92a7165ea2d4bb31b 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -2,6 +2,7 @@
  *  Copyright (C) 2013-2015 Savoir-faire Linux Inc.
  *
  *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
+ *  Author: Eloi Bail <Eloi.Bail@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
@@ -75,6 +76,7 @@ void MediaEncoder::setOptions(const MediaDescription& args)
 
     av_dict_set(&options_, "payload_type", ring::to_string(args.payload_type).c_str(), 0);
     av_dict_set(&options_, "max_rate", ring::to_string(args.codec->bitrate).c_str(), 0);
+    av_dict_set(&options_, "crf", ring::to_string(args.codec->quality).c_str(), 0);
 
     if (args.codec->systemCodecInfo.mediaType == MEDIA_AUDIO) {
         auto accountAudioCodec = std::static_pointer_cast<AccountAudioCodecInfo>(args.codec);
@@ -140,17 +142,30 @@ MediaEncoder::openOutput(const char *filename,
 
     prepareEncoderContext(args.codec->systemCodecInfo.mediaType == MEDIA_VIDEO);
     auto maxBitrate = 1000 * atoi(av_dict_get(options_, "max_rate", NULL, 0)->value);
-    encoderCtx_->rc_buffer_size = maxBitrate;
-    RING_DBG("Using max bitrate %d", maxBitrate );
+    auto crf = atoi(av_dict_get(options_, "crf", NULL, 0)->value);
+
 
     /* let x264 preset override our encoder settings */
     if (args.codec->systemCodecInfo.avcodecId == AV_CODEC_ID_H264) {
         extractProfileLevelID(args.parameters, encoderCtx_);
         forcePresetX264();
-        // For H264 : define max bitrate in rc_max_rate
-        encoderCtx_->rc_max_rate = maxBitrate;
+        // For H264 :
+        // 1- if quality is set use it
+        // 2- otherwise set rc_max_rate and rc_buffer_size
+        if (crf != SystemCodecInfo::DEFAULT_NO_QUALITY) {
+            av_opt_set(encoderCtx_->priv_data, "crf", av_dict_get(options_, "crf", NULL, 0)->value, 0);
+            RING_DBG("Using quality factor %d", crf);
+        } else {
+            encoderCtx_->rc_buffer_size = maxBitrate;
+            encoderCtx_->rc_max_rate = maxBitrate;
+            RING_DBG("Using max bitrate %d", maxBitrate );
+        }
 
     } else if (args.codec->systemCodecInfo.avcodecId == AV_CODEC_ID_VP8) {
+        // For VP8 :
+        // 1- if quality is set use it
+        // bitrate need to be set. The target bitrate becomes the maximum allowed bitrate
+        // 2- otherwise set rc_max_rate and rc_buffer_size
         // Using information given on this page:
         // http://www.webmproject.org/docs/encoder-parameters/
         av_opt_set(encoderCtx_->priv_data, "quality", "realtime", 0);
@@ -161,11 +176,22 @@ MediaEncoder::openOutput(const char *filename,
         encoderCtx_->qmin = 4;
         encoderCtx_->qmax = 56;
         encoderCtx_->gop_size = 999999;
-        // For VP8 : define max bitrate in bit_rate
+
+        encoderCtx_->rc_buffer_size = maxBitrate;
         encoderCtx_->bit_rate = maxBitrate;
+        if (crf != SystemCodecInfo::DEFAULT_NO_QUALITY) {
+            av_opt_set(encoderCtx_->priv_data, "crf", av_dict_get(options_, "crf", NULL, 0)->value, 0);
+            RING_DBG("Using quality factor %d", crf);
+        } else {
+            RING_DBG("Using Max bitrate %d", maxBitrate);
+        }
     } else if (args.codec->systemCodecInfo.avcodecId == AV_CODEC_ID_MPEG4) {
-        // For MPEG4 : define max bitrate in bit_rate
-        encoderCtx_->bit_rate = maxBitrate;
+        // For MPEG4 :
+        // No CRF avaiable.
+        // Use CBR (set bitrate)
+        encoderCtx_->rc_buffer_size = maxBitrate;
+        encoderCtx_->bit_rate = encoderCtx_->rc_min_rate = encoderCtx_->rc_max_rate =  maxBitrate;
+        RING_DBG("Using Max bitrate %d", maxBitrate);
     }
 
     int ret;
diff --git a/src/media/system_codec_container.cpp b/src/media/system_codec_container.cpp
index 3a8a0ae2585fe3fb450c87ab478db126b310b308..ec23e5d92b261408cd93d253fbff801ab146efdb 100644
--- a/src/media/system_codec_container.cpp
+++ b/src/media/system_codec_container.cpp
@@ -46,18 +46,28 @@ SystemCodecContainer::~SystemCodecContainer()
 void
 SystemCodecContainer::initCodecConfig()
 {
+#ifdef RING_VIDEO
+    auto minH264 = SystemCodecInfo::DEFAULT_H264_MIN_QUALITY;
+    auto maxH264 = SystemCodecInfo::DEFAULT_H264_MAX_QUALITY;
+    auto minVP8 = SystemCodecInfo::DEFAULT_VP8_MIN_QUALITY;
+    auto maxVP8 = SystemCodecInfo::DEFAULT_VP8_MAX_QUALITY;
+#endif
     availableCodecList_ = {
 #ifdef RING_VIDEO
         /* Define supported video codec*/
         std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_H264,
                                                "H264", "libx264",
                                                CODEC_ENCODER_DECODER,
-                                               DEFAULT_VIDEO_BITRATE),
+                                               DEFAULT_VIDEO_BITRATE,
+                                               minH264,
+                                               maxH264),
 
         std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_VP8,
                                                "VP8", "libvpx",
                                                CODEC_ENCODER_DECODER,
-                                               DEFAULT_VIDEO_BITRATE),
+                                               DEFAULT_VIDEO_BITRATE,
+                                               minVP8,
+                                               maxVP8),
 
         std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_MPEG4,
                                                "MP4V-ES", "mpeg4",
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index 219d4da360f4e515154c1e0530c1e59c07c5c825..0727065548e33801efa1ae256bbc6b2be42e2e64 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -310,10 +310,44 @@ VideoRtpSession::checkPeerPacketLoss()
         return NO_PACKET_LOSS_CALCULATED;
 }
 
+unsigned
+VideoRtpSession::getLowerQuality()
+{
+    // if lower quality was stored we return it
+    unsigned quality = videoBitrateInfo_.videoQualityCurrent;
+    while ( not histoQuality_.empty()) {
+        quality = histoQuality_.back();
+        histoQuality_.pop_back();
+        if (quality > videoBitrateInfo_.videoQualityCurrent)
+            return quality;
+    }
+
+    // if no appropriate quality found, calculate it with dichotomie
+    quality = (videoBitrateInfo_.videoQualityCurrent + videoBitrateInfo_.videoQualityMin) / 2;
+    return quality;
+}
+
+unsigned
+VideoRtpSession::getLowerBitrate()
+{
+    // if a lower bitrate was stored we return it
+    unsigned bitrate = videoBitrateInfo_.videoBitrateCurrent;
+    while ( not histoBitrate_.empty()) {
+        bitrate = histoBitrate_.back();
+        histoBitrate_.pop_back();
+        if (bitrate < videoBitrateInfo_.videoBitrateCurrent)
+            return bitrate;
+    }
+
+    // if no appropriate bitrate found, calculate it with dichotomie
+    bitrate = (videoBitrateInfo_.videoBitrateCurrent + videoBitrateInfo_.videoBitrateMin) / 2;
+    return bitrate;
+}
+
 void
-VideoRtpSession::adaptBitrate()
+VideoRtpSession::adaptQualityAndBitrate()
 {
-    bool needToCheckBitrate = false;
+    bool needToCheckQuality = false;
     bool mediaRestartNeeded = false;
     float packetLostRate = 0.0;
 
@@ -321,65 +355,87 @@ VideoRtpSession::adaptBitrate()
     auto rtcpLongCheckTimer = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now() - lastLongRTCPCheck_);
 
     if (rtcpCheckTimer.count() >= RTCP_CHECKING_INTERVAL) {
-        needToCheckBitrate = true;
+        needToCheckQuality = true;
         lastRTCPCheck_ = std::chrono::system_clock::now();
     }
 
     if (rtcpLongCheckTimer.count() >= RTCP_LONG_CHECKING_INTERVAL) {
-        needToCheckBitrate = true;
+        needToCheckQuality = true;
         lastLongRTCPCheck_ = std::chrono::system_clock::now();
-        //we force iterative bitrate adaptation
+        // we force iterative bitrate adaptation
         videoBitrateInfo_.cptBitrateChecking = 0;
     }
 
 
-    if (needToCheckBitrate) {
+    if (needToCheckQuality) {
+
         videoBitrateInfo_.cptBitrateChecking++;
-        auto oldBitrate = videoBitrateInfo_.videoBitrateCurrent;
 
-        //packetLostRate is not already available. Do nothing
+        // packetLostRate is not already available. Do nothing
         if ((packetLostRate = checkPeerPacketLoss()) == NO_PACKET_LOSS_CALCULATED) {
-            //we force iterative bitrate adaptation
+            // we force iterative bitrate adaptation
             videoBitrateInfo_.cptBitrateChecking = 0;
 
-        //too much packet lost : decrease bitrate
+        // too much packet lost : decrease quality and bitrate
         } else if (packetLostRate >= videoBitrateInfo_.packetLostThreshold) {
 
-            //calculate new bitrate by dichotomie
-            videoBitrateInfo_.videoBitrateCurrent =
-                (videoBitrateInfo_.videoBitrateCurrent + videoBitrateInfo_.videoBitrateMin) / 2;
+            // calculate new quality by dichotomie
+            videoBitrateInfo_.videoQualityCurrent = getLowerQuality();
+
+            // calculate new bitrate by dichotomie
+            videoBitrateInfo_.videoBitrateCurrent =  getLowerBitrate();
+
+            // boundaries low
+            if (videoBitrateInfo_.videoQualityCurrent > videoBitrateInfo_.videoQualityMin)
+                videoBitrateInfo_.videoQualityCurrent = videoBitrateInfo_.videoQualityMin;
 
-            //boundaries low
             if (videoBitrateInfo_.videoBitrateCurrent < videoBitrateInfo_.videoBitrateMin)
                 videoBitrateInfo_.videoBitrateCurrent = videoBitrateInfo_.videoBitrateMin;
 
-            //we force iterative bitrate adaptation
+
+            // we force iterative bitrate and quality adaptation
             videoBitrateInfo_.cptBitrateChecking = 0;
 
-            //asynchronous A/V media restart
-            if (videoBitrateInfo_.videoBitrateCurrent != oldBitrate)
+            // asynchronous A/V media restart
+            // we give priority to quality
+            if (((videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY) &&
+                    (videoBitrateInfo_.videoQualityCurrent !=  (histoQuality_.empty() ? 0 : histoQuality_.back()))) ||
+                ((videoBitrateInfo_.videoQualityCurrent == SystemCodecInfo::DEFAULT_NO_QUALITY) &&
+                    (videoBitrateInfo_.videoBitrateCurrent !=  (histoBitrate_.empty() ? 0 : histoBitrate_.back()))))
                 mediaRestartNeeded = true;
 
-        //no packet lost: increase bitrate
+
+        // no packet lost: increase quality and bitrate
         } else if (videoBitrateInfo_.cptBitrateChecking <= videoBitrateInfo_.maxBitrateChecking) {
 
-            //calculate new bitrate by dichotomie
+            // calculate new quality by dichotomie
+            videoBitrateInfo_.videoQualityCurrent =
+                (videoBitrateInfo_.videoQualityCurrent + videoBitrateInfo_.videoQualityMax) / 2;
+
+            // calculate new bitrate by dichotomie
             videoBitrateInfo_.videoBitrateCurrent =
                 ( videoBitrateInfo_.videoBitrateCurrent + videoBitrateInfo_.videoBitrateMax) / 2;
 
-            //boundaries high
+            // boundaries high
+            if (videoBitrateInfo_.videoQualityCurrent < videoBitrateInfo_.videoQualityMax)
+                videoBitrateInfo_.videoQualityCurrent = videoBitrateInfo_.videoQualityMax;
+
             if (videoBitrateInfo_.videoBitrateCurrent > videoBitrateInfo_.videoBitrateMax)
                 videoBitrateInfo_.videoBitrateCurrent = videoBitrateInfo_.videoBitrateMax;
 
-            //asynchronous A/V media restart
-            if (videoBitrateInfo_.videoBitrateCurrent != oldBitrate)
+            // asynchronous A/V media restart
+            // we give priority to quality
+            if (((videoBitrateInfo_.videoQualityCurrent != SystemCodecInfo::DEFAULT_NO_QUALITY) &&
+                    (videoBitrateInfo_.videoQualityCurrent !=  (histoQuality_.empty() ? 0 : histoQuality_.back()))) ||
+                ((videoBitrateInfo_.videoQualityCurrent == SystemCodecInfo::DEFAULT_NO_QUALITY) &&
+                    (videoBitrateInfo_.videoBitrateCurrent !=  (histoBitrate_.empty() ? 0 : histoBitrate_.back()))))
                 mediaRestartNeeded = true;
 
             if (videoBitrateInfo_.cptBitrateChecking == videoBitrateInfo_.maxBitrateChecking)
                 lastLongRTCPCheck_ = std::chrono::system_clock::now();
 
         } else {
-            //nothing we reach maximal tries
+            // nothing we reach maximal tries
         }
     }
 
@@ -387,10 +443,11 @@ VideoRtpSession::adaptBitrate()
         storeVideoBitrateInfo();
         const auto& cid = callID_;
 
-        RING_WARN("[%u/%u] packetLostRate=%f -> change bitrate to %d",
+        RING_WARN("[%u/%u] packetLostRate=%f -> change quality to %d bitrate to %d",
                 videoBitrateInfo_.cptBitrateChecking,
                 videoBitrateInfo_.maxBitrateChecking,
                 packetLostRate,
+                videoBitrateInfo_.videoQualityCurrent,
                 videoBitrateInfo_.videoBitrateCurrent);
 
         runOnMainThread([cid]{
@@ -399,7 +456,6 @@ VideoRtpSession::adaptBitrate()
             });
     }
 }
-
 void
 VideoRtpSession::getVideoBitrateInfo() {
     auto codecVideo = std::static_pointer_cast<ring::AccountVideoCodecInfo>(send_.codec);
@@ -408,12 +464,15 @@ VideoRtpSession::getVideoBitrateInfo() {
             (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::BITRATE])),
             (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE])),
             (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE])),
+            (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::QUALITY])),
+            (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY])),
+            (unsigned)(ring::stoi(codecVideo->getCodecSpecifications()[DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY])),
             videoBitrateInfo_.cptBitrateChecking,
             videoBitrateInfo_.maxBitrateChecking,
             videoBitrateInfo_.packetLostThreshold,
         };
     } else {
-        videoBitrateInfo_ = {0,0,0,0,0,0};
+        videoBitrateInfo_ = {0,0,0,0,0,0,0,0,0};
     }
 }
 
@@ -425,8 +484,21 @@ VideoRtpSession::storeVideoBitrateInfo() {
         codecVideo->setCodecSpecifications({
             {DRing::Account::ConfProperties::CodecInfo::BITRATE, ring::to_string(videoBitrateInfo_.videoBitrateCurrent)},
             {DRing::Account::ConfProperties::CodecInfo::MIN_BITRATE, ring::to_string(videoBitrateInfo_.videoBitrateMin)},
-            {DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE, ring::to_string(videoBitrateInfo_.videoBitrateMax)}
+            {DRing::Account::ConfProperties::CodecInfo::MAX_BITRATE, ring::to_string(videoBitrateInfo_.videoBitrateMax)},
+            {DRing::Account::ConfProperties::CodecInfo::QUALITY, ring::to_string(videoBitrateInfo_.videoQualityCurrent)},
+            {DRing::Account::ConfProperties::CodecInfo::MIN_QUALITY, ring::to_string(videoBitrateInfo_.videoQualityMin)},
+            {DRing::Account::ConfProperties::CodecInfo::MAX_QUALITY, ring::to_string(videoBitrateInfo_.videoQualityMax)}
         });
+
+        if (histoQuality_.size() > MAX_SIZE_HISTO_QUALITY_)
+            histoQuality_.pop_front();
+
+        if (histoBitrate_.size() > MAX_SIZE_HISTO_BITRATE_)
+            histoBitrate_.pop_front();
+
+        histoQuality_.push_back(videoBitrateInfo_.videoQualityCurrent);
+        histoBitrate_.push_back(videoBitrateInfo_.videoBitrateCurrent);
+
     }
 }
 bool
@@ -439,7 +511,7 @@ VideoRtpSession::setupRtcpChecker()
 void
 VideoRtpSession::processRtcpChecker()
 {
-    adaptBitrate();
+    adaptQualityAndBitrate();
     rtcpCheckerThread_.wait_for(std::chrono::seconds(RTCP_CHECKING_INTERVAL));
 }
 
diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h
index ca469b828f203d49ac2bc65f1ceee19f9bd53392..5010cb62f46d9c2ebab71a21237b9ce399fb7faa 100644
--- a/src/media/video/video_rtp_session.h
+++ b/src/media/video/video_rtp_session.h
@@ -45,6 +45,9 @@ struct VideoBitrateInfo {
     unsigned videoBitrateCurrent;
     unsigned videoBitrateMin;
     unsigned videoBitrateMax;
+    unsigned videoQualityCurrent;
+    unsigned videoQualityMin;
+    unsigned videoQualityMax;
     unsigned cptBitrateChecking;
     unsigned maxBitrateChecking;
     float packetLostThreshold;
@@ -92,19 +95,28 @@ private:
     uint16_t initSeqVal_ = 0;
 
     float checkPeerPacketLoss();
-    void adaptBitrate();
+    unsigned getLowerQuality();
+    unsigned getLowerBitrate();
+    void adaptQualityAndBitrate();
     void storeVideoBitrateInfo();
     void getVideoBitrateInfo();
 
 
-    //interval in seconds between RTCP checkings
+    // interval in seconds between RTCP checkings
     const unsigned RTCP_CHECKING_INTERVAL {4};
-    //long interval in seconds between RTCP checkings
+    // long interval in seconds between RTCP checkings
     const unsigned RTCP_LONG_CHECKING_INTERVAL {30};
-    //not packet loss can be calculated as no data in input
+    // no packet loss can be calculated as no data in input
     static constexpr float NO_PACKET_LOSS_CALCULATED {-1.0};
-    //bitrate info struct
-    VideoBitrateInfo videoBitrateInfo_ = {0,0,0,0, MAX_ADAPTATIVE_BITRATE_ITERATION, PACKET_LOSS_THRESHOLD};
+    // bitrate and quality info struct
+    VideoBitrateInfo videoBitrateInfo_ = {0,0,0,0,0,0,0, MAX_ADAPTATIVE_BITRATE_ITERATION, PACKET_LOSS_THRESHOLD};
+    // previous quality and bitrate used if quality or bitrate need to be decreased
+    std::list<unsigned> histoQuality_ {};
+    std::list<unsigned> histoBitrate_ {};
+    // max size of quality and bitrate historic
+    static constexpr unsigned MAX_SIZE_HISTO_QUALITY_ {30};
+    static constexpr unsigned MAX_SIZE_HISTO_BITRATE_ {100};
+
     //5 tries in a row
     static constexpr unsigned  MAX_ADAPTATIVE_BITRATE_ITERATION {5};
     //packet loss threshold