// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/v4l2/v4l2_video_decoder_delegate_h265.h"
#include <linux/v4l2-controls.h>
#include <linux/videodev2.h>
#include <algorithm>
#include <type_traits>
#include "base/logging.h"
#include "build/build_config.h"
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/v4l2_decode_surface.h"
#include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
#include "media/gpu/v4l2/v4l2_device.h"
namespace media {
class V4L2H265Picture : public H265Picture {
public:
explicit V4L2H265Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
: dec_surface_(std::move(dec_surface)) {}
V4L2H265Picture(const V4L2H265Picture&) = delete;
V4L2H265Picture& operator=(const V4L2H265Picture&) = delete;
V4L2H265Picture* AsV4L2H265Picture() override { return this; }
scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
private:
~V4L2H265Picture() override {}
scoped_refptr<V4L2DecodeSurface> dec_surface_;
};
V4L2VideoDecoderDelegateH265::V4L2VideoDecoderDelegateH265(
V4L2DecodeSurfaceHandler* surface_handler,
V4L2Device* device)
: surface_handler_(surface_handler), device_(device) {
DCHECK(surface_handler_);
}
V4L2VideoDecoderDelegateH265::~V4L2VideoDecoderDelegateH265() {}
scoped_refptr<H265Picture> V4L2VideoDecoderDelegateH265::CreateH265Picture() {
scoped_refptr<V4L2DecodeSurface> dec_surface =
surface_handler_->CreateSurface();
if (!dec_surface) {
return nullptr;
}
return new V4L2H265Picture(dec_surface);
}
scoped_refptr<H265Picture>
V4L2VideoDecoderDelegateH265::CreateH265PictureSecure(uint64_t secure_handle) {
scoped_refptr<V4L2DecodeSurface> dec_surface =
surface_handler_->CreateSecureSurface(secure_handle);
if (!dec_surface) {
return nullptr;
}
return new V4L2H265Picture(dec_surface);
}
std::vector<scoped_refptr<V4L2DecodeSurface>>
V4L2VideoDecoderDelegateH265::FillInV4L2DPB(
struct v4l2_ctrl_hevc_decode_params* v4l2_decode_param,
const H265Picture::Vector& ref_pic_list,
const H265Picture::Vector& ref_pic_set_lt_curr,
const H265Picture::Vector& ref_pic_set_st_curr_after,
const H265Picture::Vector& ref_pic_set_st_curr_before) {
std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
memset(v4l2_decode_param->dpb, 0, sizeof(v4l2_decode_param->dpb));
unsigned int i = 0;
for (const auto& pic : ref_pic_list) {
if (i >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX) {
VLOGF(1) << "Invalid DPB size";
break;
}
if (!pic) {
continue;
}
unsigned int index = 0xff;
if (!pic->IsUnused()) {
scoped_refptr<V4L2DecodeSurface> dec_surface =
H265PictureToV4L2DecodeSurface(pic.get());
index = dec_surface->GetReferenceID();
ref_surfaces.push_back(dec_surface);
}
v4l2_decode_param->dpb[i++] = {
.timestamp = index,
.flags = static_cast<__u8>(
pic->IsLongTermRef() ? V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE : 0),
.field_pic = V4L2_HEVC_SEI_PIC_STRUCT_FRAME, // No interlaced support
.pic_order_cnt_val = pic->pic_order_cnt_val_,
};
}
v4l2_decode_param->num_active_dpb_entries = i;
// Set defaults
std::fill_n(v4l2_decode_param->poc_st_curr_before,
std::size(v4l2_decode_param->poc_st_curr_before), 0xff);
std::fill_n(v4l2_decode_param->poc_st_curr_after,
std::size(v4l2_decode_param->poc_st_curr_after), 0xff);
std::fill_n(v4l2_decode_param->poc_lt_curr,
std::size(v4l2_decode_param->poc_lt_curr), 0xff);
i = 0;
for (const auto& pic : ref_pic_set_st_curr_before) {
if (i >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX) {
VLOGF(1) << "Invalid DPB size";
break;
}
if (!pic) {
continue;
}
for (unsigned int j = 0; j < v4l2_decode_param->num_active_dpb_entries;
j++) {
if (pic->pic_order_cnt_val_ ==
v4l2_decode_param->dpb[j].pic_order_cnt_val) {
v4l2_decode_param->poc_st_curr_before[i++] = j;
break;
}
}
}
v4l2_decode_param->num_poc_st_curr_before = i;
i = 0;
for (const auto& pic : ref_pic_set_st_curr_after) {
if (i >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX) {
VLOGF(1) << "Invalid DPB size";
break;
}
if (!pic) {
continue;
}
for (unsigned int j = 0; j < v4l2_decode_param->num_active_dpb_entries;
j++) {
if (pic->pic_order_cnt_val_ ==
v4l2_decode_param->dpb[j].pic_order_cnt_val) {
v4l2_decode_param->poc_st_curr_after[i++] = j;
break;
}
}
}
v4l2_decode_param->num_poc_st_curr_after = i;
i = 0;
for (const auto& pic : ref_pic_set_lt_curr) {
if (i >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX) {
VLOGF(1) << "Invalid DPB size";
break;
}
if (!pic) {
continue;
}
for (unsigned int j = 0; j < v4l2_decode_param->num_active_dpb_entries;
j++) {
if (pic->pic_order_cnt_val_ ==
v4l2_decode_param->dpb[j].pic_order_cnt_val) {
v4l2_decode_param->poc_lt_curr[i++] = j;
break;
}
}
}
v4l2_decode_param->num_poc_lt_curr = i;
return ref_surfaces;
}
H265Decoder::H265Accelerator::Status
V4L2VideoDecoderDelegateH265::SubmitFrameMetadata(
const H265SPS* sps,
const H265PPS* pps,
const H265SliceHeader* slice_hdr,
const H265Picture::Vector& ref_pic_list,
const H265Picture::Vector& ref_pic_set_lt_curr,
const H265Picture::Vector& ref_pic_set_st_curr_after,
const H265Picture::Vector& ref_pic_set_st_curr_before,
scoped_refptr<H265Picture> pic) {
struct v4l2_ext_control ctrl;
std::vector<struct v4l2_ext_control> ctrls;
struct v4l2_ctrl_hevc_sps v4l2_sps;
memset(&v4l2_sps, 0, sizeof(v4l2_sps));
int highest_tid = sps->sps_max_sub_layers_minus1;
// In the order of |struct v4l2_ctrl_hevc_sps|
v4l2_sps.video_parameter_set_id = sps->sps_video_parameter_set_id;
v4l2_sps.seq_parameter_set_id = sps->sps_seq_parameter_set_id;
#define SPS_TO_V4L2SPS(a) v4l2_sps.a = sps->a
SPS_TO_V4L2SPS(pic_width_in_luma_samples);
SPS_TO_V4L2SPS(pic_height_in_luma_samples);
SPS_TO_V4L2SPS(bit_depth_luma_minus8);
SPS_TO_V4L2SPS(bit_depth_chroma_minus8);
SPS_TO_V4L2SPS(log2_max_pic_order_cnt_lsb_minus4);
#define SPS_TO_V4L2SPS_FROM_ARRAY(a) v4l2_sps.a = sps->a[highest_tid];
SPS_TO_V4L2SPS_FROM_ARRAY(sps_max_dec_pic_buffering_minus1);
SPS_TO_V4L2SPS_FROM_ARRAY(sps_max_num_reorder_pics);
SPS_TO_V4L2SPS_FROM_ARRAY(sps_max_latency_increase_plus1);
#undef SPS_TO_V4L2SPS_FROM_ARRAY
SPS_TO_V4L2SPS(log2_min_luma_coding_block_size_minus3);
SPS_TO_V4L2SPS(log2_diff_max_min_luma_coding_block_size);
SPS_TO_V4L2SPS(log2_min_luma_transform_block_size_minus2);
SPS_TO_V4L2SPS(log2_diff_max_min_luma_transform_block_size);
SPS_TO_V4L2SPS(max_transform_hierarchy_depth_inter);
SPS_TO_V4L2SPS(max_transform_hierarchy_depth_intra);
SPS_TO_V4L2SPS(pcm_sample_bit_depth_luma_minus1);
SPS_TO_V4L2SPS(pcm_sample_bit_depth_chroma_minus1);
SPS_TO_V4L2SPS(log2_min_pcm_luma_coding_block_size_minus3);
SPS_TO_V4L2SPS(log2_diff_max_min_pcm_luma_coding_block_size);
SPS_TO_V4L2SPS(num_short_term_ref_pic_sets);
SPS_TO_V4L2SPS(num_long_term_ref_pics_sps);
SPS_TO_V4L2SPS(chroma_format_idc);
SPS_TO_V4L2SPS(sps_max_sub_layers_minus1);
#undef SPS_TO_V4L2SPS
#define SET_V4L2_SPS_FLAG_IF(cond, flag) \
v4l2_sps.flags |= ((sps->cond) ? (flag) : 0)
SET_V4L2_SPS_FLAG_IF(separate_colour_plane_flag,
V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE);
SET_V4L2_SPS_FLAG_IF(scaling_list_enabled_flag,
V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED);
SET_V4L2_SPS_FLAG_IF(amp_enabled_flag, V4L2_HEVC_SPS_FLAG_AMP_ENABLED);
SET_V4L2_SPS_FLAG_IF(sample_adaptive_offset_enabled_flag,
V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET);
SET_V4L2_SPS_FLAG_IF(pcm_enabled_flag, V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
SET_V4L2_SPS_FLAG_IF(pcm_loop_filter_disabled_flag,
V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED);
SET_V4L2_SPS_FLAG_IF(long_term_ref_pics_present_flag,
V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT);
SET_V4L2_SPS_FLAG_IF(sps_temporal_mvp_enabled_flag,
V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED);
SET_V4L2_SPS_FLAG_IF(strong_intra_smoothing_enabled_flag,
V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED);
#undef SET_V4L2_SPS_FLAG_IF
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_STATELESS_HEVC_SPS;
ctrl.size = sizeof(v4l2_sps);
ctrl.ptr = &v4l2_sps;
ctrls.push_back(ctrl);
struct v4l2_ctrl_hevc_pps v4l2_pps;
memset(&v4l2_pps, 0, sizeof(v4l2_pps));
// In the order of |struct v4l2_ctrl_hevc_pps|
#define PPS_TO_V4L2PPS(a) v4l2_pps.a = pps->a
v4l2_pps.pic_parameter_set_id = pps->pps_pic_parameter_set_id;
PPS_TO_V4L2PPS(num_extra_slice_header_bits);
PPS_TO_V4L2PPS(num_ref_idx_l0_default_active_minus1);
PPS_TO_V4L2PPS(num_ref_idx_l1_default_active_minus1);
PPS_TO_V4L2PPS(init_qp_minus26);
PPS_TO_V4L2PPS(diff_cu_qp_delta_depth);
PPS_TO_V4L2PPS(pps_cb_qp_offset);
PPS_TO_V4L2PPS(pps_cr_qp_offset);
if (pps->tiles_enabled_flag) {
PPS_TO_V4L2PPS(num_tile_columns_minus1);
PPS_TO_V4L2PPS(num_tile_rows_minus1);
// Cedrus currently requires userspace to provide the values regardless of
// uniform spacing. But since we don't support Cedrus's slice-based
// decoding, lets follow the spec for now.
if (!pps->uniform_spacing_flag) {
static_assert(std::size(v4l2_pps.column_width_minus1) >=
std::extent<decltype(pps->column_width_minus1)>(),
"column_width_minus1 arrays must be same size");
for (int i = 0; i <= pps->num_tile_columns_minus1; ++i) {
v4l2_pps.column_width_minus1[i] = pps->column_width_minus1[i];
}
static_assert(std::size(v4l2_pps.row_height_minus1) >=
std::extent<decltype(pps->row_height_minus1)>(),
"row_height_minus1 arrays must be same size");
for (int i = 0; i <= pps->num_tile_rows_minus1; ++i) {
v4l2_pps.row_height_minus1[i] = pps->row_height_minus1[i];
}
}
}
PPS_TO_V4L2PPS(pps_beta_offset_div2);
PPS_TO_V4L2PPS(pps_tc_offset_div2);
PPS_TO_V4L2PPS(log2_parallel_merge_level_minus2);
#undef PPS_TO_V4L2PPS
#define SET_V4L2_PPS_FLAG_IF(cond, flag) \
v4l2_pps.flags |= ((pps->cond) ? (flag) : 0)
SET_V4L2_PPS_FLAG_IF(dependent_slice_segments_enabled_flag,
V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED);
SET_V4L2_PPS_FLAG_IF(output_flag_present_flag,
V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT);
SET_V4L2_PPS_FLAG_IF(sign_data_hiding_enabled_flag,
V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED);
SET_V4L2_PPS_FLAG_IF(cabac_init_present_flag,
V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT);
SET_V4L2_PPS_FLAG_IF(constrained_intra_pred_flag,
V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED);
SET_V4L2_PPS_FLAG_IF(transform_skip_enabled_flag,
V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED);
SET_V4L2_PPS_FLAG_IF(cu_qp_delta_enabled_flag,
V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED);
SET_V4L2_PPS_FLAG_IF(pps_slice_chroma_qp_offsets_present_flag,
V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT);
SET_V4L2_PPS_FLAG_IF(weighted_pred_flag, V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED);
SET_V4L2_PPS_FLAG_IF(weighted_bipred_flag,
V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
SET_V4L2_PPS_FLAG_IF(transquant_bypass_enabled_flag,
V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED);
SET_V4L2_PPS_FLAG_IF(tiles_enabled_flag, V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
SET_V4L2_PPS_FLAG_IF(entropy_coding_sync_enabled_flag,
V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED);
SET_V4L2_PPS_FLAG_IF(loop_filter_across_tiles_enabled_flag,
V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED);
SET_V4L2_PPS_FLAG_IF(
pps_loop_filter_across_slices_enabled_flag,
V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED);
SET_V4L2_PPS_FLAG_IF(deblocking_filter_override_enabled_flag,
V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED);
SET_V4L2_PPS_FLAG_IF(pps_deblocking_filter_disabled_flag,
V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER);
SET_V4L2_PPS_FLAG_IF(lists_modification_present_flag,
V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT);
SET_V4L2_PPS_FLAG_IF(
slice_segment_header_extension_present_flag,
V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT);
SET_V4L2_PPS_FLAG_IF(deblocking_filter_control_present_flag,
V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
SET_V4L2_PPS_FLAG_IF(uniform_spacing_flag,
V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING);
#undef SET_V4L2_PPS_FLAG_IF
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_STATELESS_HEVC_PPS;
ctrl.size = sizeof(v4l2_pps);
ctrl.ptr = &v4l2_pps;
ctrls.push_back(ctrl);
struct v4l2_ctrl_hevc_scaling_matrix v4l2_scaling_matrix;
memset(&v4l2_scaling_matrix, 0, sizeof(v4l2_scaling_matrix));
struct H265ScalingListData checker;
// TODO(jkardatzke): Optimize storage of the 32x32 since only indices 0 and 3
// are actually used. See ../../video/h265_parser.h
static_assert(
std::size(checker.scaling_list_dc_coef_16x16) ==
std::size(v4l2_scaling_matrix.scaling_list_dc_coef_16x16) &&
std::size(checker.scaling_list_dc_coef_32x32) / 3 ==
std::size(v4l2_scaling_matrix.scaling_list_dc_coef_32x32) &&
std::size(checker.scaling_list_4x4) ==
std::size(v4l2_scaling_matrix.scaling_list_4x4) &&
std::size(checker.scaling_list_4x4[0]) ==
std::size(v4l2_scaling_matrix.scaling_list_4x4[0]) &&
std::size(checker.scaling_list_8x8) ==
std::size(v4l2_scaling_matrix.scaling_list_8x8) &&
std::size(checker.scaling_list_8x8[0]) ==
std::size(v4l2_scaling_matrix.scaling_list_8x8[0]) &&
std::size(checker.scaling_list_16x16) ==
std::size(v4l2_scaling_matrix.scaling_list_16x16) &&
std::size(checker.scaling_list_16x16[0]) ==
std::size(v4l2_scaling_matrix.scaling_list_16x16[0]) &&
std::size(checker.scaling_list_32x32) / 3 ==
std::size(v4l2_scaling_matrix.scaling_list_32x32) &&
std::size(checker.scaling_list_32x32[0]) ==
std::size(v4l2_scaling_matrix.scaling_list_32x32[0]),
"scaling_list_data must be of correct size");
if (sps->scaling_list_enabled_flag) {
// Copied from H265VaapiVideoDecoderDelegate:
// We already populated the scaling list data with default values in the
// parser if they are not present in the stream, so just fill them all in.
const auto& scaling_list = pps->pps_scaling_list_data_present_flag
? pps->scaling_list_data
: sps->scaling_list_data;
for (size_t i = 0; i < H265ScalingListData::kNumScalingListMatrices; ++i) {
for (size_t j = 0; j < H265ScalingListData::kScalingListSizeId0Count;
++j) {
v4l2_scaling_matrix.scaling_list_4x4[i][j] =
scaling_list.GetScalingList4x4EntryInRasterOrder(/*matrix_id=*/i,
/*raster_idx=*/j);
}
}
for (size_t i = 0; i < H265ScalingListData::kNumScalingListMatrices; ++i) {
for (size_t j = 0; j < H265ScalingListData::kScalingListSizeId1To3Count;
++j) {
v4l2_scaling_matrix.scaling_list_8x8[i][j] =
scaling_list.GetScalingList8x8EntryInRasterOrder(/*matrix_id=*/i,
/*raster_idx=*/j);
}
}
for (size_t i = 0; i < H265ScalingListData::kNumScalingListMatrices; ++i) {
for (size_t j = 0; j < H265ScalingListData::kScalingListSizeId1To3Count;
++j) {
v4l2_scaling_matrix.scaling_list_16x16[i][j] =
scaling_list.GetScalingList16x16EntryInRasterOrder(
/*matrix_id=*/i,
/*raster_idx=*/j);
}
}
for (size_t i = 0; i < H265ScalingListData::kNumScalingListMatrices;
i += 3) {
for (size_t j = 0; j < H265ScalingListData::kScalingListSizeId1To3Count;
++j) {
v4l2_scaling_matrix.scaling_list_32x32[i / 3][j] =
scaling_list.GetScalingList32x32EntryInRasterOrder(
/*matrix_id=*/i,
/*raster_idx=*/j);
}
}
memcpy(v4l2_scaling_matrix.scaling_list_dc_coef_16x16,
scaling_list.scaling_list_dc_coef_16x16,
sizeof(v4l2_scaling_matrix.scaling_list_dc_coef_16x16));
v4l2_scaling_matrix.scaling_list_dc_coef_32x32[0] =
scaling_list.scaling_list_dc_coef_32x32[0];
v4l2_scaling_matrix.scaling_list_dc_coef_32x32[1] =
scaling_list.scaling_list_dc_coef_32x32[3];
}
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX;
ctrl.size = sizeof(v4l2_scaling_matrix);
ctrl.ptr = &v4l2_scaling_matrix;
ctrls.push_back(ctrl);
scoped_refptr<V4L2DecodeSurface> dec_surface =
H265PictureToV4L2DecodeSurface(pic.get());
// In the order seen in |struct v4l2_hevc_decode_param|.
struct v4l2_ctrl_hevc_decode_params v4l2_decode_param = {
.pic_order_cnt_val = pic->pic_order_cnt_val_,
.short_term_ref_pic_set_size = static_cast<__u16>(slice_hdr->st_rps_bits),
.long_term_ref_pic_set_size = static_cast<__u16>(slice_hdr->lt_rps_bits),
#if BUILDFLAG(IS_CHROMEOS)
// .num_delta_pocs_of_ref_rps_idx is upstream but not yet pulled
// into linux build sysroot.
// TODO(wenst): Remove once linux-libc-dev package is updated to
// at least v6.5 in the sysroots.
.num_delta_pocs_of_ref_rps_idx =
static_cast<__u8>(slice_hdr->st_ref_pic_set.rps_idx_num_delta_pocs),
#endif
.flags = static_cast<__u64>(
(pic->irap_pic_ ? V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC : 0) |
((pic->nal_unit_type_ >= H265NALU::IDR_W_RADL &&
pic->nal_unit_type_ <= H265NALU::IDR_N_LP)
? V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC
: 0) |
(pic->no_output_of_prior_pics_flag_
? V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR
: 0)),
};
// Also sets remaining fields in v4l2_hevc_decode_param
auto ref_surfaces =
FillInV4L2DPB(&v4l2_decode_param, ref_pic_list, ref_pic_set_lt_curr,
ref_pic_set_st_curr_after, ref_pic_set_st_curr_before);
dec_surface->SetReferenceSurfaces(ref_surfaces);
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS;
ctrl.size = sizeof(v4l2_decode_param);
ctrl.ptr = &v4l2_decode_param;
ctrls.push_back(ctrl);
struct v4l2_ext_controls ext_ctrls;
memset(&ext_ctrls, 0, sizeof(ext_ctrls));
ext_ctrls.count = ctrls.size();
ext_ctrls.controls = ctrls.data();
dec_surface->PrepareSetCtrls(&ext_ctrls);
if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
RecordVidiocIoctlErrorUMA(VidiocIoctlRequests::kVidiocSExtCtrls);
VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
return Status::kFail;
}
return Status::kOk;
}
H265Decoder::H265Accelerator::Status V4L2VideoDecoderDelegateH265::SubmitSlice(
const H265SPS* sps,
const H265PPS* pps,
const H265SliceHeader* slice_hdr,
const H265Picture::Vector& ref_pic_list0,
const H265Picture::Vector& ref_pic_list1,
const H265Picture::Vector& ref_pic_set_lt_curr,
const H265Picture::Vector& ref_pic_set_st_curr_after,
const H265Picture::Vector& ref_pic_set_st_curr_before,
scoped_refptr<H265Picture> pic,
const uint8_t* data,
size_t size,
const std::vector<SubsampleEntry>& subsamples) {
scoped_refptr<V4L2DecodeSurface> dec_surface =
H265PictureToV4L2DecodeSurface(pic.get());
// Add the 3-bytes NAL start code.
// TODO: don't do it here, but have it passed from the parser?
const size_t data_copy_size = size + 3;
if (dec_surface->secure_handle()) {
// The secure world already post-processed the secure buffer so that all of
// the slice NALUs w/ 3 byte start codes are the only contents.
return surface_handler_->SubmitSlice(dec_surface.get(), nullptr,
data_copy_size)
? Status::kOk
: Status::kFail;
}
auto data_copy = base::HeapArray<uint8_t>::Uninit(data_copy_size);
memset(data_copy.data(), 0, data_copy_size);
data_copy[2] = 0x01;
memcpy(data_copy.data() + 3, data, size);
return surface_handler_->SubmitSlice(dec_surface.get(), data_copy.data(),
data_copy_size)
? Status::kOk
: Status::kFail;
}
H265Decoder::H265Accelerator::Status V4L2VideoDecoderDelegateH265::SubmitDecode(
scoped_refptr<H265Picture> pic) {
scoped_refptr<V4L2DecodeSurface> dec_surface =
H265PictureToV4L2DecodeSurface(pic.get());
Reset();
DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
surface_handler_->DecodeSurface(dec_surface);
return Status::kOk;
}
bool V4L2VideoDecoderDelegateH265::OutputPicture(
scoped_refptr<H265Picture> pic) {
surface_handler_->SurfaceReady(H265PictureToV4L2DecodeSurface(pic.get()),
pic->bitstream_id(), pic->visible_rect(),
pic->get_colorspace());
return true;
}
void V4L2VideoDecoderDelegateH265::Reset() {}
bool V4L2VideoDecoderDelegateH265::IsChromaSamplingSupported(
VideoChromaSampling chroma_sampling) {
return chroma_sampling == VideoChromaSampling::k420;
}
scoped_refptr<V4L2DecodeSurface>
V4L2VideoDecoderDelegateH265::H265PictureToV4L2DecodeSurface(H265Picture* pic) {
V4L2H265Picture* v4l2_pic = pic->AsV4L2H265Picture();
CHECK(v4l2_pic);
return v4l2_pic->dec_surface();
}
} // namespace media