From 1efa9d877e6ed0ce3fbf673d9d1ad3eb27e4ae76 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 11 Jul 2018 17:06:40 +0200 Subject: [PATCH] Add support for H264 decoding Signed-off-by: Maxime Ripard Signed-off-by: Paul Kocialkowski --- src/Makefile.am | 5 +- src/config.c | 19 ++++ src/context.c | 8 ++ src/h264.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++ src/h264.h | 36 +++++++ src/picture.c | 48 +++++++++ src/surface.h | 5 + 7 files changed, 400 insertions(+), 2 deletions(-) create mode 100644 src/h264.c create mode 100644 src/h264.h diff --git a/src/Makefile.am b/src/Makefile.am index d5c1247..85e0db9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,13 +5,14 @@ backend_ldflags = -module -avoid-version -no-undefined -Wl,--no-undefined backend_libs = -lpthread -ldl $(DRM_LIBS) $(LIBVA_DEPS_LIBS) backend_c = sunxi_cedrus.c object_heap.c config.c surface.c context.c buffer.c \ - mpeg2.c picture.c subpicture.c image.c v4l2.c video.c media.c utils.c + mpeg2.c picture.c subpicture.c image.c v4l2.c video.c media.c utils.c \ + h264.c backend_s = tiled_yuv.S backend_h = sunxi_cedrus.h object_heap.h config.h surface.h context.h buffer.h \ mpeg2.h picture.h subpicture.h image.h v4l2.h video.h media.h utils.h \ - tiled_yuv.h + tiled_yuv.h h264.h sunxi_cedrus_drv_video_la_LTLIBRARIES = sunxi_cedrus_drv_video.la sunxi_cedrus_drv_video_ladir = $(LIBVA_DRIVERS_PATH) diff --git a/src/config.c b/src/config.c index d556dcf..3ba2242 100644 --- a/src/config.c +++ b/src/config.c @@ -49,6 +49,11 @@ VAStatus SunxiCedrusCreateConfig(VADriverContextP context, VAProfile profile, switch (profile) { case VAProfileMPEG2Simple: case VAProfileMPEG2Main: + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: if (entrypoint != VAEntrypointVLD) return VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT; break; @@ -112,6 +117,15 @@ VAStatus SunxiCedrusQueryConfigProfiles(VADriverContextP context, profiles[index++] = VAProfileMPEG2Main; } + found = v4l2_find_format(driver_data->video_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_PIX_FMT_H264_SLICE); + if (found && index < (SUNXI_CEDRUS_MAX_CONFIG_ATTRIBUTES - 5)) { + profiles[index++] = VAProfileH264Main; + profiles[index++] = VAProfileH264High; + profiles[index++] = VAProfileH264ConstrainedBaseline; + profiles[index++] = VAProfileH264MultiviewHigh; + profiles[index++] = VAProfileH264StereoHigh; + } + *profiles_count = index; return VA_STATUS_SUCCESS; @@ -123,6 +137,11 @@ VAStatus SunxiCedrusQueryConfigEntrypoints(VADriverContextP context, switch (profile) { case VAProfileMPEG2Simple: case VAProfileMPEG2Main: + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: entrypoints[0] = VAEntrypointVLD; *entrypoints_count = 1; break; diff --git a/src/context.c b/src/context.c index 76b7ece..8a3a9e2 100644 --- a/src/context.c +++ b/src/context.c @@ -80,6 +80,14 @@ VAStatus SunxiCedrusCreateContext(VADriverContextP context, pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; break; + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + pixelformat = V4L2_PIX_FMT_H264_SLICE; + break; + default: status = VA_STATUS_ERROR_UNSUPPORTED_PROFILE; goto error; diff --git a/src/h264.c b/src/h264.c new file mode 100644 index 0000000..e7a0134 --- /dev/null +++ b/src/h264.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2018 Bootlin + * Copyright (c) 2016 Florent Revest, + * 2007 Intel Corporation. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include +#include + +#include + +#include "surface.h" +#include "sunxi_cedrus.h" +#include "v4l2.h" + +enum h264_slice_type { + H264_SLICE_P = 0, + H264_SLICE_B = 1, +}; + +static int h264_lookup_ref_pic(VAPictureParameterBufferH264 *VAPicture, + unsigned int frame_num) +{ + int i; + + for (i = 0; i < VAPicture->num_ref_frames; i++) { + VAPictureH264 *pic = &VAPicture->ReferenceFrames[i]; + + if (frame_num == pic->frame_idx) + return i; + } + + return 0; +} + +static void h264_va_picture_to_v4l2(struct sunxi_cedrus_driver_data *driver_data, + VAPictureParameterBufferH264 *VAPicture, + struct v4l2_ctrl_h264_decode_param *decode, + struct v4l2_ctrl_h264_pps *pps, + struct v4l2_ctrl_h264_sps *sps) +{ + int i; + + decode->num_slices = VAPicture->num_ref_frames; + decode->top_field_order_cnt = VAPicture->CurrPic.TopFieldOrderCnt; + decode->bottom_field_order_cnt = VAPicture->CurrPic.BottomFieldOrderCnt; + + for (i = 0; i < VAPicture->num_ref_frames; i++) { + struct v4l2_h264_dpb_entry *dpb = &decode->dpb[i]; + VAPictureH264 *pic = &VAPicture->ReferenceFrames[i]; + struct object_surface *surface_object = SURFACE(pic->picture_id); + + if (surface_object) + dpb->buf_index = surface_object->destination_index; + + dpb->frame_num = pic->frame_idx; + dpb->top_field_order_cnt = pic->TopFieldOrderCnt; + dpb->bottom_field_order_cnt = pic->BottomFieldOrderCnt; + + if (pic->flags & VA_PICTURE_H264_LONG_TERM_REFERENCE) + dpb->flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM; + + if (!(pic->flags & VA_PICTURE_H264_INVALID)) + dpb->flags |= V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; + } + + pps->weighted_bipred_idc = + VAPicture->pic_fields.bits.weighted_bipred_idc; + pps->pic_init_qs_minus26 = VAPicture->pic_init_qs_minus26; + pps->chroma_qp_index_offset = VAPicture->chroma_qp_index_offset; + pps->second_chroma_qp_index_offset = + VAPicture->second_chroma_qp_index_offset; + + if (VAPicture->pic_fields.bits.entropy_coding_mode_flag) + pps->flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; + + if (VAPicture->pic_fields.bits.weighted_pred_flag) + pps->flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED; + + if (VAPicture->pic_fields.bits.transform_8x8_mode_flag) + pps->flags |= V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE; + + if (VAPicture->pic_fields.bits.constrained_intra_pred_flag) + pps->flags |= V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED; + + if (VAPicture->pic_fields.bits.pic_order_present_flag) + pps->flags |= + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT; + + if (VAPicture->pic_fields.bits.deblocking_filter_control_present_flag) + pps->flags |= + V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT; + + if (VAPicture->pic_fields.bits.redundant_pic_cnt_present_flag) + pps->flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT; + + sps->chroma_format_idc = VAPicture->seq_fields.bits.chroma_format_idc; + sps->bit_depth_luma_minus8 = VAPicture->bit_depth_luma_minus8; + sps->bit_depth_chroma_minus8 = VAPicture->bit_depth_chroma_minus8; + sps->log2_max_frame_num_minus4 = + VAPicture->seq_fields.bits.log2_max_frame_num_minus4; + sps->log2_max_pic_order_cnt_lsb_minus4 = + VAPicture->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4; + sps->pic_order_cnt_type = VAPicture->seq_fields.bits.pic_order_cnt_type; + sps->pic_width_in_mbs_minus1 = VAPicture->picture_width_in_mbs_minus1; + sps->pic_height_in_map_units_minus1 = + VAPicture->picture_height_in_mbs_minus1; + + if (VAPicture->seq_fields.bits.residual_colour_transform_flag) + sps->flags |= V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE; + if (VAPicture->seq_fields.bits.gaps_in_frame_num_value_allowed_flag) + sps->flags |= + V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED; + if (VAPicture->seq_fields.bits.frame_mbs_only_flag) + sps->flags |= V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY; + if (VAPicture->seq_fields.bits.mb_adaptive_frame_field_flag) + sps->flags |= V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD; + if (VAPicture->seq_fields.bits.direct_8x8_inference_flag) + sps->flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE; + if (VAPicture->seq_fields.bits.delta_pic_order_always_zero_flag) + sps->flags |= V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO; +} + +static void h264_va_matrix_to_v4l2(struct sunxi_cedrus_driver_data *driver_data, + VAIQMatrixBufferH264 *VAMatrix, + struct v4l2_ctrl_h264_scaling_matrix *v4l2_matrix) +{ + memcpy(v4l2_matrix->scaling_list_4x4, &VAMatrix->ScalingList4x4, + sizeof(VAMatrix->ScalingList4x4)); + + /* + * In YUV422, there's only two matrices involved, while YUV444 + * needs 6. However, in the former case, the two matrices + * should be placed at the 0 and 3 offsets. + */ + memcpy(v4l2_matrix->scaling_list_8x8[0], &VAMatrix->ScalingList8x8[0], + sizeof(v4l2_matrix->scaling_list_8x8[0])); + memcpy(v4l2_matrix->scaling_list_8x8[3], &VAMatrix->ScalingList8x8[1], + sizeof(v4l2_matrix->scaling_list_8x8[3])); +} + +static void h264_va_slice_to_v4l2(struct sunxi_cedrus_driver_data *driver_data, + VASliceParameterBufferH264 *VASlice, + VAPictureParameterBufferH264 *VAPicture, + struct v4l2_ctrl_h264_slice_param *slice) +{ + struct v4l2_h264_weight_factors *factors; + int i; + + slice->size = VASlice->slice_data_size; + slice->header_bit_size = VASlice->slice_data_bit_offset; + slice->first_mb_in_slice = VASlice->first_mb_in_slice; + slice->slice_type = VASlice->slice_type; + slice->cabac_init_idc = VASlice->cabac_init_idc; + slice->slice_qp_delta = VASlice->slice_qp_delta; + slice->disable_deblocking_filter_idc = + VASlice->disable_deblocking_filter_idc; + slice->slice_alpha_c0_offset_div2 = VASlice->slice_alpha_c0_offset_div2; + slice->slice_beta_offset_div2 = VASlice->slice_beta_offset_div2; + + if (((VASlice->slice_type % 5) == H264_SLICE_P) || + ((VASlice->slice_type % 5) == H264_SLICE_B)) { + slice->num_ref_idx_l0_active_minus1 = + VASlice->num_ref_idx_l0_active_minus1; + + for (i = 0; i < VASlice->num_ref_idx_l0_active_minus1 + 1; i++) + slice->ref_pic_list0[i] = + h264_lookup_ref_pic(VAPicture, + VASlice->RefPicList0[i].frame_idx); + } + + if ((VASlice->slice_type % 5) == H264_SLICE_B) { + slice->num_ref_idx_l1_active_minus1 = + VASlice->num_ref_idx_l1_active_minus1; + + for (i = 0; i < VASlice->num_ref_idx_l1_active_minus1 + 1; i++) + slice->ref_pic_list1[i] = + h264_lookup_ref_pic(VAPicture, + VASlice->RefPicList1[i].frame_idx); + } + + if (VASlice->direct_spatial_mv_pred_flag) + slice->flags |= V4L2_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED; + + slice->pred_weight_table.chroma_log2_weight_denom = + VASlice->chroma_log2_weight_denom; + slice->pred_weight_table.luma_log2_weight_denom = + VASlice->luma_log2_weight_denom; + + factors = &slice->pred_weight_table.weight_factors[0]; + memcpy(&factors->chroma_weight, &VASlice->chroma_weight_l0, + sizeof(factors->chroma_weight)); + memcpy(&factors->chroma_offset, &VASlice->chroma_offset_l0, + sizeof(factors->chroma_offset)); + memcpy(&factors->luma_weight, &VASlice->luma_weight_l0, + sizeof(factors->luma_weight)); + memcpy(&factors->luma_offset, &VASlice->luma_offset_l0, + sizeof(factors->luma_offset)); + + factors = &slice->pred_weight_table.weight_factors[1]; + memcpy(&factors->chroma_weight, &VASlice->chroma_weight_l1, + sizeof(factors->chroma_weight)); + memcpy(&factors->chroma_offset, &VASlice->chroma_offset_l1, + sizeof(factors->chroma_offset)); + memcpy(&factors->luma_weight, &VASlice->luma_weight_l1, + sizeof(factors->luma_weight)); + memcpy(&factors->luma_offset, &VASlice->luma_offset_l1, + sizeof(factors->luma_offset)); +} + +int h264_set_controls(struct sunxi_cedrus_driver_data *driver_data, + struct object_surface *surface_object) +{ + struct v4l2_ctrl_h264_scaling_matrix matrix = { 0 }; + struct v4l2_ctrl_h264_decode_param decode = { 0 }; + struct v4l2_ctrl_h264_slice_param slice = { 0 }; + struct v4l2_ctrl_h264_pps pps = { 0 }; + struct v4l2_ctrl_h264_sps sps = { 0 }; + int rc; + + h264_va_picture_to_v4l2(driver_data, &surface_object->params.h264.picture, + &decode, &pps, &sps); + h264_va_matrix_to_v4l2(driver_data, &surface_object->params.h264.matrix, &matrix); + h264_va_slice_to_v4l2(driver_data, &surface_object->params.h264.slice, + &surface_object->params.h264.picture, &slice); + + rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd, + V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM, + &decode, sizeof(decode)); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd, + V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM, + &slice, sizeof(slice)); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd, + V4L2_CID_MPEG_VIDEO_H264_PPS, + &pps, sizeof(pps)); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd, + V4L2_CID_MPEG_VIDEO_H264_SPS, + &sps, sizeof(sps)); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd, + V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + &matrix, sizeof(matrix)); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + + return VA_STATUS_SUCCESS; +} diff --git a/src/h264.h b/src/h264.h new file mode 100644 index 0000000..8d52475 --- /dev/null +++ b/src/h264.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Bootlin + * Copyright (c) 2016 Florent Revest, + * 2007 Intel Corporation. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _H264_H_ +#define _H264_H_ + +struct object_surface; +struct sunxi_cedrus_driver_data; + +int h264_fill_controls(struct sunxi_cedrus_driver_data *driver, + struct object_surface *surface); + +#endif diff --git a/src/picture.c b/src/picture.c index 37131bb..24377bf 100644 --- a/src/picture.c +++ b/src/picture.c @@ -31,6 +31,7 @@ #include "config.h" #include "mpeg2.h" +#include "h264.h" #include #include @@ -67,6 +68,43 @@ static VAStatus codec_store_buffer(struct sunxi_cedrus_driver_data *driver_data, case VAProfileMPEG2Main: memcpy(&surface_object->params.mpeg2.picture, buffer_object->data, sizeof(surface_object->params.mpeg2.picture)); break; + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + memcpy(&surface_object->params.h264.picture, buffer_object->data, sizeof(surface_object->params.h264.picture)); + break; + default: + break; + } + break; + + case VASliceParameterBufferType: + switch (profile) { + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + memcpy(&surface_object->params.h264.slice, buffer_object->data, sizeof(surface_object->params.h264.slice)); + break; + + default: + break; + } + break; + + case VAIQMatrixBufferType: + switch (profile) { + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + memcpy(&surface_object->params.h264.matrix, buffer_object->data, sizeof(surface_object->params.h264.matrix)); + break; + default: break; } @@ -92,6 +130,16 @@ static VAStatus codec_set_controls(struct sunxi_cedrus_driver_data *driver_data, return VA_STATUS_ERROR_OPERATION_FAILED; break; + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + rc = h264_set_controls(driver_data, surface_object); + if (rc < 0) + return VA_STATUS_ERROR_OPERATION_FAILED; + break; + default: return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; } diff --git a/src/surface.h b/src/surface.h index d4765d2..e205c65 100644 --- a/src/surface.h +++ b/src/surface.h @@ -63,6 +63,11 @@ struct object_surface { struct { VAPictureParameterBufferMPEG2 picture; } mpeg2; + struct { + VAIQMatrixBufferH264 matrix; + VAPictureParameterBufferH264 picture; + VASliceParameterBufferH264 slice; + } h264; } params; int request_fd;