| // 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/vaapi_h264_accelerator.h" |
| |
| #include <va/va.h> |
| |
| #include "base/stl_util.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 Status = 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}; |
| |
| } // namespace |
| |
| VaapiH264Accelerator::VaapiH264Accelerator( |
| DecodeSurfaceHandler<VASurface>* vaapi_dec, |
| scoped_refptr<VaapiWrapper> vaapi_wrapper) |
| : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { |
| DCHECK(vaapi_wrapper_); |
| DCHECK(vaapi_dec_); |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| } |
| |
| VaapiH264Accelerator::~VaapiH264Accelerator() { |
| // TODO(mcasas): consider enabling the checker, https://crbug.com/789160 |
| // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| scoped_refptr<H264Picture> VaapiH264Accelerator::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; |
| } |
| |
| Status VaapiH264Accelerator::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, |
| const scoped_refptr<H264Picture>& pic) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VAPictureParameterBufferH264 pic_param; |
| memset(&pic_param, 0, sizeof(pic_param)); |
| |
| #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, 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; |
| |
| if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, &pic_param)) |
| return Status::kFail; |
| |
| 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]; |
| } |
| } |
| |
| return vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, &iq_matrix_buf) |
| ? Status::kOk |
| : Status::kFail; |
| } |
| |
| Status VaapiH264Accelerator::SubmitSlice( |
| const H264PPS* pps, |
| const H264SliceHeader* slice_hdr, |
| const H264Picture::Vector& ref_pic_list0, |
| const H264Picture::Vector& ref_pic_list1, |
| const scoped_refptr<H264Picture>& pic, |
| const uint8_t* data, |
| size_t size, |
| const std::vector<SubsampleEntry>& subsamples) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 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 (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, &slice_param)) |
| return Status::kFail; |
| |
| return vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, size, data) |
| ? Status::kOk |
| : Status::kFail; |
| } |
| |
| Status VaapiH264Accelerator::SubmitDecode( |
| const scoped_refptr<H264Picture>& pic) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| const bool success = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( |
| pic->AsVaapiH264Picture()->va_surface()->id()); |
| return success ? Status::kOk : Status::kFail; |
| } |
| |
| bool VaapiH264Accelerator::OutputPicture( |
| const 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 VaapiH264Accelerator::Reset() { |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| vaapi_wrapper_->DestroyPendingBuffers(); |
| } |
| |
| void VaapiH264Accelerator::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()->va_surface()->id(); |
| |
| 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 VaapiH264Accelerator::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 |