| // 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. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and spanify to fix the errors. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #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) { |
| drop_frame_ = false; |
| if (pic->no_rasl_output_flag_ && |
| (slice_hdr->nal_unit_type == H265NALU::RASL_N || |
| slice_hdr->nal_unit_type == H265NALU::RASL_R)) { |
| // Drop this RASL frame as this is not decodable. |
| DVLOGF(3) << "Drop RASL frame"; |
| drop_frame_ = true; |
| return Status::kOk; |
| } |
| |
| 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) { |
| if (drop_frame_) { |
| return Status::kOk; |
| } |
| |
| 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) { |
| if (drop_frame_) { |
| return Status::kOk; |
| } |
| |
| scoped_refptr<V4L2DecodeSurface> dec_surface = |
| H265PictureToV4L2DecodeSurface(pic.get()); |
| |
| 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() { |
| drop_frame_ = false; |
| } |
| |
| 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 |