summaryrefslogtreecommitdiff
path: root/chromium/media/gpu/vaapi/vp9_encoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/gpu/vaapi/vp9_encoder.cc')
-rw-r--r--chromium/media/gpu/vaapi/vp9_encoder.cc151
1 files changed, 138 insertions, 13 deletions
diff --git a/chromium/media/gpu/vaapi/vp9_encoder.cc b/chromium/media/gpu/vaapi/vp9_encoder.cc
index 140ac37af4c..0c125f02b36 100644
--- a/chromium/media/gpu/vaapi/vp9_encoder.cc
+++ b/chromium/media/gpu/vaapi/vp9_encoder.cc
@@ -4,8 +4,12 @@
#include "media/gpu/vaapi/vp9_encoder.h"
+#include <algorithm>
+
#include "base/bits.h"
#include "media/gpu/macros.h"
+#include "media/gpu/vaapi/vp9_rate_control.h"
+#include "third_party/libvpx/source/libvpx/vp9/ratectrl_rtc.h"
namespace media {
@@ -19,6 +23,9 @@ constexpr int kCPBWindowSizeMs = 500;
// Quantization parameter. They are vp9 ac/dc indices and their ranges are
// 0-255. Based on WebRTC's defaults.
constexpr int kMinQP = 4;
+// TODO(crbug.com/1060775): Relax this max quantization parameter upper bound
+// so that our encoder and bitrate controller can select a higher value in the
+// case a requested bitrate is small.
constexpr int kMaxQP = 112;
// This stands for 31 as a real ac value (see rfc 8.6.1 table
// ac_qlookup[3][256]). Note: This needs to be revisited once we have 10&12 bit
@@ -29,6 +36,84 @@ constexpr int kDefaultQP = 24;
// we set a constant value (== 10) which is what other VA-API
// implementations like libyami and gstreamer-vaapi are using.
constexpr uint8_t kDefaultLfLevel = 10;
+
+// Convert Qindex, whose range is 0-255, to the quantizer parameter used in
+// libvpx vp9 rate control, whose range is 0-63.
+// Cited from //third_party/libvpx/source/libvpx/vp9/encoder/vp9_quantize.cc.
+int QindexToQuantizer(int q_index) {
+ constexpr int kQuantizerToQindex[] = {
+ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48,
+ 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100,
+ 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152,
+ 156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204,
+ 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 249, 255,
+ };
+
+ for (size_t q = 0; q < base::size(kQuantizerToQindex); ++q) {
+ if (kQuantizerToQindex[q] >= q_index)
+ return q;
+ }
+ return base::size(kQuantizerToQindex) - 1;
+}
+
+// The return value is expressed as a percentage of the average. For example,
+// to allocate no more than 4.5 frames worth of bitrate to a keyframe, the
+// return value is 450.
+uint32_t MaxSizeOfKeyframeAsPercentage(uint32_t optimal_buffer_size,
+ uint32_t max_framerate) {
+ // Set max to the optimal buffer level (normalized by target BR),
+ // and scaled by a scale_par.
+ // Max target size = scale_par * optimal_buffer_size * targetBR[Kbps].
+ // This value is presented in percentage of perFrameBw:
+ // perFrameBw = targetBR[Kbps] * 1000 / framerate.
+ // The target in % is as follows:
+ const double target_size_byte_per_frame = optimal_buffer_size * 0.5;
+ const uint32_t target_size_kbyte =
+ target_size_byte_per_frame * max_framerate / 1000;
+ const uint32_t target_size_kbyte_as_percent = target_size_kbyte * 100;
+
+ // Don't go below 3 times the per frame bandwidth.
+ constexpr uint32_t kMinIntraSizePercentage = 300u;
+ return std::max(kMinIntraSizePercentage, target_size_kbyte_as_percent);
+}
+
+libvpx::VP9RateControlRtcConfig CreateRCConfig(
+ const gfx::Size& encode_size,
+ const VP9Encoder::EncodeParams& encode_params) {
+ libvpx::VP9RateControlRtcConfig rc_cfg{};
+ rc_cfg.width = encode_size.width();
+ rc_cfg.height = encode_size.height();
+ rc_cfg.max_quantizer =
+ QindexToQuantizer(encode_params.scaling_settings.max_qp);
+ rc_cfg.min_quantizer =
+ QindexToQuantizer(encode_params.scaling_settings.min_qp);
+ // libvpx::VP9RateControlRtcConfig is kbps.
+ rc_cfg.target_bandwidth =
+ encode_params.bitrate_allocation.GetSumBps() / 1000.0;
+ // These default values come from
+ // //third_party/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc.
+ rc_cfg.buf_initial_sz = 500;
+ rc_cfg.buf_optimal_sz = 600;
+ rc_cfg.buf_sz = 1000;
+ rc_cfg.undershoot_pct = 50;
+ rc_cfg.overshoot_pct = 50;
+ rc_cfg.max_intra_bitrate_pct = MaxSizeOfKeyframeAsPercentage(
+ rc_cfg.buf_optimal_sz, encode_params.framerate);
+ rc_cfg.framerate = encode_params.framerate;
+
+ // Spatial layer variables.
+ rc_cfg.ss_number_layers = 1;
+ rc_cfg.max_quantizers[0] = rc_cfg.max_quantizer;
+ rc_cfg.min_quantizers[0] = rc_cfg.min_quantizer;
+ // TODO(crbug.com/1030199): Fill multiple temporal layers variables.
+ // Temporal layer variables.
+ rc_cfg.ts_number_layers = 1;
+ rc_cfg.scaling_factor_num[0] = 1;
+ rc_cfg.scaling_factor_den[0] = 1;
+ rc_cfg.layer_target_bitrate[0] = rc_cfg.target_bandwidth;
+ rc_cfg.ts_rate_decimator[0] = 1;
+ return rc_cfg;
+}
} // namespace
VP9Encoder::EncodeParams::EncodeParams()
@@ -40,6 +125,11 @@ VP9Encoder::EncodeParams::EncodeParams()
scaling_settings(kMinQP, kMaxQP),
error_resilient_mode(false) {}
+void VP9Encoder::set_rate_ctrl_for_testing(
+ std::unique_ptr<VP9RateControl> rate_ctrl) {
+ rate_ctrl_ = std::move(rate_ctrl);
+}
+
void VP9Encoder::Reset() {
current_params_ = EncodeParams();
reference_frames_.Clear();
@@ -66,20 +156,27 @@ bool VP9Encoder::Initialize(const VideoEncodeAccelerator::Config& config,
DVLOGF(1) << "Input visible size could not be empty";
return false;
}
- // 4:2:0 format has to be 2-aligned.
- if ((config.input_visible_size.width() % 2 != 0) ||
- (config.input_visible_size.height() % 2 != 0)) {
- DVLOGF(1) << "The pixel sizes are not even: "
- << config.input_visible_size.ToString();
- return false;
- }
+ accelerator_->set_bitrate_control(ave_config.bitrate_control);
visible_size_ = config.input_visible_size;
coded_size_ = gfx::Size(base::bits::Align(visible_size_.width(), 16),
base::bits::Align(visible_size_.height(), 16));
-
Reset();
+ if (ave_config.bitrate_control ==
+ BitrateControl::kConstantQuantizationParameter) {
+ // |rate_ctrl_| might be injected for tests.
+ if (!rate_ctrl_) {
+ rate_ctrl_ = VP9RateControl::Create(
+ CreateRCConfig(visible_size_, current_params_));
+ }
+ if (!rate_ctrl_)
+ return false;
+ } else {
+ DCHECK(!rate_ctrl_) << "|rate_ctrl_| should only be configured when in "
+ "kConstantQuantizationParameter";
+ }
+
VideoBitrateAllocation initial_bitrate_allocation;
initial_bitrate_allocation.SetBitrate(0, 0, config.initial_bitrate);
return UpdateRates(initial_bitrate_allocation,
@@ -121,13 +218,14 @@ bool VP9Encoder::PrepareEncodeJob(EncodeJob* encode_job) {
scoped_refptr<VP9Picture> picture = accelerator_->GetPicture(encode_job);
DCHECK(picture);
- UpdateFrameHeader(encode_job->IsKeyframeRequested());
+ const bool keyframe = encode_job->IsKeyframeRequested();
+ UpdateFrameHeader(keyframe);
*picture->frame_hdr = current_frame_hdr_;
// Use last, golden and altref for references.
- constexpr std::array<bool, kVp9NumRefsPerFrame> ref_frames_used = {true, true,
- true};
+ const std::array<bool, kVp9NumRefsPerFrame> ref_frames_used = {
+ !keyframe, !keyframe, !keyframe};
if (!accelerator_->SubmitFrameParameters(encode_job, current_params_, picture,
reference_frames_,
ref_frames_used)) {
@@ -139,6 +237,18 @@ bool VP9Encoder::PrepareEncodeJob(EncodeJob* encode_job) {
return true;
}
+void VP9Encoder::BitrateControlUpdate(uint64_t encoded_chunk_size_bytes) {
+ if (accelerator_->bitrate_control() !=
+ BitrateControl::kConstantQuantizationParameter ||
+ !rate_ctrl_) {
+ DLOG(ERROR) << __func__ << "() is called when no bitrate controller exists";
+ return;
+ }
+
+ DVLOGF(4) << "|encoded_chunk_size_bytes|=" << encoded_chunk_size_bytes;
+ rate_ctrl_->PostEncodeUpdate(encoded_chunk_size_bytes);
+}
+
bool VP9Encoder::UpdateRates(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -160,6 +270,10 @@ bool VP9Encoder::UpdateRates(const VideoBitrateAllocation& bitrate_allocation,
current_params_.bitrate_allocation.GetSumBps() *
current_params_.cpb_window_size_ms / 1000;
+ if (!rate_ctrl_)
+ return true;
+
+ rate_ctrl_->UpdateRateControl(CreateRCConfig(visible_size_, current_params_));
return true;
}
@@ -181,8 +295,6 @@ void VP9Encoder::UpdateFrameHeader(bool keyframe) {
current_frame_hdr_.refresh_frame_flags = 0xff;
ref_frame_index_ = 0;
} else {
- // TODO(crbug.com/811912): Add temporal layer support when there is a driver
- // support. Use the last three frames for reference.
current_frame_hdr_.frame_type = Vp9FrameHeader::INTERFRAME;
current_frame_hdr_.ref_frame_idx[0] = ref_frame_index_;
current_frame_hdr_.ref_frame_idx[1] =
@@ -192,6 +304,19 @@ void VP9Encoder::UpdateFrameHeader(bool keyframe) {
ref_frame_index_ = (ref_frame_index_ + 1) % kVp9NumRefFrames;
current_frame_hdr_.refresh_frame_flags = 1 << ref_frame_index_;
}
+
+ if (!rate_ctrl_)
+ return;
+
+ libvpx::VP9FrameParamsQpRTC frame_params{};
+ frame_params.frame_type =
+ keyframe ? FRAME_TYPE::KEY_FRAME : FRAME_TYPE::INTER_FRAME;
+ rate_ctrl_->ComputeQP(frame_params);
+ // TODO(crbug.com/1030199): Fill temporal layer id.
+ current_frame_hdr_.quant_params.base_q_idx = rate_ctrl_->GetQP();
+ current_frame_hdr_.loop_filter.level = rate_ctrl_->GetLoopfilterLevel();
+ DVLOGF(4) << "|qp|=" << rate_ctrl_->GetQP()
+ << ", |filter_level|=" << rate_ctrl_->GetLoopfilterLevel();
}
void VP9Encoder::UpdateReferenceFrames(scoped_refptr<VP9Picture> picture) {