diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak
index 063317b0220214de227f671b88be90356027d275..2d0d1d1eea29c3cb3349d9a3b5e38b62c341898f 100644
--- a/contrib/src/ffmpeg/rules.mak
+++ b/contrib/src/ffmpeg/rules.mak
@@ -76,6 +76,7 @@ FFMPEGCONF += \
 FFMPEGCONF += \
 	--enable-parser=h263 \
 	--enable-parser=h264 \
+	--enable-parser=hevc \
 	--enable-parser=mpeg4video \
 	--enable-parser=vp8 \
 	--enable-parser=vp9 \
@@ -197,7 +198,8 @@ FFMPEGCONF += \
 	--enable-mediacodec \
 	--enable-decoder=vp8_mediacodec \
 	--enable-decoder=h264_mediacodec \
-	--enable-decoder=mpeg4_mediacodec
+	--enable-decoder=mpeg4_mediacodec \
+	--enable-decoder=hevc_mediacodec
 # ASM not working on Android x86 https://trac.ffmpeg.org/ticket/4928
 ifeq ($(ARCH),i386)
 FFMPEGCONF += --disable-asm
@@ -221,9 +223,11 @@ FFMPEGCONF += \
 	--enable-hwaccel=h263_vaapi \
 	--enable-hwaccel=vp8_vaapi \
 	--enable-hwaccel=mjpeg_vaapi \
+	--enable-hwaccel=hevc_vaapi \
 	--enable-encoder=h264_vaapi \
 	--enable-encoder=vp8_vaapi \
-	--enable-encoder=mjpeg_vaapi
+	--enable-encoder=mjpeg_vaapi \
+	--enable-encoder=hevc_vaapi
 # ffnvcodec is not supported on ARM then we enable it here for i386 and x86_64
 ifeq ($(ARCH),$(filter $(ARCH),i386 x86_64))
 FFMPEGCONF += --enable-cuvid \
@@ -250,7 +254,9 @@ FFMPEGCONF += \
 	--enable-hwaccel=h263_videotoolbox \
 	--enable-hwaccel=h264_videotoolbox \
 	--enable-hwaccel=mpeg4_videotoolbox \
+	--enable-hwaccel=hevc_videotoolbox \
 	--enable-encoder=h264_videotoolbox \
+	--enable-encoder=hevc_videotoolbox \
 	--disable-securetransport
 endif
 
@@ -260,7 +266,9 @@ FFMPEGCONF += \
 	--enable-hwaccel=h263_videotoolbox \
 	--enable-hwaccel=h264_videotoolbox \
 	--enable-hwaccel=mpeg4_videotoolbox \
+	--enable-hwaccel=hevc_videotoolbox \
 	--enable-encoder=h264_videotoolbox \
+	--enable-encoder=hevc_videotoolbox \
 	--target-os=darwin \
 	--enable-cross-compile \
 	--arch=$(ARCH) \
diff --git a/src/account.cpp b/src/account.cpp
index 9670189ac4ed54aecebd081d8b84feb3c249daed..e315e3c7f4ad21382988648aff9c92fa050af0a5 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -181,6 +181,8 @@ Account::loadDefaultCodecs()
     // default codec are system codecs
     auto systemCodecList = systemCodecContainer_->getSystemCodecInfoList();
 
+    accountCodecInfoList_.empty();
+
     for (const auto& systemCodec: systemCodecList) {
         // As defined in SDP RFC, only select a codec if he can encode and decode
         if ((systemCodec->codecType & CODEC_ENCODER_DECODER) != CODEC_ENCODER_DECODER)
@@ -353,7 +355,12 @@ Account::setActiveCodecs(const std::vector<unsigned>& list)
             ++order;
         }
     }
+    sortCodec();
+}
 
