blob: aa5e9bde2ec18470cb5ab9299490f61d024e30c5 [file] [log] [blame]
// Copyright 2018 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/h264_vaapi_video_decoder_delegate.h"
#include <va/va.h>
#include "base/cxx17_backports.h"
#include "base/memory/aligned_memory.h"
#include "base/trace_event/trace_event.h"
#include "media/base/cdm_context.h"
#include "media/gpu/decode_surface_handler.h"
#include "media/gpu/h264_dpb.h"
#include "media/gpu/macros.h"
#include "media/gpu/vaapi/vaapi_common.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
namespace media {
using DecodeStatus = H264Decoder::H264Accelerator::Status;
namespace {
// from ITU-T REC H.264 spec
// section 8.5.6
// "Inverse scanning process for 4x4 transform coefficients and scaling lists"
static constexpr int kZigzagScan4x4[16] = {0, 1, 4, 8, 5, 2, 3, 6,
9, 12, 13, 10, 7, 11, 14, 15};
// section 8.5.7
// "Inverse scanning process for 8x8 transform coefficients and scaling lists"
static constexpr uint8_t kZigzagScan8x8[64] = {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
int GetSliceHeaderCounter() {
// Needs to be static in case there are multiple active at once, in which case
// they all need unique values.
static base::AtomicSequenceNumber parsed_slice_hdr_counter;
return parsed_slice_hdr_counter.GetNext();
}
} // namespace
H264VaapiVideoDecoderDelegate::H264VaapiVideoDecoderDelegate(
DecodeSurfaceHandler<VASurface>* const vaapi_dec,
scoped_refptr<VaapiWrapper> vaapi_wrapper,
ProtectedSessionUpdateCB on_protected_session_update_cb,
CdmContext* cdm_context,
EncryptionScheme encryption_scheme)
: VaapiVideoDecoderDelegate(vaapi_dec,
std::move(vaapi_wrapper),
std::move(on_protected_session_update_cb),
cdm_context,
encryption_scheme) {}
H264VaapiVideoDecoderDelegate::~H264VaapiVideoDecoderDelegate() = default;
scoped_refptr<H264Picture> H264VaapiVideoDecoderDelegate::CreateH264Picture() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto va_surface = vaapi_dec_->CreateSurface();
if (!va_surface)
return nullptr;
return new VaapiH264Picture(std::move(va_surface));
}
// Fill |va_pic| with default/neutral values.
static void InitVAPicture(VAPictureH264* va_pic) {
memset(va_pic, 0, sizeof(*va_pic));
va_pic->picture_id = VA_INVALID_ID;
va_pic->flags = VA_PICTURE_H264_INVALID;
}
DecodeStatus H264VaapiVideoDecoderDelegate::SubmitFrameMetadata(
const H264SPS* sps,
const H264PPS* pps,
const H264DPB& dpb,
const H264Picture::Vector& ref_pic_listp0,
const H264Picture::Vector& ref_pic_listb0,
const H264Picture::Vector& ref_pic_listb1,
scoped_refptr<H264Picture> pic) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT0("media,gpu",
"H264VaapiVideoDecoderDelegate::SubmitFrameMetadata");
VAPictureParameterBufferH264 pic_param;
memset(&pic_param, 0, sizeof(pic_param));
#if BUILDFLAG(IS_CHROMEOS_ASH)
memset(&crypto_params_, 0, sizeof(crypto_params_));
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
full_sample_ = false;
#define FROM_SPS_TO_PP(a) pic_param.a = sps->a
#define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a
FROM_SPS_TO_PP2(pic_width_in_mbs_minus1, picture_width_in_mbs_minus1);
// This assumes non-interlaced video
FROM_SPS_TO_PP2(pic_height_in_map_units_minus1, picture_height_in_mbs_minus1);
FROM_SPS_TO_PP(bit_depth_luma_minus8);
FROM_SPS_TO_PP(bit_depth_chroma_minus8);
#undef FROM_SPS_TO_PP
#undef FROM_SPS_TO_PP2
#define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a
#define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a
FROM_SPS_TO_PP_SF(chroma_format_idc);
FROM_SPS_TO_PP_SF2(separate_colour_plane_flag,
residual_colour_transform_flag);
FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag);
FROM_SPS_TO_PP_SF(frame_mbs_only_flag);
FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag);
FROM_SPS_TO_PP_SF(direct_8x8_inference_flag);
pic_param.seq_fields.bits.MinLumaBiPredSize8x8 = (sps->level_idc >= 31);
FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4);
FROM_SPS_TO_PP_SF(pic_order_cnt_type);
FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4);
FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag);
#undef FROM_SPS_TO_PP_SF
#undef FROM_SPS_TO_PP_SF2
#define FROM_PPS_TO_PP(a) pic_param.a = pps->a
FROM_PPS_TO_PP(pic_init_qp_minus26);
FROM_PPS_TO_PP(pic_init_qs_minus26);
FROM_PPS_TO_PP(chroma_qp_index_offset);
FROM_PPS_TO_PP(second_chroma_qp_index_offset);
#undef FROM_PPS_TO_PP
#define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a
#define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a
FROM_PPS_TO_PP_PF(entropy_coding_mode_flag);
FROM_PPS_TO_PP_PF(weighted_pred_flag);
FROM_PPS_TO_PP_PF(weighted_bipred_idc);
FROM_PPS_TO_PP_PF(transform_8x8_mode_flag);
pic_param.pic_fields.bits.field_pic_flag = 0;
FROM_PPS_TO_PP_PF(constrained_intra_pred_flag);
FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag,
pic_order_present_flag);
FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag);
FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag);
pic_param.pic_fields.bits.reference_pic_flag = pic->ref;
#undef FROM_PPS_TO_PP_PF
#undef FROM_PPS_TO_PP_PF2
pic_param.frame_num = pic->frame_num;
InitVAPicture(&pic_param.CurrPic);
FillVAPicture(&pic_param.CurrPic, std::move(pic));
// Init reference pictures' array.
for (int i = 0; i < 16; ++i)
InitVAPicture(&pic_param.ReferenceFrames[i]);
// And fill it with picture info from DPB.
FillVARefFramesFromDPB(dpb, pic_param.ReferenceFrames,
base::size(pic_param.ReferenceFrames));
pic_param.num_ref_frames = sps->max_num_ref_frames;
VAIQMatrixBufferH264 iq_matrix_buf;
memset(&iq_matrix_buf, 0, sizeof(iq_matrix_buf));
if (pps->pic_scaling_matrix_present_flag) {
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 16; ++j)
iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] =
pps->scaling_list4x4[i][j];
}
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 64; ++j)
iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] =
pps->scaling_list8x8[i][j];
}
} else {
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 16; ++j)
iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] =
sps->scaling_list4x4[i][j];
}
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 64; ++j)
iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] =
sps->scaling_list8x8[i][j];
}
}
const bool success = vaapi_wrapper_->SubmitBuffers(
{{VAPictureParameterBufferType, sizeof(pic_param), &pic_param},
{VAIQMatrixBufferType, sizeof(iq_matrix_buf), &iq_matrix_buf}});
return success ? DecodeStatus::kOk : DecodeStatus::kFail;
}
DecodeStatus H264VaapiVideoDecoderDelegate::ParseEncryptedSliceHeader(
const std::vector<base::span<const uint8_t>>& data,
const std::vector<SubsampleEntry>& subsamples,
const std::vector<uint8_t>& sps_nalu_data,
const std::vector<uint8_t>& pps_nalu_data,
H264SliceHeader* slice_header_out) {
DCHECK(slice_header_out);
DCHECK(!subsamples.empty());
DCHECK(!data.empty());
// This is done by sending in the encryption parameters and the encrypted
// slice header. Then the vaEndPicture call is blocking while it decrypts and
// parses the header parameters. We use VACencStatusBuf which allows us to
// extract the slice header parameters of interest and return them to the
// caller.
#if BUILDFLAG(IS_CHROMEOS_ASH)
VAEncryptionParameters crypto_params = {};
// Don't use the VAEncryptionSegmentInfo vector in the class since we do not
// need to hold this data across calls.
std::vector<VAEncryptionSegmentInfo> segment_info;
ProtectedSessionState state =
SetupDecryptDecode(true /* full sample */, data[0].size(), &crypto_params,
&segment_info, subsamples);
if (state == ProtectedSessionState::kFailed) {
LOG(ERROR) << "ParseEncryptedSliceHeader fails because we couldn't setup "
"the protected session";
return DecodeStatus::kFail;
} else if (state != ProtectedSessionState::kCreated) {
return DecodeStatus::kTryAgain;
}
// For encrypted header parsing, we need to also send the SPS and PPS. Both of
// those and the slice NALU need to be prefixed with the 0x000001 start code.
constexpr size_t kStartCodeSize = 3;
constexpr size_t kExtraDataBytes = 3 * kStartCodeSize;
// Adjust the first segment length and init length to compensate for inserting
// the SPS, PPS and 3 start codes.
size_t size_adjustment =
sps_nalu_data.size() + pps_nalu_data.size() + kExtraDataBytes;
size_t total_size = 0;
size_t offset_adjustment = 0;
for (auto& segment : segment_info) {
segment.segment_length += size_adjustment;
segment.init_byte_length += size_adjustment;
segment.segment_start_offset += offset_adjustment;
offset_adjustment += size_adjustment;
// Any additional segments are only adjusted by the start code size;
size_adjustment = kStartCodeSize;
total_size += segment.segment_length;
}
crypto_params.status_report_index = GetSliceHeaderCounter();
// This is based on a sample from Intel for how to use this API.
constexpr size_t kDecryptQuerySizeAndAlignment = 4096;
std::unique_ptr<void, base::AlignedFreeDeleter> surface_memory(
base::AlignedAlloc(kDecryptQuerySizeAndAlignment,
kDecryptQuerySizeAndAlignment));
constexpr size_t kVaQueryCencBufferSize = 2048;
auto back_buffer_mem = std::make_unique<uint8_t[]>(kVaQueryCencBufferSize);
VACencStatusBuf* status_buf =
reinterpret_cast<VACencStatusBuf*>(surface_memory.get());
status_buf->status = VA_ENCRYPTION_STATUS_INCOMPLETE;
status_buf->buf = back_buffer_mem.get();
status_buf->buf_size = kVaQueryCencBufferSize;
auto slice_param_buf = std::make_unique<VACencSliceParameterBufferH264>();
status_buf->slice_buf_type = VaCencSliceBufParamter;
status_buf->slice_buf_size = sizeof(VACencSliceParameterBufferH264);
status_buf->slice_buf = slice_param_buf.get();
constexpr int kCencStatusSurfaceDimension = 64;
auto buffer_ptr_alloc = std::make_unique<uintptr_t>();
uintptr_t* buffer_ptr = reinterpret_cast<uintptr_t*>(buffer_ptr_alloc.get());
buffer_ptr[0] = reinterpret_cast<uintptr_t>(surface_memory.get());
auto surface = vaapi_wrapper_->CreateVASurfaceForUserPtr(
gfx::Size(kCencStatusSurfaceDimension, kCencStatusSurfaceDimension),
buffer_ptr,
3 * kCencStatusSurfaceDimension * kCencStatusSurfaceDimension);
if (!surface) {
DVLOG(1) << "Failed allocating surface for decrypt status";
return DecodeStatus::kFail;
}
// Assembles the 'slice data' which is the SPS, PPS, encrypted SEIS and
// encrypted slice data, each of which is also prefixed by the 0x000001 start
// code.
std::vector<uint8_t> full_data;
const std::vector<uint8_t> start_code = {0u, 0u, 1u};
full_data.reserve(total_size);
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), sps_nalu_data.begin(), sps_nalu_data.end());
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), pps_nalu_data.begin(), pps_nalu_data.end());
for (auto& nalu : data) {
full_data.insert(full_data.end(), start_code.begin(), start_code.end());
full_data.insert(full_data.end(), nalu.begin(), nalu.end());
}
if (!vaapi_wrapper_->SubmitBuffers({{VAEncryptionParameterBufferType,
sizeof(crypto_params), &crypto_params},
{VAProtectedSliceDataBufferType,
full_data.size(), full_data.data()}})) {
DVLOG(1) << "Failure submitting encrypted slice header buffers";
return DecodeStatus::kFail;
}
if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(surface->id())) {
LOG(ERROR) << "Failed executing for slice header decrypt";
return DecodeStatus::kFail;
}
if (status_buf->status != VA_ENCRYPTION_STATUS_SUCCESSFUL) {
LOG(ERROR) << "Failure status in encrypted header parsing: "
<< static_cast<int>(status_buf->status);
return DecodeStatus::kFail;
}
// Read the parsed slice header data back and populate the structure with it.
slice_header_out->idr_pic_flag = !!slice_param_buf->idr_pic_flag;
slice_header_out->nal_ref_idc = slice_param_buf->nal_ref_idc;
// The last span in |data| will be the slice header NALU.
slice_header_out->nalu_data = data.back().data();
slice_header_out->nalu_size = data.back().size();
slice_header_out->slice_type = slice_param_buf->slice_type;
slice_header_out->frame_num = slice_param_buf->frame_number;
slice_header_out->idr_pic_id = slice_param_buf->idr_pic_id;
slice_header_out->pic_order_cnt_lsb = slice_param_buf->pic_order_cnt_lsb;
slice_header_out->delta_pic_order_cnt_bottom =
slice_param_buf->delta_pic_order_cnt_bottom;
slice_header_out->delta_pic_order_cnt0 =
slice_param_buf->delta_pic_order_cnt[0];
slice_header_out->delta_pic_order_cnt1 =
slice_param_buf->delta_pic_order_cnt[1];
slice_header_out->no_output_of_prior_pics_flag =
slice_param_buf->ref_pic_fields.bits.no_output_of_prior_pics_flag;
slice_header_out->long_term_reference_flag =
slice_param_buf->ref_pic_fields.bits.long_term_reference_flag;
slice_header_out->adaptive_ref_pic_marking_mode_flag =
slice_param_buf->ref_pic_fields.bits.adaptive_ref_pic_marking_mode_flag;
const size_t num_dec_ref_pics =
slice_param_buf->ref_pic_fields.bits.dec_ref_pic_marking_count;
if (num_dec_ref_pics > H264SliceHeader::kRefListSize) {
DVLOG(1) << "Invalid number of dec_ref_pics: " << num_dec_ref_pics;
return DecodeStatus::kFail;
}
for (size_t i = 0; i < num_dec_ref_pics; ++i) {
slice_header_out->ref_pic_marking[i].memory_mgmnt_control_operation =
slice_param_buf->memory_management_control_operation[i];
slice_header_out->ref_pic_marking[i].difference_of_pic_nums_minus1 =
slice_param_buf->difference_of_pic_nums_minus1[i];
slice_header_out->ref_pic_marking[i].long_term_pic_num =
slice_param_buf->long_term_pic_num[i];
slice_header_out->ref_pic_marking[i].long_term_frame_idx =
slice_param_buf->long_term_frame_idx[i];
slice_header_out->ref_pic_marking[i].max_long_term_frame_idx_plus1 =
slice_param_buf->max_long_term_frame_idx_plus1[i];
}
slice_header_out->full_sample_encryption = true;
slice_header_out->full_sample_index =
status_buf->status_report_index_feedback;
return DecodeStatus::kOk;
#else // BUILDFLAG(IS_CHROMEOS_ASH)
return DecodeStatus::kFail;
#endif
}
DecodeStatus H264VaapiVideoDecoderDelegate::SubmitSlice(
const H264PPS* pps,
const H264SliceHeader* slice_hdr,
const H264Picture::Vector& ref_pic_list0,
const H264Picture::Vector& ref_pic_list1,
scoped_refptr<H264Picture> pic,
const uint8_t* data,
size_t size,
const std::vector<SubsampleEntry>& subsamples) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT0("media,gpu", "H264VaapiVideoDecoderDelegate::SubmitSlice");
if (slice_hdr->full_sample_encryption) {
// We do not need to submit all the slice data, instead we just submit the
// index for what was already sent for parsing. The HW decoder already has
// the full slice data from when we decrypted the header.
full_sample_ = true;
VACencStatusParameters cenc_status = {};
cenc_status.status_report_index_feedback = slice_hdr->full_sample_index;
return vaapi_wrapper_->SubmitBuffer(VACencStatusParameterBufferType,
sizeof(VACencStatusParameters),
&cenc_status)
? DecodeStatus::kOk
: DecodeStatus::kFail;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (IsEncryptedSession()) {
const ProtectedSessionState state = SetupDecryptDecode(
/*full_sample=*/false, size, &crypto_params_, &encryption_segment_info_,
subsamples);
if (state == ProtectedSessionState::kFailed) {
LOG(ERROR) << "SubmitSlice fails because we couldn't setup the protected "
"session";
return DecodeStatus::kFail;
} else if (state != ProtectedSessionState::kCreated) {
return DecodeStatus::kTryAgain;
}
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
VASliceParameterBufferH264 slice_param;
memset(&slice_param, 0, sizeof(slice_param));
slice_param.slice_data_size = slice_hdr->nalu_size;
slice_param.slice_data_offset = 0;
slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL;
slice_param.slice_data_bit_offset = slice_hdr->header_bit_size;
#define SHDRToSP(a) slice_param.a = slice_hdr->a
SHDRToSP(first_mb_in_slice);
slice_param.slice_type = slice_hdr->slice_type % 5;
SHDRToSP(direct_spatial_mv_pred_flag);
// TODO posciak: make sure parser sets those even when override flags
// in slice header is off.
SHDRToSP(num_ref_idx_l0_active_minus1);
SHDRToSP(num_ref_idx_l1_active_minus1);
SHDRToSP(cabac_init_idc);
SHDRToSP(slice_qp_delta);
SHDRToSP(disable_deblocking_filter_idc);
SHDRToSP(slice_alpha_c0_offset_div2);
SHDRToSP(slice_beta_offset_div2);
if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) &&
pps->weighted_pred_flag) ||
(slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) {
SHDRToSP(luma_log2_weight_denom);
SHDRToSP(chroma_log2_weight_denom);
SHDRToSP(luma_weight_l0_flag);
SHDRToSP(luma_weight_l1_flag);
SHDRToSP(chroma_weight_l0_flag);
SHDRToSP(chroma_weight_l1_flag);
for (int i = 0; i <= slice_param.num_ref_idx_l0_active_minus1; ++i) {
slice_param.luma_weight_l0[i] =
slice_hdr->pred_weight_table_l0.luma_weight[i];
slice_param.luma_offset_l0[i] =
slice_hdr->pred_weight_table_l0.luma_offset[i];
for (int j = 0; j < 2; ++j) {
slice_param.chroma_weight_l0[i][j] =
slice_hdr->pred_weight_table_l0.chroma_weight[i][j];
slice_param.chroma_offset_l0[i][j] =
slice_hdr->pred_weight_table_l0.chroma_offset[i][j];
}
}
if (slice_hdr->IsBSlice()) {
for (int i = 0; i <= slice_param.num_ref_idx_l1_active_minus1; ++i) {
slice_param.luma_weight_l1[i] =
slice_hdr->pred_weight_table_l1.luma_weight[i];
slice_param.luma_offset_l1[i] =
slice_hdr->pred_weight_table_l1.luma_offset[i];
for (int j = 0; j < 2; ++j) {
slice_param.chroma_weight_l1[i][j] =
slice_hdr->pred_weight_table_l1.chroma_weight[i][j];
slice_param.chroma_offset_l1[i][j] =
slice_hdr->pred_weight_table_l1.chroma_offset[i][j];
}
}
}
}
static_assert(base::size(slice_param.RefPicList0) ==
base::size(slice_param.RefPicList1),
"Invalid RefPicList sizes");
for (size_t i = 0; i < base::size(slice_param.RefPicList0); ++i) {
InitVAPicture(&slice_param.RefPicList0[i]);
InitVAPicture(&slice_param.RefPicList1[i]);
}
for (size_t i = 0;
i < ref_pic_list0.size() && i < base::size(slice_param.RefPicList0);
++i) {
if (ref_pic_list0[i])
FillVAPicture(&slice_param.RefPicList0[i], ref_pic_list0[i]);
}
for (size_t i = 0;
i < ref_pic_list1.size() && i < base::size(slice_param.RefPicList1);
++i) {
if (ref_pic_list1[i])
FillVAPicture(&slice_param.RefPicList1[i], ref_pic_list1[i]);
}
if (IsTranscrypted()) {
CHECK_EQ(subsamples.size(), 1u);
return vaapi_wrapper_->SubmitBuffers(
{{VAProtectedSliceDataBufferType, GetDecryptKeyId().length(),
GetDecryptKeyId().data()},
{VASliceParameterBufferType, sizeof(slice_param), &slice_param},
{VASliceDataBufferType, subsamples[0].cypher_bytes,
data + subsamples[0].clear_bytes}})
? DecodeStatus::kOk
: DecodeStatus::kFail;
}
return vaapi_wrapper_->SubmitBuffers(
{{VASliceParameterBufferType, sizeof(slice_param), &slice_param},
{VASliceDataBufferType, size, data}})
? DecodeStatus::kOk
: DecodeStatus::kFail;
}
DecodeStatus H264VaapiVideoDecoderDelegate::SubmitDecode(
scoped_refptr<H264Picture> pic) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT0("media,gpu", "H264VaapiVideoDecoderDelegate::SubmitDecode");
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (IsEncryptedSession() && !full_sample_ &&
!vaapi_wrapper_->SubmitBuffer(VAEncryptionParameterBufferType,
sizeof(crypto_params_), &crypto_params_)) {
return DecodeStatus::kFail;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
const VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture();
CHECK(
gfx::Rect(vaapi_pic->va_surface()->size()).Contains(pic->visible_rect()));
const bool success = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
vaapi_pic->GetVASurfaceID());
#if BUILDFLAG(IS_CHROMEOS_ASH)
encryption_segment_info_.clear();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
if (!success && NeedsProtectedSessionRecovery())
return DecodeStatus::kTryAgain;
if (success && IsEncryptedSession())
ProtectedDecodedSucceeded();
return success ? DecodeStatus::kOk : DecodeStatus::kFail;
}
bool H264VaapiVideoDecoderDelegate::OutputPicture(
scoped_refptr<H264Picture> pic) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture();
vaapi_dec_->SurfaceReady(vaapi_pic->va_surface(), vaapi_pic->bitstream_id(),
vaapi_pic->visible_rect(),
vaapi_pic->get_colorspace());
return true;
}
void H264VaapiVideoDecoderDelegate::Reset() {
DETACH_FROM_SEQUENCE(sequence_checker_);
#if BUILDFLAG(IS_CHROMEOS_ASH)
encryption_segment_info_.clear();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
vaapi_wrapper_->DestroyPendingBuffers();
}
DecodeStatus H264VaapiVideoDecoderDelegate::SetStream(
base::span<const uint8_t> /*stream*/,
const DecryptConfig* decrypt_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!decrypt_config)
return Status::kOk;
return SetDecryptConfig(decrypt_config->Clone()) ? Status::kOk
: Status::kFail;
}
void H264VaapiVideoDecoderDelegate::FillVAPicture(
VAPictureH264* va_pic,
scoped_refptr<H264Picture> pic) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VASurfaceID va_surface_id = VA_INVALID_SURFACE;
if (!pic->nonexisting)
va_surface_id = pic->AsVaapiH264Picture()->GetVASurfaceID();
va_pic->picture_id = va_surface_id;
va_pic->frame_idx = pic->frame_num;
va_pic->flags = 0;
switch (pic->field) {
case H264Picture::FIELD_NONE:
break;
case H264Picture::FIELD_TOP:
va_pic->flags |= VA_PICTURE_H264_TOP_FIELD;
break;
case H264Picture::FIELD_BOTTOM:
va_pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD;
break;
}
if (pic->ref) {
va_pic->flags |= pic->long_term ? VA_PICTURE_H264_LONG_TERM_REFERENCE
: VA_PICTURE_H264_SHORT_TERM_REFERENCE;
}
va_pic->TopFieldOrderCnt = pic->top_field_order_cnt;
va_pic->BottomFieldOrderCnt = pic->bottom_field_order_cnt;
}
int H264VaapiVideoDecoderDelegate::FillVARefFramesFromDPB(
const H264DPB& dpb,
VAPictureH264* va_pics,
int num_pics) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
H264Picture::Vector::const_reverse_iterator rit;
int i;
// Return reference frames in reverse order of insertion.
// Libva does not document this, but other implementations (e.g. mplayer)
// do it this way as well.
for (rit = dpb.rbegin(), i = 0; rit != dpb.rend() && i < num_pics; ++rit) {
if ((*rit)->ref)
FillVAPicture(&va_pics[i++], *rit);
}
return i;
}
} // namespace media