diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn index 11dafc1c21..abb514e18b 100644 --- a/webrtc/modules/video_coding/BUILD.gn +++ b/webrtc/modules/video_coding/BUILD.gn @@ -234,6 +234,7 @@ rtc_static_library("webrtc_vp8") { "../../api/video_codecs:video_codecs_api", "../../common_video", "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_numerics", "../../rtc_base:sequenced_task_checker", "../../system_wrappers", ] diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc index dd9da0f4a6..3f9f879bef 100644 --- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc +++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc @@ -29,6 +29,7 @@ #include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h" #include "webrtc/modules/video_coding/include/video_codec_interface.h" #include "webrtc/rtc_base/checks.h" +#include "webrtc/rtc_base/numerics/exp_filter.h" #include "webrtc/rtc_base/random.h" #include "webrtc/rtc_base/timeutils.h" #include "webrtc/rtc_base/trace_event.h" @@ -122,6 +123,28 @@ bool GetGfBoostPercentageFromFieldTrialGroup(int* boost_percentage) { return true; } + +void GetPostProcParamsFromFieldTrialGroup( + VP8DecoderImpl::DeblockParams* deblock_params) { + std::string group = + webrtc::field_trial::FindFullName(kVp8PostProcArmFieldTrial); + if (group.empty()) + return; + + VP8DecoderImpl::DeblockParams params; + if (sscanf(group.c_str(), "Enabled-%d,%d,%d", ¶ms.max_level, + ¶ms.min_qp, ¶ms.degrade_qp) != 3) + return; + + if (params.max_level < 0 || params.max_level > 16) + return; + + if (params.min_qp < 0 || params.degrade_qp <= params.min_qp) + return; + + *deblock_params = params; +} + } // namespace VP8Encoder* VP8Encoder::Create() { @@ -925,9 +948,33 @@ int VP8EncoderImpl::RegisterEncodeCompleteCallback( return WEBRTC_VIDEO_CODEC_OK; } +class VP8DecoderImpl::QpSmoother { + public: + QpSmoother() : last_sample_ms_(rtc::TimeMillis()), smoother_(kAlpha) {} + + int GetAvg() const { + float value = smoother_.filtered(); + return (value == rtc::ExpFilter::kValueUndefined) ? 0 + : static_cast(value); + } + + void Add(float sample) { + int64_t now_ms = rtc::TimeMillis(); + smoother_.Apply(static_cast(now_ms - last_sample_ms_), sample); + last_sample_ms_ = now_ms; + } + + void Reset() { smoother_.Reset(kAlpha); } + + private: + const float kAlpha = 0.95f; + int64_t last_sample_ms_; + rtc::ExpFilter smoother_; +}; + VP8DecoderImpl::VP8DecoderImpl() - : use_postproc_arm_(webrtc::field_trial::FindFullName( - kVp8PostProcArmFieldTrial) == "Enabled"), + : use_postproc_arm_( + webrtc::field_trial::IsEnabled(kVp8PostProcArmFieldTrial)), buffer_pool_(false, 300 /* max_number_of_buffers*/), decode_complete_callback_(NULL), inited_(false), @@ -935,7 +982,11 @@ VP8DecoderImpl::VP8DecoderImpl() propagation_cnt_(-1), last_frame_width_(0), last_frame_height_(0), - key_frame_required_(true) {} + key_frame_required_(true), + qp_smoother_(use_postproc_arm_ ? new QpSmoother() : nullptr) { + if (use_postproc_arm_) + GetPostProcParamsFromFieldTrialGroup(&deblock_); +} VP8DecoderImpl::~VP8DecoderImpl() { inited_ = true; // in order to do the actual release @@ -999,12 +1050,23 @@ int VP8DecoderImpl::Decode(const EncodedImage& input_image, if (use_postproc_arm_) { vp8_postproc_cfg_t ppcfg; ppcfg.post_proc_flag = VP8_MFQE; - // For low resolutions, use stronger deblocking filter and enable the - // deblock and demacroblocker. + // For low resolutions, use stronger deblocking filter. int last_width_x_height = last_frame_width_ * last_frame_height_; if (last_width_x_height > 0 && last_width_x_height <= 320 * 240) { - ppcfg.deblocking_level = 6; // Only affects VP8_DEMACROBLOCK. - ppcfg.post_proc_flag |= VP8_DEBLOCK | VP8_DEMACROBLOCK; + // Enable the deblock and demacroblocker based on qp thresholds. + RTC_DCHECK(qp_smoother_); + int qp = qp_smoother_->GetAvg(); + if (qp > deblock_.min_qp) { + int level = deblock_.max_level; + if (qp < deblock_.degrade_qp) { + // Use lower level. + level = deblock_.max_level * (qp - deblock_.min_qp) / + (deblock_.degrade_qp - deblock_.min_qp); + } + // Deblocking level only affects VP8_DEMACROBLOCK. + ppcfg.deblocking_level = std::max(level, 1); + ppcfg.post_proc_flag |= VP8_DEBLOCK | VP8_DEMACROBLOCK; + } } vpx_codec_control(decoder_, VP8_SET_POSTPROC, &ppcfg); } @@ -1104,6 +1166,13 @@ int VP8DecoderImpl::ReturnFrame(const vpx_image_t* img, // Decoder OK and NULL image => No show frame return WEBRTC_VIDEO_CODEC_NO_OUTPUT; } + if (qp_smoother_) { + if (last_frame_width_ != static_cast(img->d_w) || + last_frame_height_ != static_cast(img->d_h)) { + qp_smoother_->Reset(); + } + qp_smoother_->Add(qp); + } last_frame_width_ = img->d_w; last_frame_height_ = img->d_h; // Allocate memory for decoded image. diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h index 376dde1280..7b5e000d23 100644 --- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h +++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.h @@ -135,7 +135,14 @@ class VP8DecoderImpl : public VP8Decoder { const char* ImplementationName() const override; + struct DeblockParams { + int max_level = 6; // Deblocking strength: [0, 16]. + int degrade_qp = 1; // If QP value is below, start lowering |max_level|. + int min_qp = 0; // If QP value is below, turn off deblocking. + }; + private: + class QpSmoother; int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp, int64_t ntp_time_ms, @@ -151,6 +158,8 @@ class VP8DecoderImpl : public VP8Decoder { int last_frame_width_; int last_frame_height_; bool key_frame_required_; + DeblockParams deblock_; + const std::unique_ptr qp_smoother_; }; } // namespace webrtc