blob: 76f43e988c0543f0ca11e54bd272a697370a3a36 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.h"
#include <algorithm>
#include <numeric>
#include <va/va.h>
#include "base/bits.h"
#include "base/memory/ref_counted_memory.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "media/gpu/macros.h"
#include "media/gpu/vaapi/vaapi_common.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "media/gpu/vaapi/vp9_rate_control.h"
#include "media/gpu/vaapi/vp9_svc_layers.h"
#include "third_party/libvpx/source/libvpx/vp9/ratectrl_rtc.h"
namespace media {
namespace {
// Keyframe period.
constexpr size_t kKFPeriod = 3000;
// Quantization parameter. They are vp9 ac/dc indices and their ranges are
// 0-255. Based on WebRTC's defaults.
constexpr uint8_t kMinQP = 4;
constexpr uint8_t kMaxQP = 112;
// The upper limitation of the quantization parameter for the software rate
// controller. This is larger than |kMaxQP| because a driver might ignore the
// specified maximum quantization parameter when the driver determines the
// value, but it doesn't ignore the quantization parameter by the software rate
// controller.
constexpr uint8_t kMaxQPForSoftwareRateCtrl = 224;
// 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
// encoder support.
constexpr uint8_t kDefaultQP = 24;
// filter level may affect on quality at lower bitrates; for now,
// 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.
uint8_t QindexToQuantizer(uint8_t q_index) {
constexpr uint8_t 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;
}
// TODO(crbug.com/752720): remove this in favor of std::gcd if c++17 is enabled
// to use.
int GCD(int a, int b) {
return a == 0 ? b : GCD(b % a, a);
}
// 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);
}
VideoBitrateAllocation GetDefaultVideoBitrateAllocation(
const VideoEncodeAccelerator::Config& config) {
VideoBitrateAllocation bitrate_allocation;
if (!config.HasTemporalLayer() && !config.HasSpatialLayer()) {
bitrate_allocation.SetBitrate(0, 0, config.bitrate.target());
return bitrate_allocation;
}
DCHECK_LE(config.spatial_layers.size(), VP9SVCLayers::kMaxSpatialLayers);
for (size_t sid = 0; sid < config.spatial_layers.size(); ++sid) {
const auto& spatial_layer = config.spatial_layers[sid];
const size_t num_temporal_layers = spatial_layer.num_of_temporal_layers;
DCHECK_LE(num_temporal_layers, VP9SVCLayers::kMaxSupportedTemporalLayers);
// The same bitrate factors as the software encoder.
// https://source.chromium.org/chromium/chromium/src/+/main:media/video/vpx_video_encoder.cc;l=131;drc=d383d0b3e4f76789a6de2a221c61d3531f4c59da
constexpr double kTemporalLayersBitrateScaleFactors
[][VP9SVCLayers::kMaxSupportedTemporalLayers] = {
{1.00, 0.00, 0.00}, // For one temporal layer.
{0.60, 0.40, 0.00}, // For two temporal layers.
{0.50, 0.20, 0.30}, // For three temporal layers.
};
const uint32_t bitrate_bps = spatial_layer.bitrate_bps;
for (size_t tid = 0; tid < num_temporal_layers; ++tid) {
const double factor =
kTemporalLayersBitrateScaleFactors[num_temporal_layers - 1][tid];
bitrate_allocation.SetBitrate(
sid, tid, base::checked_cast<int>(bitrate_bps * factor));
}
}
return bitrate_allocation;
}
libvpx::VP9RateControlRtcConfig CreateRateControlConfig(
const VP9VaapiVideoEncoderDelegate::EncodeParams& encode_params,
const VideoBitrateAllocation& bitrate_allocation,
const size_t num_temporal_layers,
const std::vector<gfx::Size>& spatial_layer_resolutions) {
DCHECK(!spatial_layer_resolutions.empty());
const gfx::Size& encode_size = spatial_layer_resolutions.back();
const size_t num_spatial_layers = spatial_layer_resolutions.size();
libvpx::VP9RateControlRtcConfig rc_cfg{};
rc_cfg.rc_mode = VPX_CBR;
rc_cfg.width = encode_size.width();
rc_cfg.height = encode_size.height();
rc_cfg.max_quantizer = QindexToQuantizer(encode_params.max_qp);
rc_cfg.min_quantizer = QindexToQuantizer(encode_params.min_qp);
// libvpx::VP9RateControlRtcConfig is kbps.
rc_cfg.target_bandwidth = encode_params.bitrate_allocation.GetSumBps() / 1000;
// 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;
// Fill spatial/temporal layers variables.
rc_cfg.ss_number_layers = num_spatial_layers;
rc_cfg.ts_number_layers = num_temporal_layers;
for (size_t sid = 0; sid < num_spatial_layers; ++sid) {
int gcd =
GCD(encode_size.height(), spatial_layer_resolutions[sid].height());
rc_cfg.scaling_factor_num[sid] =
spatial_layer_resolutions[sid].height() / gcd;
rc_cfg.scaling_factor_den[sid] = encode_size.height() / gcd;
int bitrate_sum = 0;
for (size_t tid = 0; tid < num_temporal_layers; ++tid) {
size_t idx = sid * num_temporal_layers + tid;
rc_cfg.max_quantizers[idx] = rc_cfg.max_quantizer;
rc_cfg.min_quantizers[idx] = rc_cfg.min_quantizer;
bitrate_sum += bitrate_allocation.GetBitrateBps(sid, tid);
rc_cfg.layer_target_bitrate[idx] = bitrate_sum / 1000;
rc_cfg.ts_rate_decimator[tid] = 1u << (num_temporal_layers - tid - 1);
}
}
return rc_cfg;
}
static scoped_refptr<base::RefCountedBytes> MakeRefCountedBytes(void* ptr,
size_t size) {
return base::MakeRefCounted<base::RefCountedBytes>(
reinterpret_cast<uint8_t*>(ptr), size);
}
} // namespace
VP9VaapiVideoEncoderDelegate::EncodeParams::EncodeParams()
: kf_period_frames(kKFPeriod),
framerate(0),
initial_qp(kDefaultQP),
min_qp(kMinQP),
max_qp(kMaxQP),
error_resilient_mode(false) {}
void VP9VaapiVideoEncoderDelegate::set_rate_ctrl_for_testing(
std::unique_ptr<VP9RateControl> rate_ctrl) {
rate_ctrl_ = std::move(rate_ctrl);
}
VP9VaapiVideoEncoderDelegate::VP9VaapiVideoEncoderDelegate(
scoped_refptr<VaapiWrapper> vaapi_wrapper,
base::RepeatingClosure error_cb)
: VaapiVideoEncoderDelegate(std::move(vaapi_wrapper), error_cb) {}
VP9VaapiVideoEncoderDelegate::~VP9VaapiVideoEncoderDelegate() {
// VP9VaapiVideoEncoderDelegate can be destroyed on any thread.
}
bool VP9VaapiVideoEncoderDelegate::Initialize(
const VideoEncodeAccelerator::Config& config,
const VaapiVideoEncoderDelegate::Config& ave_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (VideoCodecProfileToVideoCodec(config.output_profile) !=
VideoCodec::kVP9) {
DVLOGF(1) << "Invalid profile: " << GetProfileName(config.output_profile);
return false;
}
if (config.input_visible_size.IsEmpty()) {
DVLOGF(1) << "Input visible size could not be empty";
return false;
}
// Even though VP9VaapiVideoEncoderDelegate might support other bitrate
// control modes, only the kConstantQuantizationParameter is used.
if (ave_config.bitrate_control != VaapiVideoEncoderDelegate::BitrateControl::
kConstantQuantizationParameter) {
DVLOGF(1) << "Only CQ bitrate control is supported";
return false;
}
visible_size_ = config.input_visible_size;
coded_size_ = gfx::Size(base::bits::AlignUp(visible_size_.width(), 16),
base::bits::AlignUp(visible_size_.height(), 16));
current_params_ = EncodeParams();
reference_frames_.Clear();
frame_num_ = 0;
auto initial_bitrate_allocation = GetDefaultVideoBitrateAllocation(config);
size_t num_temporal_layers = 1;
size_t num_spatial_layers = 1;
std::vector<gfx::Size> spatial_layer_resolutions;
if (config.HasTemporalLayer() || config.HasSpatialLayer()) {
num_spatial_layers = config.spatial_layers.size();
num_temporal_layers = config.spatial_layers[0].num_of_temporal_layers;
DCHECK(num_spatial_layers != 1 || num_temporal_layers != 1);
for (size_t sid = 1; sid < num_spatial_layers; ++sid) {
if (num_temporal_layers !=
config.spatial_layers[sid].num_of_temporal_layers) {
VLOGF(1) << "The temporal layer sizes among spatial layers must be "
"identical";
return false;
}
}
if (num_spatial_layers > VP9SVCLayers::kMaxSpatialLayers ||
num_temporal_layers > VP9SVCLayers::kMaxSupportedTemporalLayers) {
VLOGF(1) << "Unsupported amount of spatial/temporal layers: "
<< ", Spatial layer number: " << num_spatial_layers
<< ", Temporal layer number: " << num_temporal_layers;
return false;
}
if (num_spatial_layers > 1 &&
config.inter_layer_pred !=
VideoEncodeAccelerator::Config::InterLayerPredMode::kOnKeyPic) {
std::string inter_layer_pred;
if (config.inter_layer_pred ==
VideoEncodeAccelerator::Config::InterLayerPredMode::kOn)
inter_layer_pred = base::StringPrintf("InterLayerPredMode::kOn");
else
inter_layer_pred = base::StringPrintf("InterLayerPredMode::kOff");
VLOGF(1) << "Support only k-SVC encoding. inter_layer_pred="
<< inter_layer_pred;
return false;
}
for (const auto& spatial_layer : config.spatial_layers) {
spatial_layer_resolutions.emplace_back(
gfx::Size(spatial_layer.width, spatial_layer.height));
}
svc_layers_ = std::make_unique<VP9SVCLayers>(config.spatial_layers);
}
current_params_.max_qp = kMaxQPForSoftwareRateCtrl;
// Store layer size for vp9 simple stream.
if (spatial_layer_resolutions.empty())
spatial_layer_resolutions.push_back(visible_size_);
// |rate_ctrl_| might be injected for tests.
if (!rate_ctrl_) {
rate_ctrl_ = VP9RateControl::Create(CreateRateControlConfig(
current_params_, initial_bitrate_allocation, num_temporal_layers,
spatial_layer_resolutions));
}
if (!rate_ctrl_)
return false;
DCHECK(!pending_update_rates_);
pending_update_rates_ =
std::make_pair(initial_bitrate_allocation,
config.initial_framerate.value_or(
VideoEncodeAccelerator::kDefaultFramerate));
return ApplyPendingUpdateRates();
}
gfx::Size VP9VaapiVideoEncoderDelegate::GetCodedSize() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!coded_size_.IsEmpty());
return coded_size_;
}
size_t VP9VaapiVideoEncoderDelegate::GetMaxNumOfRefFrames() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return kVp9NumRefFrames;
}
bool VP9VaapiVideoEncoderDelegate::PrepareEncodeJob(EncodeJob* encode_job) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (svc_layers_) {
if (svc_layers_->UpdateEncodeJob(encode_job->IsKeyframeRequested(),
current_params_.kf_period_frames)) {
encode_job->ProduceKeyframe();
}
} else {
if (encode_job->IsKeyframeRequested())
frame_num_ = 0;
if (frame_num_ == 0)
encode_job->ProduceKeyframe();
frame_num_++;
frame_num_ %= current_params_.kf_period_frames;
}
scoped_refptr<VP9Picture> picture = GetPicture(encode_job);
DCHECK(picture);
std::array<bool, kVp9NumRefsPerFrame> ref_frames_used = {false, false, false};
SetFrameHeader(encode_job->IsKeyframeRequested(), picture.get(),
&ref_frames_used);
if (!SubmitFrameParameters(encode_job, current_params_, picture,
reference_frames_, ref_frames_used)) {
LOG(ERROR) << "Failed submitting frame parameters";
return false;
}
UpdateReferenceFrames(picture);
return true;
}
BitstreamBufferMetadata VP9VaapiVideoEncoderDelegate::GetMetadata(
EncodeJob* encode_job,
size_t payload_size) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto metadata =
VaapiVideoEncoderDelegate::GetMetadata(encode_job, payload_size);
auto picture = GetPicture(encode_job);
DCHECK(picture);
metadata.vp9 = picture->metadata_for_encoding;
return metadata;
}
std::vector<gfx::Size> VP9VaapiVideoEncoderDelegate::GetSVCLayerResolutions() {
if (!ApplyPendingUpdateRates()) {
DLOG(ERROR) << __func__ << " ApplyPendingUpdateRates failed";
return {};
}
if (svc_layers_) {
return svc_layers_->active_spatial_layer_resolutions();
} else {
return {visible_size_};
}
}
void VP9VaapiVideoEncoderDelegate::BitrateControlUpdate(
uint64_t encoded_chunk_size_bytes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(rate_ctrl_);
DVLOGF(4) << "|encoded_chunk_size_bytes|=" << encoded_chunk_size_bytes;
rate_ctrl_->PostEncodeUpdate(encoded_chunk_size_bytes);
}
bool VP9VaapiVideoEncoderDelegate::ApplyPendingUpdateRates() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pending_update_rates_)
return true;
VLOGF(2) << "New bitrate: " << pending_update_rates_->first.ToString()
<< ", New framerate: " << pending_update_rates_->second;
current_params_.bitrate_allocation = pending_update_rates_->first;
current_params_.framerate = pending_update_rates_->second;
pending_update_rates_.reset();
// Update active layer status in |svc_layers_|, and key frame is produced when
// active layer changed.
if (svc_layers_) {
if (!svc_layers_->MaybeUpdateActiveLayer(
&current_params_.bitrate_allocation)) {
return false;
}
} else {
// Simple stream encoding.
if (current_params_.bitrate_allocation.GetSumBps() !=
current_params_.bitrate_allocation.GetBitrateBps(0, 0)) {
return false;
}
}
CHECK(rate_ctrl_);
const size_t num_temporal_layers =
svc_layers_ ? svc_layers_->num_temporal_layers() : 1u;
std::vector<gfx::Size> spatial_layer_resolutions = {visible_size_};
if (svc_layers_)
spatial_layer_resolutions = svc_layers_->active_spatial_layer_resolutions();
rate_ctrl_->UpdateRateControl(CreateRateControlConfig(
current_params_, current_params_.bitrate_allocation, num_temporal_layers,
spatial_layer_resolutions));
return true;
}
bool VP9VaapiVideoEncoderDelegate::UpdateRates(
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (bitrate_allocation.GetSumBps() == 0 || framerate == 0)
return false;
pending_update_rates_ = std::make_pair(bitrate_allocation, framerate);
if (current_params_.bitrate_allocation == pending_update_rates_->first &&
current_params_.framerate == pending_update_rates_->second) {
pending_update_rates_.reset();
}
return true;
}
Vp9FrameHeader VP9VaapiVideoEncoderDelegate::GetDefaultFrameHeader(
const bool keyframe) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Vp9FrameHeader hdr{};
DCHECK(!visible_size_.IsEmpty());
hdr.frame_width = visible_size_.width();
hdr.frame_height = visible_size_.height();
hdr.render_width = visible_size_.width();
hdr.render_height = visible_size_.height();
hdr.quant_params.base_q_idx = kDefaultQP;
hdr.loop_filter.level = kDefaultLfLevel;
hdr.show_frame = true;
hdr.frame_type =
keyframe ? Vp9FrameHeader::KEYFRAME : Vp9FrameHeader::INTERFRAME;
return hdr;
}
void VP9VaapiVideoEncoderDelegate::SetFrameHeader(
bool keyframe,
VP9Picture* picture,
std::array<bool, kVp9NumRefsPerFrame>* ref_frames_used) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(picture);
DCHECK(ref_frames_used);
*picture->frame_hdr = GetDefaultFrameHeader(keyframe);
if (svc_layers_) {
// Reference frame settings for k-SVC stream.
svc_layers_->FillUsedRefFramesAndMetadata(picture, ref_frames_used);
// Enable error resilient mode so that the syntax of a frame can be decoded
// independently of previous frames.
picture->frame_hdr->error_resilient_mode = true;
} else {
// Reference frame settings for simple stream.
if (keyframe) {
picture->frame_hdr->refresh_frame_flags = 0xff;
ref_frame_index_ = 0;
} else {
picture->frame_hdr->ref_frame_idx[0] = ref_frame_index_;
picture->frame_hdr->ref_frame_idx[1] =
(ref_frame_index_ - 1) & (kVp9NumRefFrames - 1);
picture->frame_hdr->ref_frame_idx[2] =
(ref_frame_index_ - 2) & (kVp9NumRefFrames - 1);
ref_frame_index_ = (ref_frame_index_ + 1) % kVp9NumRefFrames;
picture->frame_hdr->refresh_frame_flags = 1 << ref_frame_index_;
// Use last, golden, alt frames.
ref_frames_used->fill(true);
}
}
CHECK(rate_ctrl_);
libvpx::VP9FrameParamsQpRTC frame_params{};
frame_params.frame_type =
keyframe ? FRAME_TYPE::KEY_FRAME : FRAME_TYPE::INTER_FRAME;
if (picture->metadata_for_encoding) {
frame_params.temporal_layer_id =
picture->metadata_for_encoding->temporal_idx;
frame_params.spatial_layer_id = picture->metadata_for_encoding->spatial_idx;
}
rate_ctrl_->ComputeQP(frame_params);
picture->frame_hdr->quant_params.base_q_idx = rate_ctrl_->GetQP();
picture->frame_hdr->loop_filter.level = rate_ctrl_->GetLoopfilterLevel();
DVLOGF(4) << "qp="
<< static_cast<int>(picture->frame_hdr->quant_params.base_q_idx)
<< ", filter_level="
<< static_cast<int>(picture->frame_hdr->loop_filter.level)
<< ", frame_params.temporal_layer_id:"
<< frame_params.temporal_layer_id
<< ", frame_params.spatial_layer_id:"
<< frame_params.spatial_layer_id;
}
void VP9VaapiVideoEncoderDelegate::UpdateReferenceFrames(
scoped_refptr<VP9Picture> picture) {
reference_frames_.Refresh(picture);
}
void VP9VaapiVideoEncoderDelegate::NotifyEncodedChunkSize(
VABufferID buffer_id,
VASurfaceID sync_surface_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const uint64_t encoded_chunk_size =
vaapi_wrapper_->GetEncodedChunkSize(buffer_id, sync_surface_id);
if (encoded_chunk_size == 0)
error_cb_.Run();
BitrateControlUpdate(encoded_chunk_size);
}
scoped_refptr<VP9Picture> VP9VaapiVideoEncoderDelegate::GetPicture(
EncodeJob* job) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::WrapRefCounted(
reinterpret_cast<VP9Picture*>(job->picture().get()));
}
bool VP9VaapiVideoEncoderDelegate::SubmitFrameParameters(
EncodeJob* job,
const EncodeParams& encode_params,
scoped_refptr<VP9Picture> pic,
const Vp9ReferenceFrameVector& ref_frames,
const std::array<bool, kVp9NumRefsPerFrame>& ref_frames_used) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VAEncSequenceParameterBufferVP9 seq_param = {};
const auto& frame_header = pic->frame_hdr;
// TODO(crbug.com/811912): Double check whether the
// max_frame_width or max_frame_height affects any of the memory
// allocation and tighten these values based on that.
constexpr gfx::Size kMaxFrameSize(4096, 4096);
seq_param.max_frame_width = kMaxFrameSize.height();
seq_param.max_frame_height = kMaxFrameSize.width();
seq_param.bits_per_second = encode_params.bitrate_allocation.GetSumBps();
seq_param.intra_period = encode_params.kf_period_frames;
VAEncPictureParameterBufferVP9 pic_param = {};
pic_param.frame_width_src = frame_header->frame_width;
pic_param.frame_height_src = frame_header->frame_height;
pic_param.frame_width_dst = frame_header->render_width;
pic_param.frame_height_dst = frame_header->render_height;
pic_param.reconstructed_frame = pic->AsVaapiVP9Picture()->GetVASurfaceID();
DCHECK_NE(pic_param.reconstructed_frame, VA_INVALID_ID);
for (size_t i = 0; i < kVp9NumRefFrames; i++) {
auto ref_pic = ref_frames.GetFrame(i);
pic_param.reference_frames[i] =
ref_pic ? ref_pic->AsVaapiVP9Picture()->GetVASurfaceID()
: VA_INVALID_ID;
}
pic_param.coded_buf = job->coded_buffer_id();
DCHECK_NE(pic_param.coded_buf, VA_INVALID_ID);
if (frame_header->IsKeyframe()) {
pic_param.ref_flags.bits.force_kf = true;
} else {
// Non-key frame mode, the frame has at least 1 reference frames.
size_t first_used_ref_frame = 3;
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
if (ref_frames_used[i]) {
first_used_ref_frame = std::min(first_used_ref_frame, i);
pic_param.ref_flags.bits.ref_frame_ctrl_l0 |= (1 << i);
}
}
CHECK_LT(first_used_ref_frame, 3u);
pic_param.ref_flags.bits.ref_last_idx =
ref_frames_used[0] ? frame_header->ref_frame_idx[0]
: frame_header->ref_frame_idx[first_used_ref_frame];
pic_param.ref_flags.bits.ref_gf_idx =
ref_frames_used[1] ? frame_header->ref_frame_idx[1]
: frame_header->ref_frame_idx[first_used_ref_frame];
pic_param.ref_flags.bits.ref_arf_idx =
ref_frames_used[2] ? frame_header->ref_frame_idx[2]
: frame_header->ref_frame_idx[first_used_ref_frame];
}
pic_param.pic_flags.bits.frame_type = frame_header->frame_type;
pic_param.pic_flags.bits.show_frame = frame_header->show_frame;
pic_param.pic_flags.bits.error_resilient_mode =
frame_header->error_resilient_mode;
pic_param.pic_flags.bits.intra_only = frame_header->intra_only;
pic_param.pic_flags.bits.allow_high_precision_mv =
frame_header->allow_high_precision_mv;
pic_param.pic_flags.bits.mcomp_filter_type =
frame_header->interpolation_filter;
pic_param.pic_flags.bits.frame_parallel_decoding_mode =
frame_header->frame_parallel_decoding_mode;
pic_param.pic_flags.bits.reset_frame_context =
frame_header->reset_frame_context;
pic_param.pic_flags.bits.refresh_frame_context =
frame_header->refresh_frame_context;
pic_param.pic_flags.bits.frame_context_idx = frame_header->frame_context_idx;
pic_param.refresh_frame_flags = frame_header->refresh_frame_flags;
pic_param.luma_ac_qindex = frame_header->quant_params.base_q_idx;
pic_param.luma_dc_qindex_delta = frame_header->quant_params.delta_q_y_dc;
pic_param.chroma_ac_qindex_delta = frame_header->quant_params.delta_q_uv_ac;
pic_param.chroma_dc_qindex_delta = frame_header->quant_params.delta_q_uv_dc;
pic_param.filter_level = frame_header->loop_filter.level;
pic_param.log2_tile_rows = frame_header->tile_rows_log2;
pic_param.log2_tile_columns = frame_header->tile_cols_log2;
job->AddSetupCallback(
base::BindOnce(&VaapiVideoEncoderDelegate::SubmitBuffer,
base::Unretained(this), VAEncSequenceParameterBufferType,
MakeRefCountedBytes(&seq_param, sizeof(seq_param))));
job->AddSetupCallback(
base::BindOnce(&VaapiVideoEncoderDelegate::SubmitBuffer,
base::Unretained(this), VAEncPictureParameterBufferType,
MakeRefCountedBytes(&pic_param, sizeof(pic_param))));
job->AddPostExecuteCallback(
base::BindOnce(&VP9VaapiVideoEncoderDelegate::NotifyEncodedChunkSize,
base::Unretained(this), job->coded_buffer_id(),
job->input_surface()->id()));
return true;
}
} // namespace media