+void
+Account::sortCodec()
+{
     std::sort(std::begin(accountCodecInfoList_),
               std::end  (accountCodecInfoList_),
               [](const std::shared_ptr<AccountCodecInfo>& a,
diff --git a/src/account.h b/src/account.h
index 052973192f8b6505bd9f6f4d6bddce16e8782952..a8913ddbc9119ca6e7fb601b4915d30b7cbecaa3 100644
--- a/src/account.h
+++ b/src/account.h
@@ -316,6 +316,13 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
          */
         virtual void connectivityChanged() {};
 
+        /**
+         * Helper function used to load the default codec order from the codec factory
+         */
+        void loadDefaultCodecs();
+
+        void setCodecActive(unsigned codecId);
+
     public: // virtual methods that has to be implemented by concrete classes
         /**
          * This method is called to request removal of possible account traces on the system,
@@ -326,11 +333,6 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
     private:
         NON_COPYABLE(Account);
 
-        /**
-         * Helper function used to load the default codec order from the codec factory
-         */
-        void loadDefaultCodecs();
-
         /**
          * Set of call's ID attached to the account.
          */
@@ -493,7 +495,8 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
         std::shared_ptr<AccountCodecInfo> searchCodecByName(const std::string& name, MediaType mediaType);
         std::vector<unsigned> getAccountCodecInfoIdList(MediaType mediaType) const;
         void setAllCodecsActive(MediaType mediaType, bool active);
-        void setCodecActive(unsigned codecId);
+        void sortCodec();
+
 };
 
 static inline std::ostream&
diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp
index c4237a512c2a65ea473ebbab37111928d595e99e..938d8854eba5fbaeb34c27a23042e1af556c0a35 100644
--- a/src/client/videomanager.cpp
+++ b/src/client/videomanager.cpp
@@ -37,6 +37,7 @@
 #include "dring/media_const.h"
 #include "libav_utils.h"
 #include "call_const.h"
+#include "system_codec_container.h"
 
 #include <functional>
 #include <memory>
@@ -574,6 +575,23 @@ setEncodingAccelerated(bool state)
     jami::Manager::instance().videoPreferences.setEncodingAccelerated(state);
     jami::Manager::instance().saveConfig();
 #endif
+    // refresh codec container + setH265
+    jami::getSystemCodecContainer()->initCodecConfig();
+    auto accIdList = jami::Manager::instance().getAccountList();
+    for (const auto accId : accIdList) {
+        auto acc = jami::Manager::instance().accountFactory.getAccount(accId);
+        if (!acc)
+            continue;
+        // Save activated codec
+        auto activeCodecs = acc->getActiveCodecs();
+        // Refresh codec list for the account
+        acc->loadDefaultCodecs();
+        // Activate H265 if it is available, if not ignore
+        acc->setCodecActive(AV_CODEC_ID_H265);
+        // Reactivate saved codec
+        acc->setActiveCodecs(activeCodecs);
+        jami::Manager::instance().saveConfig(acc);
+    }
 }
 
 #if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS)
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index b8efc5a97ee76fe1a9cdaa06c446f236a4e44e04..a64325512e98a092ad6d26738e9cf45b1531b1cc 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -2746,6 +2746,7 @@ JamiAccount::setActiveCodecs(const std::vector<unsigned>& list)
     if (!hasActiveCodec(MEDIA_AUDIO))
         setCodecActive(AV_CODEC_ID_OPUS);
     if (!hasActiveCodec(MEDIA_VIDEO)) {
+        setCodecActive(AV_CODEC_ID_H265);
         setCodecActive(AV_CODEC_ID_H264);
         setCodecActive(AV_CODEC_ID_VP8);
     }
diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index 2d3b15816458ccffe6efe3c497415d7613afe06c..3c9f047d5c43227db0a2fa11dfb9b98d5944ca4b 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -233,14 +233,14 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr
                 }
                 accel_->setDetails(encoderCtx);
                 if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0) {
-                    // Fail opening codec
+                    // Failed to open codec
                     JAMI_WARN("Fail to open hardware encoder %s with %s ", avcodec_get_name(static_cast<AVCodecID>(systemCodecInfo.avcodecId)), it.getName().c_str());
                     avcodec_free_context(&encoderCtx);
                     encoderCtx = nullptr;
                     accel_ = nullptr;
                     continue;
                 } else {
-                    // Success opening codec
+                    // Succeed to open codec
                     JAMI_WARN("Using hardware encoding for %s with %s ", avcodec_get_name(static_cast<AVCodecID>(systemCodecInfo.avcodecId)), it.getName().c_str());
                     encoders_.push_back(encoderCtx);
                     break;
@@ -600,7 +600,7 @@ MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video)
 }
 
 void
