libvpx_vp8_decoder.cc (11719B)
1 /* 2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/video_coding/codecs/vp8/libvpx_vp8_decoder.h" 12 13 #include <algorithm> 14 #include <cstdint> 15 #include <cstdio> 16 #include <cstring> 17 #include <memory> 18 #include <optional> 19 #include <string> 20 21 #include "api/environment/environment.h" 22 #include "api/field_trials_view.h" 23 #include "api/scoped_refptr.h" 24 #include "api/units/timestamp.h" 25 #include "api/video/color_space.h" 26 #include "api/video/encoded_image.h" 27 #include "api/video/i420_buffer.h" 28 #include "api/video/video_frame.h" 29 #include "api/video/video_frame_buffer.h" 30 #include "api/video/video_frame_type.h" 31 #include "api/video_codecs/video_decoder.h" 32 #include "modules/video_coding/codecs/vp8/include/vp8.h" 33 #include "modules/video_coding/include/video_error_codes.h" 34 #include "rtc_base/checks.h" 35 #include "rtc_base/numerics/exp_filter.h" 36 #include "system_wrappers/include/metrics.h" 37 #include "third_party/libvpx/source/libvpx/vpx/vp8.h" 38 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" 39 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" 40 #include "third_party/libvpx/source/libvpx/vpx/vpx_image.h" 41 #include "third_party/libyuv/include/libyuv/convert.h" 42 43 namespace webrtc { 44 namespace { 45 // vpx_decoder.h documentation indicates decode deadline is time in us, with 46 // "Set to zero for unlimited.", but actual implementation requires this to be 47 // a mode with 0 meaning allow delay and 1 not allowing it. 48 constexpr long kDecodeDeadlineRealtime = 1; // NOLINT 49 50 const char kVp8PostProcArmFieldTrial[] = "WebRTC-VP8-Postproc-Config-Arm"; 51 const char kVp8PostProcFieldTrial[] = "WebRTC-VP8-Postproc-Config"; 52 53 #if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || \ 54 defined(WEBRTC_ANDROID) || defined(WEBRTC_ARCH_MIPS) 55 constexpr bool kIsArm = true; 56 #else 57 constexpr bool kIsArm = false; 58 #endif 59 60 std::optional<LibvpxVp8Decoder::DeblockParams> DefaultDeblockParams() { 61 return LibvpxVp8Decoder::DeblockParams(/*max_level=*/8, 62 /*degrade_qp=*/60, 63 /*min_qp=*/30); 64 } 65 66 std::optional<LibvpxVp8Decoder::DeblockParams> 67 GetPostProcParamsFromFieldTrialGroup(const FieldTrialsView& field_trials) { 68 std::string group = field_trials.Lookup(kIsArm ? kVp8PostProcArmFieldTrial 69 : kVp8PostProcFieldTrial); 70 if (group.empty()) { 71 return DefaultDeblockParams(); 72 } 73 74 LibvpxVp8Decoder::DeblockParams params; 75 if (sscanf(group.c_str(), "Enabled-%d,%d,%d", ¶ms.max_level, 76 ¶ms.min_qp, ¶ms.degrade_qp) != 3) { 77 return DefaultDeblockParams(); 78 } 79 80 if (params.max_level < 0 || params.max_level > 16) { 81 return DefaultDeblockParams(); 82 } 83 84 if (params.min_qp < 0 || params.degrade_qp <= params.min_qp) { 85 return DefaultDeblockParams(); 86 } 87 88 return params; 89 } 90 91 } // namespace 92 93 std::unique_ptr<VideoDecoder> CreateVp8Decoder(const Environment& env) { 94 return std::make_unique<LibvpxVp8Decoder>(env); 95 } 96 97 class LibvpxVp8Decoder::QpSmoother { 98 public: 99 explicit QpSmoother(const Environment& env) 100 : env_(env), 101 last_sample_(env_.clock().CurrentTime()), 102 smoother_(kAlpha) {} 103 104 int GetAvg() const { 105 float value = smoother_.filtered(); 106 return (value == ExpFilter::kValueUndefined) ? 0 : static_cast<int>(value); 107 } 108 109 void Add(float sample) { 110 Timestamp now = env_.clock().CurrentTime(); 111 smoother_.Apply((now - last_sample_).ms<float>(), sample); 112 last_sample_ = now; 113 } 114 115 void Reset() { smoother_.Reset(kAlpha); } 116 117 private: 118 static constexpr float kAlpha = 0.95f; 119 120 const Environment env_; 121 Timestamp last_sample_; 122 ExpFilter smoother_; 123 }; 124 125 LibvpxVp8Decoder::LibvpxVp8Decoder(const Environment& env) 126 : use_postproc_( 127 kIsArm ? env.field_trials().IsEnabled(kVp8PostProcArmFieldTrial) 128 : true), 129 buffer_pool_(false, 300 /* max_number_of_buffers*/), 130 decode_complete_callback_(nullptr), 131 inited_(false), 132 decoder_(nullptr), 133 last_frame_width_(0), 134 last_frame_height_(0), 135 key_frame_required_(true), 136 deblock_params_(use_postproc_ ? GetPostProcParamsFromFieldTrialGroup( 137 env.field_trials()) 138 : std::nullopt), 139 qp_smoother_(use_postproc_ ? std::make_unique<QpSmoother>(env) 140 : nullptr) {} 141 142 LibvpxVp8Decoder::~LibvpxVp8Decoder() { 143 inited_ = true; // in order to do the actual release 144 Release(); 145 } 146 147 bool LibvpxVp8Decoder::Configure(const Settings& settings) { 148 if (Release() < 0) { 149 return false; 150 } 151 if (decoder_ == nullptr) { 152 decoder_ = new vpx_codec_ctx_t; 153 memset(decoder_, 0, sizeof(*decoder_)); 154 } 155 vpx_codec_dec_cfg_t cfg; 156 // Setting number of threads to a constant value (1) 157 cfg.threads = 1; 158 cfg.h = cfg.w = 0; // set after decode 159 160 vpx_codec_flags_t flags = use_postproc_ ? VPX_CODEC_USE_POSTPROC : 0; 161 162 if (vpx_codec_dec_init(decoder_, vpx_codec_vp8_dx(), &cfg, flags)) { 163 delete decoder_; 164 decoder_ = nullptr; 165 return false; 166 } 167 168 inited_ = true; 169 170 // Always start with a complete key frame. 171 key_frame_required_ = true; 172 if (std::optional<int> buffer_pool_size = settings.buffer_pool_size()) { 173 if (!buffer_pool_.Resize(*buffer_pool_size)) { 174 return false; 175 } 176 } 177 return true; 178 } 179 180 int LibvpxVp8Decoder::Decode(const EncodedImage& input_image, 181 int64_t render_time_ms) { 182 return Decode(input_image, /*missing_frames=*/false, render_time_ms); 183 } 184 185 int LibvpxVp8Decoder::Decode(const EncodedImage& input_image, 186 bool /*missing_frames*/, 187 int64_t /*render_time_ms*/) { 188 if (!inited_) { 189 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 190 } 191 if (decode_complete_callback_ == nullptr) { 192 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; 193 } 194 if (input_image.data() == nullptr && input_image.size() > 0) { 195 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; 196 } 197 198 // Post process configurations. 199 if (use_postproc_) { 200 vp8_postproc_cfg_t ppcfg; 201 // MFQE enabled to reduce key frame popping. 202 ppcfg.post_proc_flag = VP8_MFQE; 203 204 if (kIsArm) { 205 RTC_DCHECK(deblock_params_.has_value()); 206 } 207 if (deblock_params_.has_value()) { 208 // For low resolutions, use stronger deblocking filter. 209 int last_width_x_height = last_frame_width_ * last_frame_height_; 210 if (last_width_x_height > 0 && last_width_x_height <= 320 * 240) { 211 // Enable the deblock and demacroblocker based on qp thresholds. 212 RTC_DCHECK(qp_smoother_); 213 int qp = qp_smoother_->GetAvg(); 214 if (qp > deblock_params_->min_qp) { 215 int level = deblock_params_->max_level; 216 if (qp < deblock_params_->degrade_qp) { 217 // Use lower level. 218 level = deblock_params_->max_level * 219 (qp - deblock_params_->min_qp) / 220 (deblock_params_->degrade_qp - deblock_params_->min_qp); 221 } 222 // Deblocking level only affects VP8_DEMACROBLOCK. 223 ppcfg.deblocking_level = std::max(level, 1); 224 ppcfg.post_proc_flag |= VP8_DEBLOCK | VP8_DEMACROBLOCK; 225 } 226 } 227 } else { 228 // Non-arm with no explicit deblock params set. 229 ppcfg.post_proc_flag |= VP8_DEBLOCK; 230 // For VGA resolutions and lower, enable the demacroblocker postproc. 231 if (last_frame_width_ * last_frame_height_ <= 640 * 360) { 232 ppcfg.post_proc_flag |= VP8_DEMACROBLOCK; 233 } 234 // Strength of deblocking filter. Valid range:[0,16] 235 ppcfg.deblocking_level = 3; 236 } 237 238 vpx_codec_control(decoder_, VP8_SET_POSTPROC, &ppcfg); 239 } 240 241 // Always start with a complete key frame. 242 if (key_frame_required_) { 243 if (input_image._frameType != VideoFrameType::kVideoFrameKey) 244 return WEBRTC_VIDEO_CODEC_ERROR; 245 key_frame_required_ = false; 246 } 247 248 const uint8_t* buffer = input_image.data(); 249 if (input_image.size() == 0) { 250 buffer = nullptr; // Triggers full frame concealment. 251 } 252 if (vpx_codec_decode(decoder_, buffer, input_image.size(), nullptr, 253 kDecodeDeadlineRealtime)) { 254 return WEBRTC_VIDEO_CODEC_ERROR; 255 } 256 257 vpx_codec_iter_t iter = nullptr; 258 vpx_image_t* img = vpx_codec_get_frame(decoder_, &iter); 259 int qp; 260 vpx_codec_err_t vpx_ret = 261 vpx_codec_control(decoder_, VPXD_GET_LAST_QUANTIZER, &qp); 262 RTC_DCHECK_EQ(vpx_ret, VPX_CODEC_OK); 263 int ret = ReturnFrame(img, input_image.RtpTimestamp(), qp, 264 input_image.ColorSpace()); 265 if (ret != 0) { 266 return ret; 267 } 268 return WEBRTC_VIDEO_CODEC_OK; 269 } 270 271 int LibvpxVp8Decoder::ReturnFrame(const vpx_image_t* img, 272 uint32_t timestamp, 273 int qp, 274 const ColorSpace* explicit_color_space) { 275 if (img == nullptr) { 276 // Decoder OK and NULL image => No show frame 277 return WEBRTC_VIDEO_CODEC_NO_OUTPUT; 278 } 279 if (qp_smoother_) { 280 if (last_frame_width_ != static_cast<int>(img->d_w) || 281 last_frame_height_ != static_cast<int>(img->d_h)) { 282 qp_smoother_->Reset(); 283 } 284 qp_smoother_->Add(qp); 285 } 286 last_frame_width_ = img->d_w; 287 last_frame_height_ = img->d_h; 288 // Allocate memory for decoded image. 289 scoped_refptr<VideoFrameBuffer> buffer; 290 291 scoped_refptr<I420Buffer> i420_buffer = 292 buffer_pool_.CreateI420Buffer(img->d_w, img->d_h); 293 buffer = i420_buffer; 294 if (i420_buffer) { 295 libyuv::I420Copy(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y], 296 img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U], 297 img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V], 298 i420_buffer->MutableDataY(), i420_buffer->StrideY(), 299 i420_buffer->MutableDataU(), i420_buffer->StrideU(), 300 i420_buffer->MutableDataV(), i420_buffer->StrideV(), 301 img->d_w, img->d_h); 302 } 303 304 if (!buffer) { 305 // Pool has too many pending frames. 306 RTC_HISTOGRAM_BOOLEAN("WebRTC.Video.LibvpxVp8Decoder.TooManyPendingFrames", 307 1); 308 return WEBRTC_VIDEO_CODEC_NO_OUTPUT; 309 } 310 311 VideoFrame decoded_image = VideoFrame::Builder() 312 .set_video_frame_buffer(buffer) 313 .set_rtp_timestamp(timestamp) 314 .set_color_space(explicit_color_space) 315 .build(); 316 decode_complete_callback_->Decoded(decoded_image, std::nullopt, qp); 317 318 return WEBRTC_VIDEO_CODEC_OK; 319 } 320 321 int LibvpxVp8Decoder::RegisterDecodeCompleteCallback( 322 DecodedImageCallback* callback) { 323 decode_complete_callback_ = callback; 324 return WEBRTC_VIDEO_CODEC_OK; 325 } 326 327 int LibvpxVp8Decoder::Release() { 328 int ret_val = WEBRTC_VIDEO_CODEC_OK; 329 330 if (decoder_ != nullptr) { 331 if (inited_) { 332 if (vpx_codec_destroy(decoder_)) { 333 ret_val = WEBRTC_VIDEO_CODEC_MEMORY; 334 } 335 } 336 delete decoder_; 337 decoder_ = nullptr; 338 } 339 buffer_pool_.Release(); 340 inited_ = false; 341 return ret_val; 342 } 343 344 VideoDecoder::DecoderInfo LibvpxVp8Decoder::GetDecoderInfo() const { 345 DecoderInfo info; 346 info.implementation_name = "libvpx"; 347 info.is_hardware_accelerated = false; 348 return info; 349 } 350 351 const char* LibvpxVp8Decoder::ImplementationName() const { 352 return "libvpx"; 353 } 354 } // namespace webrtc