tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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", &params.max_level,
     76             &params.min_qp, &params.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