-MediaEncoder::forcePresetX264(AVCodecContext* encoderCtx)
+MediaEncoder::forcePresetX264_X265(AVCodecContext* encoderCtx)
 {
     const char *speedPreset = "veryfast";
     if (av_opt_set(encoderCtx, "preset", speedPreset, AV_OPT_SEARCH_CHILDREN))
@@ -747,8 +747,12 @@ MediaEncoder::initCodec(AVMediaType mediaType, AVCodecID avcodecId, uint64_t br)
     if (avcodecId == AV_CODEC_ID_H264) {
         auto profileLevelId = libav_utils::getDictValue(options_, "parameters");
         extractProfileLevelID(profileLevelId, encoderCtx);
-        forcePresetX264(encoderCtx);
+        forcePresetX264_X265(encoderCtx);
         initH264(encoderCtx, br);
+    } else if (avcodecId == AV_CODEC_ID_HEVC) {
+        encoderCtx->profile = FF_PROFILE_HEVC_MAIN;
+        forcePresetX264_X265(encoderCtx);
+        initH265(encoderCtx, br);
     } else if (avcodecId == AV_CODEC_ID_VP8) {
         initVP8(encoderCtx, br);
     } else if (avcodecId == AV_CODEC_ID_MPEG4) {
@@ -774,6 +778,8 @@ MediaEncoder::setBitrate(uint64_t br)
     // Change parameters on the fly
     if(codecId == AV_CODEC_ID_H264)
         initH264(encoderCtx, br);
+    if(codecId == AV_CODEC_ID_H265)
+        initH265(encoderCtx, br);
     else if(codecId == AV_CODEC_ID_H263P)
         initH263(encoderCtx, br);
     else if(codecId == AV_CODEC_ID_MPEG4)
@@ -815,6 +821,36 @@ MediaEncoder::initH264(AVCodecContext* encoderCtx, uint64_t br)
     }
 }
 
+void
+MediaEncoder::initH265(AVCodecContext* encoderCtx, uint64_t br)
+{
+    // If auto quality disabled use CRF mode
+    if(not auto_quality) {
+        uint64_t maxBitrate = 1000 * br;
+        // H265 use 50% less bitrate compared to H264 (half bitrate is equivalent to a change 6 for CRF)
+        // https://slhck.info/video/2017/02/24/crf-guide.html
+        uint8_t crf = (uint8_t) std::round(LOGREG_PARAM_A + log(pow(maxBitrate, LOGREG_PARAM_B)) - 6);     // CRF = A + B*ln(maxBitrate)
+        uint64_t bufSize = maxBitrate * 2;
+
+        av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "b", maxBitrate, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "maxrate", maxBitrate, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "minrate", -1, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "bufsize", bufSize, AV_OPT_SEARCH_CHILDREN);
+        JAMI_DBG("H265 encoder setup: crf=%u, maxrate=%lu, bufsize=%lu", crf, maxBitrate, bufSize);
+    }
+    else {
+        av_opt_set_int(encoderCtx, "b", br * 1000, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "maxrate", br * 1000, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "minrate", br * 1000, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "bufsize", br * 500, AV_OPT_SEARCH_CHILDREN);
+        av_opt_set_int(encoderCtx, "crf", -1, AV_OPT_SEARCH_CHILDREN);
+
+        JAMI_DBG("H265 encoder setup cbr: bitrate=%lu kbit/s", br);
+    }
+
+}
+
 void
 MediaEncoder::initVP8(AVCodecContext* encoderCtx, uint64_t br)
 {
@@ -943,4 +979,67 @@ MediaEncoder::readConfig(AVCodecContext* encoderCtx)
     }
 }
 
