Message ID | 20201209202513.27449-6-jonas@kwiboo.se |
---|---|
State | New |
Headers | show |
Series | Add V4L2 request API H.264 hwaccel | expand |
Context | Check | Description |
---|---|---|
andriy/x86_make | success | Make finished |
andriy/x86_make_fate | success | Make fate finished |
andriy/PPC64_make | success | Make finished |
andriy/PPC64_make_fate | warning | Make fate failed |
On 09/12/2020 20:25, Jonas Karlman wrote: > From: Jernej Skrabec <jernej.skrabec@siol.net> > > Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net> > Signed-off-by: Jonas Karlman <jonas@kwiboo.se> > --- > Changelog | 1 + > configure | 3 + > libavcodec/Makefile | 1 + > libavcodec/h264_slice.c | 4 + > libavcodec/h264dec.c | 3 + > libavcodec/hwaccels.h | 1 + > libavcodec/v4l2_request_h264.c | 457 +++++++++++++++++++++++++++++++++ > libavcodec/version.h | 4 +- > 8 files changed, 472 insertions(+), 2 deletions(-) > create mode 100644 libavcodec/v4l2_request_h264.c > > ... > diff --git a/libavcodec/v4l2_request_h264.c b/libavcodec/v4l2_request_h264.c > new file mode 100644 > index 0000000000..5ade6616e3 > --- /dev/null > +++ b/libavcodec/v4l2_request_h264.c > @@ -0,0 +1,457 @@ > +/* > + * This file is part of FFmpeg. > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include "h264dec.h" > +#include "hwconfig.h" > +#include "v4l2_request.h" > + > +#define OUTPUT_BUFFER_SIZE (4 * 1024 * 1024) Sounds like a random number. Given your comments about saving memory elsewhere, perhaps you can set it from the input stream? (E.g. with the dimensions of the input or MinCR from level.) > + > +typedef struct V4L2RequestControlsH264 { > + struct v4l2_ctrl_h264_sps sps; > + struct v4l2_ctrl_h264_pps pps; > + struct v4l2_ctrl_h264_scaling_matrix scaling_matrix; > + struct v4l2_ctrl_h264_decode_params decode_params; > + struct v4l2_ctrl_h264_slice_params slice_params; > + struct v4l2_ctrl_h264_pred_weights pred_weights; > + int pred_weights_required; > + int first_slice; > + int num_slices; > +} V4L2RequestControlsH264; > + > +typedef struct V4L2RequestContextH264 { > + V4L2RequestContext base; > + int decode_mode; > + int start_code; > +} V4L2RequestContextH264; > + > +static uint8_t nalu_slice_start_code[] = { 0x00, 0x00, 0x01 }; > + > +static void fill_weight_factors(struct v4l2_h264_weight_factors *factors, int list, const H264SliceContext *sl) > +{ > + for (int i = 0; i < sl->ref_count[list]; i++) { > + if (sl->pwt.luma_weight_flag[list]) { > + factors->luma_weight[i] = sl->pwt.luma_weight[i][list][0]; > + factors->luma_offset[i] = sl->pwt.luma_weight[i][list][1]; > + } else { > + factors->luma_weight[i] = 1 << sl->pwt.luma_log2_weight_denom; > + factors->luma_offset[i] = 0; > + } > + for (int j = 0; j < 2; j++) { > + if (sl->pwt.chroma_weight_flag[list]) { > + factors->chroma_weight[i][j] = sl->pwt.chroma_weight[i][list][j][0]; > + factors->chroma_offset[i][j] = sl->pwt.chroma_weight[i][list][j][1]; > + } else { > + factors->chroma_weight[i][j] = 1 << sl->pwt.chroma_log2_weight_denom; > + factors->chroma_offset[i][j] = 0; > + } > + } > + } > +} > + > +static void fill_dpb_entry(struct v4l2_h264_dpb_entry *entry, const H264Picture *pic) > +{ > + entry->reference_ts = ff_v4l2_request_get_capture_timestamp(pic->f); > + entry->pic_num = pic->pic_id; > + entry->frame_num = pic->frame_num; > + entry->fields = pic->reference & V4L2_H264_FRAME_REF; > + entry->flags = V4L2_H264_DPB_ENTRY_FLAG_VALID; > + if (entry->fields) > + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; That doesn't look right. Whether an entry in the DPB is active will depend on whether it is in RefPicList[01] of the current slice. > + if (pic->long_ref) > + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM; > + if (pic->field_picture) > + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_FIELD; > + if (pic->field_poc[0] != INT_MAX) > + entry->top_field_order_cnt = pic->field_poc[0]; > + if (pic->field_poc[1] != INT_MAX) > + entry->bottom_field_order_cnt = pic->field_poc[1]; > +} > + > +static void fill_dpb(struct v4l2_ctrl_h264_decode_params *decode, const H264Context *h) > +{ > + int entries = 0; > + > + for (int i = 0; i < h->short_ref_count; i++) { > + const H264Picture *pic = h->short_ref[i]; > + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) > + fill_dpb_entry(&decode->dpb[entries++], pic); > + } > + > + if (!h->long_ref_count) > + return; > + > + for (int i = 0; i < FF_ARRAY_ELEMS(h->long_ref); i++) { > + const H264Picture *pic = h->long_ref[i]; > + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) > + fill_dpb_entry(&decode->dpb[entries++], pic); > + } > +} > + > +static void fill_ref_list(struct v4l2_h264_reference *reference, struct v4l2_ctrl_h264_decode_params *decode, const H264Ref *ref) > +{ > + uint64_t timestamp; > + > + if (!ref->parent) > + return; > + > + timestamp = ff_v4l2_request_get_capture_timestamp(ref->parent->f); > + > + for (uint8_t i = 0; i < FF_ARRAY_ELEMS(decode->dpb); i++) { > + struct v4l2_h264_dpb_entry *entry = &decode->dpb[i]; > + if ((entry->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID) && > + entry->reference_ts == timestamp) { > + reference->fields = ref->reference & V4L2_H264_FRAME_REF; > + reference->index = i; > + return; > + } > + } > +} > + > +static void fill_sps(struct v4l2_ctrl_h264_sps *ctrl, const H264Context *h) > +{ > + const SPS *sps = h->ps.sps; > + > + *ctrl = (struct v4l2_ctrl_h264_sps) { > + .profile_idc = sps->profile_idc, > + .constraint_set_flags = sps->constraint_set_flags, > + .level_idc = sps->level_idc, > + .seq_parameter_set_id = sps->sps_id, > + .chroma_format_idc = sps->chroma_format_idc, > + .bit_depth_luma_minus8 = sps->bit_depth_luma - 8, > + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, > + .log2_max_frame_num_minus4 = sps->log2_max_frame_num - 4, > + .pic_order_cnt_type = sps->poc_type, > + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4, > + .max_num_ref_frames = sps->ref_frame_count, > + .num_ref_frames_in_pic_order_cnt_cycle = sps->poc_cycle_length, > + .offset_for_non_ref_pic = sps->offset_for_non_ref_pic, > + .offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field, > + .pic_width_in_mbs_minus1 = h->mb_width - 1, > + .pic_height_in_map_units_minus1 = sps->frame_mbs_only_flag ? h->mb_height - 1 : h->mb_height / 2 - 1, Structures like this are much easier to read if you align the '='s. > + }; > + > + if (sps->poc_cycle_length > 0 && sps->poc_cycle_length <= 255) > + memcpy(ctrl->offset_for_ref_frame, sps->offset_for_ref_frame, sps->poc_cycle_length * sizeof(ctrl->offset_for_ref_frame[0])); > + > + if (sps->residual_color_transform_flag) > + ctrl->flags |= V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE; > + if (sps->transform_bypass) > + ctrl->flags |= V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS; > + if (sps->delta_pic_order_always_zero_flag) > + ctrl->flags |= V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO; > + if (sps->gaps_in_frame_num_allowed_flag) > + ctrl->flags |= V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED; > + if (sps->frame_mbs_only_flag) > + ctrl->flags |= V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY; > + if (sps->mb_aff) > + ctrl->flags |= V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD; > + if (sps->direct_8x8_inference_flag) > + ctrl->flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE; > +} > + > +static void fill_pps(struct v4l2_ctrl_h264_pps *ctrl, const H264Context *h) > +{ > + const SPS *sps = h->ps.sps; > + const PPS *pps = h->ps.pps; > + const H264SliceContext *sl = &h->slice_ctx[0]; > + int qp_bd_offset = 6 * (sps->bit_depth_luma - 8); > + > + *ctrl = (struct v4l2_ctrl_h264_pps) { > + .pic_parameter_set_id = sl->pps_id, > + .seq_parameter_set_id = pps->sps_id, > + .num_slice_groups_minus1 = pps->slice_group_count - 1, > + .num_ref_idx_l0_default_active_minus1 = pps->ref_count[0] - 1, > + .num_ref_idx_l1_default_active_minus1 = pps->ref_count[1] - 1, > + .weighted_bipred_idc = pps->weighted_bipred_idc, > + .pic_init_qp_minus26 = pps->init_qp - 26 - qp_bd_offset, > + .pic_init_qs_minus26 = pps->init_qs - 26 - qp_bd_offset, > + .chroma_qp_index_offset = pps->chroma_qp_index_offset[0], > + .second_chroma_qp_index_offset = pps->chroma_qp_index_offset[1], > + }; > + > + if (pps->cabac) > + ctrl->flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; > + if (pps->pic_order_present) > + ctrl->flags |= V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT; > + if (pps->weighted_pred) > + ctrl->flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED; > + if (pps->deblocking_filter_parameters_present) > + ctrl->flags |= V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT; > + if (pps->constrained_intra_pred) > + ctrl->flags |= V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED; > + if (pps->redundant_pic_cnt_present) > + ctrl->flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT; > + if (pps->transform_8x8_mode) > + ctrl->flags |= V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE; > + > + /* FFmpeg always provide a scaling matrix */ > + ctrl->flags |= V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT; > +} > + > +static int v4l2_request_h264_start_frame(AVCodecContext *avctx, > + av_unused const uint8_t *buffer, > + av_unused uint32_t size) > +{ > + const H264Context *h = avctx->priv_data; > + const PPS *pps = h->ps.pps; > + const SPS *sps = h->ps.sps; > + const H264SliceContext *sl = &h->slice_ctx[0]; > + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; > + > + fill_sps(&controls->sps, h); > + fill_pps(&controls->pps, h); > + > + memcpy(controls->scaling_matrix.scaling_list_4x4, pps->scaling_matrix4, sizeof(controls->scaling_matrix.scaling_list_4x4)); > + memcpy(controls->scaling_matrix.scaling_list_8x8[0], pps->scaling_matrix8[0], sizeof(controls->scaling_matrix.scaling_list_8x8[0])); > + memcpy(controls->scaling_matrix.scaling_list_8x8[1], pps->scaling_matrix8[3], sizeof(controls->scaling_matrix.scaling_list_8x8[1])); > + > + if (sps->chroma_format_idc == 3) { > + memcpy(controls->scaling_matrix.scaling_list_8x8[2], pps->scaling_matrix8[1], sizeof(controls->scaling_matrix.scaling_list_8x8[2])); > + memcpy(controls->scaling_matrix.scaling_list_8x8[3], pps->scaling_matrix8[4], sizeof(controls->scaling_matrix.scaling_list_8x8[3])); > + memcpy(controls->scaling_matrix.scaling_list_8x8[4], pps->scaling_matrix8[2], sizeof(controls->scaling_matrix.scaling_list_8x8[4])); > + memcpy(controls->scaling_matrix.scaling_list_8x8[5], pps->scaling_matrix8[5], sizeof(controls->scaling_matrix.scaling_list_8x8[5])); > + } > + > + controls->decode_params = (struct v4l2_ctrl_h264_decode_params) { > + .nal_ref_idc = h->nal_ref_idc, > + .frame_num = h->poc.frame_num, > + .top_field_order_cnt = h->cur_pic_ptr->field_poc[0] != INT_MAX ? h->cur_pic_ptr->field_poc[0] : 0, > + .bottom_field_order_cnt = h->cur_pic_ptr->field_poc[1] != INT_MAX ? h->cur_pic_ptr->field_poc[1] : 0, > + .idr_pic_id = sl->idr_pic_id, > + .pic_order_cnt_lsb = sl->poc_lsb, > + .delta_pic_order_cnt_bottom = sl->delta_poc_bottom, > + .delta_pic_order_cnt0 = sl->delta_poc[0], > + .delta_pic_order_cnt1 = sl->delta_poc[1], > + /* size in bits of dec_ref_pic_marking() syntax element. */ > + .dec_ref_pic_marking_bit_size = sl->ref_pic_marking_bit_size, > + /* size in bits of pic order count syntax. */ > + .pic_order_cnt_bit_size = sl->pic_order_cnt_bit_size, > + .slice_group_change_cycle = 0, /* slice group not supported by FFmpeg */ > + }; > + > + if (h->picture_idr) > + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC; > + if (FIELD_PICTURE(h)) > + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC; > + if (h->picture_structure == PICT_BOTTOM_FIELD) > + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD; > + > + fill_dpb(&controls->decode_params, h); > + > + controls->first_slice = !FIELD_PICTURE(h) || h->first_field; > + controls->num_slices = 0; > + > + return ff_v4l2_request_reset_frame(avctx, h->cur_pic_ptr->f); > +} > + > +static int v4l2_request_h264_queue_decode(AVCodecContext *avctx, int last_slice) > +{ > + const H264Context *h = avctx->priv_data; > + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; > + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; > + > + struct v4l2_ext_control control[] = { > + { > + .id = V4L2_CID_STATELESS_H264_SPS, > + .ptr = &controls->sps, > + .size = sizeof(controls->sps), > + }, > + { > + .id = V4L2_CID_STATELESS_H264_PPS, > + .ptr = &controls->pps, > + .size = sizeof(controls->pps), > + }, > + { > + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, > + .ptr = &controls->scaling_matrix, > + .size = sizeof(controls->scaling_matrix), > + }, > + { > + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, > + .ptr = &controls->decode_params, > + .size = sizeof(controls->decode_params), > + }, The way I read it, these are submitted redundantly with every slice of a picture. Is that intended? > + { > + .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, > + .ptr = &controls->slice_params, > + .size = sizeof(controls->slice_params), > + }, > + { > + .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, > + .ptr = &controls->pred_weights, > + .size = sizeof(controls->pred_weights), > + }, > + }; > + > + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) { > + int count = FF_ARRAY_ELEMS(control) - (controls->pred_weights_required ? 0 : 1); > + return ff_v4l2_request_decode_slice(avctx, h->cur_pic_ptr->f, control, count, controls->first_slice, last_slice); > + } > + > + return ff_v4l2_request_decode_frame(avctx, h->cur_pic_ptr->f, control, FF_ARRAY_ELEMS(control) - 2); Could the submissions here be returning decode errors, or do they only turn up on the capture dequeue later? > +} > + > +static int v4l2_request_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) > +{ > + const H264Context *h = avctx->priv_data; > + const PPS *pps = h->ps.pps; > + const H264SliceContext *sl = &h->slice_ctx[0]; > + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; > + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; > + int i, ret, count; > + > + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && controls->num_slices) { > + ret = v4l2_request_h264_queue_decode(avctx, 0); > + if (ret) > + return ret; > + > + ff_v4l2_request_reset_frame(avctx, h->cur_pic_ptr->f); > + controls->first_slice = 0; > + } > + > + if (ctx->start_code == V4L2_STATELESS_H264_START_CODE_ANNEX_B) { > + ret = ff_v4l2_request_append_output_buffer(avctx, h->cur_pic_ptr->f, nalu_slice_start_code, 3); > + if (ret) > + return ret; > + } > + > + ret = ff_v4l2_request_append_output_buffer(avctx, h->cur_pic_ptr->f, buffer, size); > + if (ret) > + return ret; > + > + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) > + return 0; > + > + controls->slice_params = (struct v4l2_ctrl_h264_slice_params) { > + /* offset in bits to slice_data() from the beginning of this slice. */ > + .header_bit_size = get_bits_count(&sl->gb), > + .first_mb_in_slice = sl->first_mb_addr, > + .slice_type = ff_h264_get_slice_type(sl), > + .colour_plane_id = 0, /* separate colour plane not supported by FFmpeg */ > + .redundant_pic_cnt = sl->redundant_pic_count, > + .cabac_init_idc = sl->cabac_init_idc, > + .slice_qp_delta = sl->qscale - pps->init_qp, > + .slice_qs_delta = 0, /* not implemented by FFmpeg */ > + .disable_deblocking_filter_idc = sl->deblocking_filter < 2 ? !sl->deblocking_filter : sl->deblocking_filter, > + .slice_alpha_c0_offset_div2 = sl->slice_alpha_c0_offset / 2, > + .slice_beta_offset_div2 = sl->slice_beta_offset / 2, > + .num_ref_idx_l0_active_minus1 = sl->list_count > 0 ? sl->ref_count[0] - 1 : 0, > + .num_ref_idx_l1_active_minus1 = sl->list_count > 1 ? sl->ref_count[1] - 1 : 0, > + }; > + > + if (sl->slice_type == AV_PICTURE_TYPE_B && sl->direct_spatial_mv_pred) > + controls->slice_params.flags |= V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED; > + /* V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH: not implemented by FFmpeg */ > + > + controls->pred_weights_required = V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(&controls->pps, &controls->slice_params); > + if (controls->pred_weights_required) { > + controls->pred_weights.chroma_log2_weight_denom = sl->pwt.chroma_log2_weight_denom; > + controls->pred_weights.luma_log2_weight_denom = sl->pwt.luma_log2_weight_denom; > + } > + > + count = sl->list_count > 0 ? sl->ref_count[0] : 0; > + for (i = 0; i < count; i++) > + fill_ref_list(&controls->slice_params.ref_pic_list0[i], &controls->decode_params, &sl->ref_list[0][i]); > + if (count && controls->pred_weights_required) > + fill_weight_factors(&controls->pred_weights.weight_factors[0], 0, sl); > + > + count = sl->list_count > 1 ? sl->ref_count[1] : 0; > + for (i = 0; i < count; i++) > + fill_ref_list(&controls->slice_params.ref_pic_list1[i], &controls->decode_params, &sl->ref_list[1][i]); > + if (count && controls->pred_weights_required) > + fill_weight_factors(&controls->pred_weights.weight_factors[1], 1, sl); > + > + controls->num_slices++; > + return 0; > +} > + > +static int v4l2_request_h264_end_frame(AVCodecContext *avctx) > +{ > + const H264Context *h = avctx->priv_data; > + > + return v4l2_request_h264_queue_decode(avctx, !FIELD_PICTURE(h) || !h->first_field); > +} > + > +static int v4l2_request_h264_set_controls(AVCodecContext *avctx) > +{ > + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; > + > + struct v4l2_ext_control control[] = { > + { .id = V4L2_CID_STATELESS_H264_DECODE_MODE, }, > + { .id = V4L2_CID_STATELESS_H264_START_CODE, }, > + }; > + > + ctx->decode_mode = ff_v4l2_request_query_control_default_value(avctx, V4L2_CID_STATELESS_H264_DECODE_MODE); > + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && > + ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED) { > + av_log(avctx, AV_LOG_ERROR, "%s: unsupported decode mode, %d\n", __func__, ctx->decode_mode); > + return AVERROR(EINVAL); > + } > + > + ctx->start_code = ff_v4l2_request_query_control_default_value(avctx, V4L2_CID_STATELESS_H264_START_CODE); > + if (ctx->start_code != V4L2_STATELESS_H264_START_CODE_NONE && > + ctx->start_code != V4L2_STATELESS_H264_START_CODE_ANNEX_B) { > + av_log(avctx, AV_LOG_ERROR, "%s: unsupported start code, %d\n", __func__, ctx->start_code); > + return AVERROR(EINVAL); > + } What hardware implements each of the different modes you've got here (slice/frame + none/annexb)? > + > + control[0].value = ctx->decode_mode; > + control[1].value = ctx->start_code; > + > + return ff_v4l2_request_set_controls(avctx, control, FF_ARRAY_ELEMS(control)); > +} > + > +static int v4l2_request_h264_init(AVCodecContext *avctx) > +{ > + const H264Context *h = avctx->priv_data; > + struct v4l2_ctrl_h264_sps sps; > + int ret; > + > + struct v4l2_ext_control control[] = { > + { > + .id = V4L2_CID_STATELESS_H264_SPS, > + .ptr = &sps, > + .size = sizeof(sps), > + }, > + }; > + > + fill_sps(&sps, h); > + > + ret = ff_v4l2_request_init(avctx, V4L2_PIX_FMT_H264_SLICE, OUTPUT_BUFFER_SIZE, control, FF_ARRAY_ELEMS(control)); > + if (ret) > + return ret; > + > + return v4l2_request_h264_set_controls(avctx); > +} > + > +const AVHWAccel ff_h264_v4l2request_hwaccel = { > + .name = "h264_v4l2request", > + .type = AVMEDIA_TYPE_VIDEO, > + .id = AV_CODEC_ID_H264, > + .pix_fmt = AV_PIX_FMT_DRM_PRIME, > + .start_frame = v4l2_request_h264_start_frame, > + .decode_slice = v4l2_request_h264_decode_slice, > + .end_frame = v4l2_request_h264_end_frame, > + .frame_priv_data_size = sizeof(V4L2RequestControlsH264), > + .init = v4l2_request_h264_init, > + .uninit = ff_v4l2_request_uninit, > + .priv_data_size = sizeof(V4L2RequestContextH264), > + .frame_params = ff_v4l2_request_frame_params, > + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, > +}; > diff --git a/libavcodec/version.h b/libavcodec/version.h > index 5b92afe60a..1420439044 100644 > --- a/libavcodec/version.h > +++ b/libavcodec/version.h > @@ -28,8 +28,8 @@ > #include "libavutil/version.h" > > #define LIBAVCODEC_VERSION_MAJOR 58 > -#define LIBAVCODEC_VERSION_MINOR 115 > -#define LIBAVCODEC_VERSION_MICRO 102 > +#define LIBAVCODEC_VERSION_MINOR 116 > +#define LIBAVCODEC_VERSION_MICRO 100 > > #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ > LIBAVCODEC_VERSION_MINOR, \ > Thanks, - Mark
diff --git a/Changelog b/Changelog index 8f5e849f8d..f7930e0816 100644 --- a/Changelog +++ b/Changelog @@ -52,6 +52,7 @@ version <next>: - Microsoft Paint (MSP) version 2 decoder - Microsoft Paint (MSP) demuxer - AV1 monochrome encoding support via libaom >= 2.0.1 +- V4L2 mem2mem stateless H.264 hwaccel version 4.3: diff --git a/configure b/configure index fac85bfab4..56a8c407f3 100755 --- a/configure +++ b/configure @@ -2945,6 +2945,8 @@ h264_dxva2_hwaccel_deps="dxva2" h264_dxva2_hwaccel_select="h264_decoder" h264_nvdec_hwaccel_deps="nvdec" h264_nvdec_hwaccel_select="h264_decoder" +h264_v4l2request_hwaccel_deps="v4l2_request h264_v4l2_request" +h264_v4l2request_hwaccel_select="h264_decoder" h264_vaapi_hwaccel_deps="vaapi" h264_vaapi_hwaccel_select="h264_decoder" h264_vdpau_hwaccel_deps="vdpau" @@ -6613,6 +6615,7 @@ if enabled v4l2_m2m; then fi check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns +check_cc h264_v4l2_request linux/videodev2.h "int i = V4L2_PIX_FMT_H264_SLICE;" check_headers sys/videoio.h test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 2fafc4e028..dcda71fad0 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -926,6 +926,7 @@ OBJS-$(CONFIG_H264_D3D11VA_HWACCEL) += dxva2_h264.o OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o OBJS-$(CONFIG_H264_NVDEC_HWACCEL) += nvdec_h264.o OBJS-$(CONFIG_H264_QSV_HWACCEL) += qsvdec_h2645.o +OBJS-$(CONFIG_H264_V4L2REQUEST_HWACCEL) += v4l2_request_h264.o OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o OBJS-$(CONFIG_H264_VDPAU_HWACCEL) += vdpau_h264.o OBJS-$(CONFIG_H264_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 5ad1c347ed..533d84bb01 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -769,6 +769,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #define HWACCEL_MAX (CONFIG_H264_DXVA2_HWACCEL + \ (CONFIG_H264_D3D11VA_HWACCEL * 2) + \ CONFIG_H264_NVDEC_HWACCEL + \ + CONFIG_H264_V4L2REQUEST_HWACCEL + \ CONFIG_H264_VAAPI_HWACCEL + \ CONFIG_H264_VIDEOTOOLBOX_HWACCEL + \ CONFIG_H264_VDPAU_HWACCEL) @@ -853,6 +854,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #endif #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_H264_V4L2REQUEST_HWACCEL + *fmt++ = AV_PIX_FMT_DRM_PRIME; #endif if (h->avctx->codec->pix_fmts) choices = h->avctx->codec->pix_fmts; diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 46495d586f..4ad4d3a3dd 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -1075,6 +1075,9 @@ AVCodec ff_h264_decoder = { #endif #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(h264), +#endif +#if CONFIG_H264_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(h264), #endif NULL }, diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h index 8e54cf73f9..969a1da0f4 100644 --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -32,6 +32,7 @@ extern const AVHWAccel ff_h264_d3d11va_hwaccel; extern const AVHWAccel ff_h264_d3d11va2_hwaccel; extern const AVHWAccel ff_h264_dxva2_hwaccel; extern const AVHWAccel ff_h264_nvdec_hwaccel; +extern const AVHWAccel ff_h264_v4l2request_hwaccel; extern const AVHWAccel ff_h264_vaapi_hwaccel; extern const AVHWAccel ff_h264_vdpau_hwaccel; extern const AVHWAccel ff_h264_videotoolbox_hwaccel; diff --git a/libavcodec/v4l2_request_h264.c b/libavcodec/v4l2_request_h264.c new file mode 100644 index 0000000000..5ade6616e3 --- /dev/null +++ b/libavcodec/v4l2_request_h264.c @@ -0,0 +1,457 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "h264dec.h" +#include "hwconfig.h" +#include "v4l2_request.h" + +#define OUTPUT_BUFFER_SIZE (4 * 1024 * 1024) + +typedef struct V4L2RequestControlsH264 { + struct v4l2_ctrl_h264_sps sps; + struct v4l2_ctrl_h264_pps pps; + struct v4l2_ctrl_h264_scaling_matrix scaling_matrix; + struct v4l2_ctrl_h264_decode_params decode_params; + struct v4l2_ctrl_h264_slice_params slice_params; + struct v4l2_ctrl_h264_pred_weights pred_weights; + int pred_weights_required; + int first_slice; + int num_slices; +} V4L2RequestControlsH264; + +typedef struct V4L2RequestContextH264 { + V4L2RequestContext base; + int decode_mode; + int start_code; +} V4L2RequestContextH264; + +static uint8_t nalu_slice_start_code[] = { 0x00, 0x00, 0x01 }; + +static void fill_weight_factors(struct v4l2_h264_weight_factors *factors, int list, const H264SliceContext *sl) +{ + for (int i = 0; i < sl->ref_count[list]; i++) { + if (sl->pwt.luma_weight_flag[list]) { + factors->luma_weight[i] = sl->pwt.luma_weight[i][list][0]; + factors->luma_offset[i] = sl->pwt.luma_weight[i][list][1]; + } else { + factors->luma_weight[i] = 1 << sl->pwt.luma_log2_weight_denom; + factors->luma_offset[i] = 0; + } + for (int j = 0; j < 2; j++) { + if (sl->pwt.chroma_weight_flag[list]) { + factors->chroma_weight[i][j] = sl->pwt.chroma_weight[i][list][j][0]; + factors->chroma_offset[i][j] = sl->pwt.chroma_weight[i][list][j][1]; + } else { + factors->chroma_weight[i][j] = 1 << sl->pwt.chroma_log2_weight_denom; + factors->chroma_offset[i][j] = 0; + } + } + } +} + +static void fill_dpb_entry(struct v4l2_h264_dpb_entry *entry, const H264Picture *pic) +{ + entry->reference_ts = ff_v4l2_request_get_capture_timestamp(pic->f); + entry->pic_num = pic->pic_id; + entry->frame_num = pic->frame_num; + entry->fields = pic->reference & V4L2_H264_FRAME_REF; + entry->flags = V4L2_H264_DPB_ENTRY_FLAG_VALID; + if (entry->fields) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; + if (pic->long_ref) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM; + if (pic->field_picture) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_FIELD; + if (pic->field_poc[0] != INT_MAX) + entry->top_field_order_cnt = pic->field_poc[0]; + if (pic->field_poc[1] != INT_MAX) + entry->bottom_field_order_cnt = pic->field_poc[1]; +} + +static void fill_dpb(struct v4l2_ctrl_h264_decode_params *decode, const H264Context *h) +{ + int entries = 0; + + for (int i = 0; i < h->short_ref_count; i++) { + const H264Picture *pic = h->short_ref[i]; + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) + fill_dpb_entry(&decode->dpb[entries++], pic); + } + + if (!h->long_ref_count) + return; + + for (int i = 0; i < FF_ARRAY_ELEMS(h->long_ref); i++) { + const H264Picture *pic = h->long_ref[i]; + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) + fill_dpb_entry(&decode->dpb[entries++], pic); + } +} + +static void fill_ref_list(struct v4l2_h264_reference *reference, struct v4l2_ctrl_h264_decode_params *decode, const H264Ref *ref) +{ + uint64_t timestamp; + + if (!ref->parent) + return; + + timestamp = ff_v4l2_request_get_capture_timestamp(ref->parent->f); + + for (uint8_t i = 0; i < FF_ARRAY_ELEMS(decode->dpb); i++) { + struct v4l2_h264_dpb_entry *entry = &decode->dpb[i]; + if ((entry->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID) && + entry->reference_ts == timestamp) { + reference->fields = ref->reference & V4L2_H264_FRAME_REF; + reference->index = i; + return; + } + } +} + +static void fill_sps(struct v4l2_ctrl_h264_sps *ctrl, const H264Context *h) +{ + const SPS *sps = h->ps.sps; + + *ctrl = (struct v4l2_ctrl_h264_sps) { + .profile_idc = sps->profile_idc, + .constraint_set_flags = sps->constraint_set_flags, + .level_idc = sps->level_idc, + .seq_parameter_set_id = sps->sps_id, + .chroma_format_idc = sps->chroma_format_idc, + .bit_depth_luma_minus8 = sps->bit_depth_luma - 8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, + .log2_max_frame_num_minus4 = sps->log2_max_frame_num - 4, + .pic_order_cnt_type = sps->poc_type, + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4, + .max_num_ref_frames = sps->ref_frame_count, + .num_ref_frames_in_pic_order_cnt_cycle = sps->poc_cycle_length, + .offset_for_non_ref_pic = sps->offset_for_non_ref_pic, + .offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field, + .pic_width_in_mbs_minus1 = h->mb_width - 1, + .pic_height_in_map_units_minus1 = sps->frame_mbs_only_flag ? h->mb_height - 1 : h->mb_height / 2 - 1, + }; + + if (sps->poc_cycle_length > 0 && sps->poc_cycle_length <= 255) + memcpy(ctrl->offset_for_ref_frame, sps->offset_for_ref_frame, sps->poc_cycle_length * sizeof(ctrl->offset_for_ref_frame[0])); + + if (sps->residual_color_transform_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE; + if (sps->transform_bypass) + ctrl->flags |= V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS; + if (sps->delta_pic_order_always_zero_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO; + if (sps->gaps_in_frame_num_allowed_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED; + if (sps->frame_mbs_only_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY; + if (sps->mb_aff) + ctrl->flags |= V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD; + if (sps->direct_8x8_inference_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE; +} + +static void fill_pps(struct v4l2_ctrl_h264_pps *ctrl, const H264Context *h) +{ + const SPS *sps = h->ps.sps; + const PPS *pps = h->ps.pps; + const H264SliceContext *sl = &h->slice_ctx[0]; + int qp_bd_offset = 6 * (sps->bit_depth_luma - 8); + + *ctrl = (struct v4l2_ctrl_h264_pps) { + .pic_parameter_set_id = sl->pps_id, + .seq_parameter_set_id = pps->sps_id, + .num_slice_groups_minus1 = pps->slice_group_count - 1, + .num_ref_idx_l0_default_active_minus1 = pps->ref_count[0] - 1, + .num_ref_idx_l1_default_active_minus1 = pps->ref_count[1] - 1, + .weighted_bipred_idc = pps->weighted_bipred_idc, + .pic_init_qp_minus26 = pps->init_qp - 26 - qp_bd_offset, + .pic_init_qs_minus26 = pps->init_qs - 26 - qp_bd_offset, + .chroma_qp_index_offset = pps->chroma_qp_index_offset[0], + .second_chroma_qp_index_offset = pps->chroma_qp_index_offset[1], + }; + + if (pps->cabac) + ctrl->flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; + if (pps->pic_order_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT; + if (pps->weighted_pred) + ctrl->flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED; + if (pps->deblocking_filter_parameters_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT; + if (pps->constrained_intra_pred) + ctrl->flags |= V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED; + if (pps->redundant_pic_cnt_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT; + if (pps->transform_8x8_mode) + ctrl->flags |= V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE; + + /* FFmpeg always provide a scaling matrix */ + ctrl->flags |= V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT; +} + +static int v4l2_request_h264_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const PPS *pps = h->ps.pps; + const SPS *sps = h->ps.sps; + const H264SliceContext *sl = &h->slice_ctx[0]; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + + fill_sps(&controls->sps, h); + fill_pps(&controls->pps, h); + + memcpy(controls->scaling_matrix.scaling_list_4x4, pps->scaling_matrix4, sizeof(controls->scaling_matrix.scaling_list_4x4)); + memcpy(controls->scaling_matrix.scaling_list_8x8[0], pps->scaling_matrix8[0], sizeof(controls->scaling_matrix.scaling_list_8x8[0])); + memcpy(controls->scaling_matrix.scaling_list_8x8[1], pps->scaling_matrix8[3], sizeof(controls->scaling_matrix.scaling_list_8x8[1])); + + if (sps->chroma_format_idc == 3) { + memcpy(controls->scaling_matrix.scaling_list_8x8[2], pps->scaling_matrix8[1], sizeof(controls->scaling_matrix.scaling_list_8x8[2])); + memcpy(controls->scaling_matrix.scaling_list_8x8[3], pps->scaling_matrix8[4], sizeof(controls->scaling_matrix.scaling_list_8x8[3])); + memcpy(controls->scaling_matrix.scaling_list_8x8[4], pps->scaling_matrix8[2], sizeof(controls->scaling_matrix.scaling_list_8x8[4])); + memcpy(controls->scaling_matrix.scaling_list_8x8[5], pps->scaling_matrix8[5], sizeof(controls->scaling_matrix.scaling_list_8x8[5])); + } + + controls->decode_params = (struct v4l2_ctrl_h264_decode_params) { + .nal_ref_idc = h->nal_ref_idc, + .frame_num = h->poc.frame_num, + .top_field_order_cnt = h->cur_pic_ptr->field_poc[0] != INT_MAX ? h->cur_pic_ptr->field_poc[0] : 0, + .bottom_field_order_cnt = h->cur_pic_ptr->field_poc[1] != INT_MAX ? h->cur_pic_ptr->field_poc[1] : 0, + .idr_pic_id = sl->idr_pic_id, + .pic_order_cnt_lsb = sl->poc_lsb, + .delta_pic_order_cnt_bottom = sl->delta_poc_bottom, + .delta_pic_order_cnt0 = sl->delta_poc[0], + .delta_pic_order_cnt1 = sl->delta_poc[1], + /* size in bits of dec_ref_pic_marking() syntax element. */ + .dec_ref_pic_marking_bit_size = sl->ref_pic_marking_bit_size, + /* size in bits of pic order count syntax. */ + .pic_order_cnt_bit_size = sl->pic_order_cnt_bit_size, + .slice_group_change_cycle = 0, /* slice group not supported by FFmpeg */ + }; + + if (h->picture_idr) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC; + if (FIELD_PICTURE(h)) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC; + if (h->picture_structure == PICT_BOTTOM_FIELD) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD; + + fill_dpb(&controls->decode_params, h); + + controls->first_slice = !FIELD_PICTURE(h) || h->first_field; + controls->num_slices = 0; + + return ff_v4l2_request_reset_frame(avctx, h->cur_pic_ptr->f); +} + +static int v4l2_request_h264_queue_decode(AVCodecContext *avctx, int last_slice) +{ + const H264Context *h = avctx->priv_data; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_H264_SPS, + .ptr = &controls->sps, + .size = sizeof(controls->sps), + }, + { + .id = V4L2_CID_STATELESS_H264_PPS, + .ptr = &controls->pps, + .size = sizeof(controls->pps), + }, + { + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + .ptr = &controls->scaling_matrix, + .size = sizeof(controls->scaling_matrix), + }, + { + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + .ptr = &controls->decode_params, + .size = sizeof(controls->decode_params), + }, + { + .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, + .ptr = &controls->slice_params, + .size = sizeof(controls->slice_params), + }, + { + .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, + .ptr = &controls->pred_weights, + .size = sizeof(controls->pred_weights), + }, + }; + + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) { + int count = FF_ARRAY_ELEMS(control) - (controls->pred_weights_required ? 0 : 1); + return ff_v4l2_request_decode_slice(avctx, h->cur_pic_ptr->f, control, count, controls->first_slice, last_slice); + } + + return ff_v4l2_request_decode_frame(avctx, h->cur_pic_ptr->f, control, FF_ARRAY_ELEMS(control) - 2); +} + +static int v4l2_request_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const PPS *pps = h->ps.pps; + const H264SliceContext *sl = &h->slice_ctx[0]; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + int i, ret, count; + + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && controls->num_slices) { + ret = v4l2_request_h264_queue_decode(avctx, 0); + if (ret) + return ret; + + ff_v4l2_request_reset_frame(avctx, h->cur_pic_ptr->f); + controls->first_slice = 0; + } + + if (ctx->start_code == V4L2_STATELESS_H264_START_CODE_ANNEX_B) { + ret = ff_v4l2_request_append_output_buffer(avctx, h->cur_pic_ptr->f, nalu_slice_start_code, 3); + if (ret) + return ret; + } + + ret = ff_v4l2_request_append_output_buffer(avctx, h->cur_pic_ptr->f, buffer, size); + if (ret) + return ret; + + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) + return 0; + + controls->slice_params = (struct v4l2_ctrl_h264_slice_params) { + /* offset in bits to slice_data() from the beginning of this slice. */ + .header_bit_size = get_bits_count(&sl->gb), + .first_mb_in_slice = sl->first_mb_addr, + .slice_type = ff_h264_get_slice_type(sl), + .colour_plane_id = 0, /* separate colour plane not supported by FFmpeg */ + .redundant_pic_cnt = sl->redundant_pic_count, + .cabac_init_idc = sl->cabac_init_idc, + .slice_qp_delta = sl->qscale - pps->init_qp, + .slice_qs_delta = 0, /* not implemented by FFmpeg */ + .disable_deblocking_filter_idc = sl->deblocking_filter < 2 ? !sl->deblocking_filter : sl->deblocking_filter, + .slice_alpha_c0_offset_div2 = sl->slice_alpha_c0_offset / 2, + .slice_beta_offset_div2 = sl->slice_beta_offset / 2, + .num_ref_idx_l0_active_minus1 = sl->list_count > 0 ? sl->ref_count[0] - 1 : 0, + .num_ref_idx_l1_active_minus1 = sl->list_count > 1 ? sl->ref_count[1] - 1 : 0, + }; + + if (sl->slice_type == AV_PICTURE_TYPE_B && sl->direct_spatial_mv_pred) + controls->slice_params.flags |= V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED; + /* V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH: not implemented by FFmpeg */ + + controls->pred_weights_required = V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(&controls->pps, &controls->slice_params); + if (controls->pred_weights_required) { + controls->pred_weights.chroma_log2_weight_denom = sl->pwt.chroma_log2_weight_denom; + controls->pred_weights.luma_log2_weight_denom = sl->pwt.luma_log2_weight_denom; + } + + count = sl->list_count > 0 ? sl->ref_count[0] : 0; + for (i = 0; i < count; i++) + fill_ref_list(&controls->slice_params.ref_pic_list0[i], &controls->decode_params, &sl->ref_list[0][i]); + if (count && controls->pred_weights_required) + fill_weight_factors(&controls->pred_weights.weight_factors[0], 0, sl); + + count = sl->list_count > 1 ? sl->ref_count[1] : 0; + for (i = 0; i < count; i++) + fill_ref_list(&controls->slice_params.ref_pic_list1[i], &controls->decode_params, &sl->ref_list[1][i]); + if (count && controls->pred_weights_required) + fill_weight_factors(&controls->pred_weights.weight_factors[1], 1, sl); + + controls->num_slices++; + return 0; +} + +static int v4l2_request_h264_end_frame(AVCodecContext *avctx) +{ + const H264Context *h = avctx->priv_data; + + return v4l2_request_h264_queue_decode(avctx, !FIELD_PICTURE(h) || !h->first_field); +} + +static int v4l2_request_h264_set_controls(AVCodecContext *avctx) +{ + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_ext_control control[] = { + { .id = V4L2_CID_STATELESS_H264_DECODE_MODE, }, + { .id = V4L2_CID_STATELESS_H264_START_CODE, }, + }; + + ctx->decode_mode = ff_v4l2_request_query_control_default_value(avctx, V4L2_CID_STATELESS_H264_DECODE_MODE); + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && + ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED) { + av_log(avctx, AV_LOG_ERROR, "%s: unsupported decode mode, %d\n", __func__, ctx->decode_mode); + return AVERROR(EINVAL); + } + + ctx->start_code = ff_v4l2_request_query_control_default_value(avctx, V4L2_CID_STATELESS_H264_START_CODE); + if (ctx->start_code != V4L2_STATELESS_H264_START_CODE_NONE && + ctx->start_code != V4L2_STATELESS_H264_START_CODE_ANNEX_B) { + av_log(avctx, AV_LOG_ERROR, "%s: unsupported start code, %d\n", __func__, ctx->start_code); + return AVERROR(EINVAL); + } + + control[0].value = ctx->decode_mode; + control[1].value = ctx->start_code; + + return ff_v4l2_request_set_controls(avctx, control, FF_ARRAY_ELEMS(control)); +} + +static int v4l2_request_h264_init(AVCodecContext *avctx) +{ + const H264Context *h = avctx->priv_data; + struct v4l2_ctrl_h264_sps sps; + int ret; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_H264_SPS, + .ptr = &sps, + .size = sizeof(sps), + }, + }; + + fill_sps(&sps, h); + + ret = ff_v4l2_request_init(avctx, V4L2_PIX_FMT_H264_SLICE, OUTPUT_BUFFER_SIZE, control, FF_ARRAY_ELEMS(control)); + if (ret) + return ret; + + return v4l2_request_h264_set_controls(avctx); +} + +const AVHWAccel ff_h264_v4l2request_hwaccel = { + .name = "h264_v4l2request", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_h264_start_frame, + .decode_slice = v4l2_request_h264_decode_slice, + .end_frame = v4l2_request_h264_end_frame, + .frame_priv_data_size = sizeof(V4L2RequestControlsH264), + .init = v4l2_request_h264_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextH264), + .frame_params = ff_v4l2_request_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 5b92afe60a..1420439044 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 115 -#define LIBAVCODEC_VERSION_MICRO 102 +#define LIBAVCODEC_VERSION_MINOR 116 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \