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 ¶meters, 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 }, { } }, };