+std::string
+MediaEncoder::testH265Accel()
+{
+#ifdef RING_ACCEL
+    if (jami::Manager::instance().videoPreferences.getEncodingAccelerated()) {
+        // Get compatible list of Hardware API
+        auto APIs = video::HardwareAccel::getCompatibleAccel(AV_CODEC_ID_H265,
+                1280, 720, CODEC_ENCODER);
+
+        std::unique_ptr<video::HardwareAccel> accel;
+
+        if (APIs.size() > 0) {
+            for (const auto& it : APIs) {
+                accel = std::make_unique<video::HardwareAccel>(it);    // save accel
+                // Init codec need accel to init encoderCtx accelerated
+                auto outputCodec = avcodec_find_encoder_by_name(accel->getCodecName().c_str());
+
+                AVCodecContext* encoderCtx = avcodec_alloc_context3(outputCodec);
+                encoderCtx->thread_count = std::min(std::thread::hardware_concurrency(), 16u);
+                encoderCtx->width = 1280;
+                encoderCtx->height = 720;
+                AVRational framerate;
+                framerate.num = 30;
+                framerate.den = 1;
+                encoderCtx->time_base = av_inv_q(framerate);
+                encoderCtx->pix_fmt = accel->getFormat();
+                encoderCtx->profile = FF_PROFILE_HEVC_MAIN;
+                encoderCtx->opaque = accel.get();
+
+                auto br = SystemCodecInfo::DEFAULT_VIDEO_BITRATE;
+                av_opt_set_int(encoderCtx, "b", br * 1000, AV_OPT_SEARCH_CHILDREN);
+                av_opt_set_int(encoderCtx, "maxrate", br * 1000, AV_OPT_SEARCH_CHILDREN);
+                av_opt_set_int(encoderCtx, "minrate", br * 1000, AV_OPT_SEARCH_CHILDREN);
+                av_opt_set_int(encoderCtx, "bufsize", br * 500, AV_OPT_SEARCH_CHILDREN);
+                av_opt_set_int(encoderCtx, "crf", -1, AV_OPT_SEARCH_CHILDREN);
+
+                auto ret = accel->initAPI(false, nullptr);
+                if (ret < 0) {
+                    accel = nullptr;
+                    encoderCtx = nullptr;
+                    continue;
+                }
+                accel->setDetails(encoderCtx);
+                if (avcodec_open2(encoderCtx, outputCodec, nullptr) < 0) {
+                    // Failed to open codec
+                    avcodec_free_context(&encoderCtx);
+                    encoderCtx = nullptr;
+                    accel = nullptr;
+                    continue;
+                } else {
+                    // Succeed to open codec
+                    avcodec_free_context(&encoderCtx);
+                    encoderCtx = nullptr;
+                    accel = nullptr;
+                    return it.getName();
+                }
+            }
+        }
+    }
+#endif
+    return "";
+}
+
 } // namespace jami
diff --git a/src/media/media_encoder.h b/src/media/media_encoder.h
index b223afea036452edf60645af665cd9c5ca780c1c..b578a8a6735150d628c315b201db7442f3d6e3ff 100644
--- a/src/media/media_encoder.h
+++ b/src/media/media_encoder.h
@@ -106,13 +106,15 @@ public:
     void enableAccel(bool enableAccel);
 #endif
 
+    static std::string testH265Accel();
+
     unsigned getStreamCount() const;
     MediaStream getStream(const std::string& name, int streamIdx = -1) const;
 
 private:
     NON_COPYABLE(MediaEncoder);
     AVCodecContext* prepareEncoderContext(AVCodec* outputCodec, bool is_video);
-    void forcePresetX264(AVCodecContext* encoderCtx);
+    void forcePresetX264_X265(AVCodecContext* encoderCtx);
     void extractProfileLevelID(const std::string &parameters, AVCodecContext *ctx);
     int initStream(const std::string& codecName, AVBufferRef* framesCtx);
     int initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* framesCtx);
@@ -136,6 +138,7 @@ private:
     bool linkableHW_ {false};
 
     void initH264(AVCodecContext* encoderCtx, uint64_t br);
+    void initH265(AVCodecContext* encoderCtx, uint64_t br);
     void initVP8(AVCodecContext* encoderCtx, uint64_t br);
     void initMPEG4(AVCodecContext* encoderCtx, uint64_t br);
     void initH263(AVCodecContext* encoderCtx, uint64_t br);
diff --git a/src/media/system_codec_container.cpp b/src/media/system_codec_container.cpp
index 07417b4577f4c81a6a6083df3cef1684fb792b8f..3689c5944b96e46cbb59bc2e76b3b48f91760e3a 100644
--- a/src/media/system_codec_container.cpp
+++ b/src/media/system_codec_container.cpp
@@ -24,6 +24,7 @@
 #endif
 #include "logger.h"
 #include "system_codec_container.h"
+#include "media_encoder.h"
 
 #include <sstream>
 
@@ -51,6 +52,8 @@ SystemCodecContainer::initCodecConfig()
 #ifdef ENABLE_VIDEO
     auto minH264 = SystemCodecInfo::DEFAULT_H264_MIN_QUALITY;
     auto maxH264 = SystemCodecInfo::DEFAULT_H264_MAX_QUALITY;
+    auto minH265 = SystemCodecInfo::DEFAULT_H264_MIN_QUALITY;
+    auto maxH265 = SystemCodecInfo::DEFAULT_H264_MAX_QUALITY;
     auto minVP8 = SystemCodecInfo::DEFAULT_VP8_MIN_QUALITY;
     auto maxVP8 = SystemCodecInfo::DEFAULT_VP8_MAX_QUALITY;
     auto defaultBitrate = SystemCodecInfo::DEFAULT_VIDEO_BITRATE;
@@ -58,12 +61,20 @@ SystemCodecContainer::initCodecConfig()
     availableCodecList_ = {
 #ifdef ENABLE_VIDEO
         /* Define supported video codec*/
+        std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_HEVC,
+                                               "H265", "",
+                                               CODEC_ENCODER_DECODER,
+                                               defaultBitrate,
+                                               minH265,
+                                               maxH265),
+
         std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_H264,
                                                "H264", "libx264",
                                                CODEC_ENCODER_DECODER,
                                                defaultBitrate,
                                                minH264,
                                                maxH264),
+
         std::make_shared<SystemVideoCodecInfo>(AV_CODEC_ID_VP8,
                                                "VP8", "libvpx",
                                                CODEC_ENCODER_DECODER,
@@ -120,10 +131,24 @@ SystemCodecContainer::initCodecConfig()
                                                CODEC_ENCODER_DECODER,
                                                64, 8000, 1, 0),
     };
-
+    setActiveH265();
     checkInstalledCodecs();
 }
 
+bool
+SystemCodecContainer::setActiveH265()
+{
+    auto apiName = MediaEncoder::testH265Accel();
+    if (apiName != "") {
+        JAMI_WARN("Found a usable accelerated H265/HEVC codec: %s, enabling.", apiName.c_str());
+        return true;
+    } else {
+        JAMI_ERR("Can't find a usable accelerated H265/HEVC codec, disabling.");
+        removeCodecByName("H265");
+    }
+    return false;
+}
+
 void
 SystemCodecContainer::checkInstalledCodecs()
 {
@@ -214,5 +239,15 @@ SystemCodecContainer::searchCodecByPayload(unsigned payload, MediaType mediaType
     }
     return {};
 }
+void
+SystemCodecContainer::removeCodecByName(const std::string& name, MediaType mediaType)
+{
+    for (auto codecIt = availableCodecList_.begin(); codecIt != availableCodecList_.end(); ++codecIt) {
+        if ((*codecIt)->name == name && (*codecIt)->mediaType & mediaType) {
+            availableCodecList_.erase(codecIt);
+            break;
+        }
+    }
+}
 
 } // namespace jami
diff --git a/src/media/system_codec_container.h b/src/media/system_codec_container.h
index a11ad1fdf209ddca14ed630e772602b246df7caf..2090bd297401a38af4ef99df59f35d64cd1a0c18 100644
--- a/src/media/system_codec_container.h
+++ b/src/media/system_codec_container.h
@@ -55,11 +55,14 @@ class SystemCodecContainer
         std::shared_ptr<SystemCodecInfo>
         searchCodecByPayload(unsigned payload, MediaType type = MEDIA_ALL);
 
+        void removeCodecByName(const std::string& name, MediaType type = MEDIA_ALL);
+
+        void initCodecConfig();
     private:
         /* available audio & video codec  */
         std::vector<std::shared_ptr<SystemCodecInfo>> availableCodecList_;
 
-        void initCodecConfig();
+        bool setActiveH265();
         void checkInstalledCodecs();
 };
 
diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp
index 906d52b6355b0565c933e9f9f90eac3e0c9c8361..f4224a94d239d68e7cbcd7deb3abd8415a5cfd3c 100644
--- a/src/media/video/accel.cpp
+++ b/src/media/video/accel.cpp
@@ -41,7 +41,7 @@ static const std::list<HardwareAPI> apiListDec = {
 
 static const std::list<HardwareAPI> apiListEnc = {
     { "nvenc", AV_HWDEVICE_TYPE_CUDA, AV_PIX_FMT_CUDA, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265 }, { "0", "1", "2" } },
-    { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 }, { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
+    { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 }, { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
     { "videotoolbox", AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264 }, { } },
     { "qsv", AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 }, { } },
 };