From 6b67aa32fc3b9cad3e2487875e058ba726e0513d Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sat, 6 Jul 2024 08:41:30 +0000 Subject: [PATCH 11/13] avcodec: Add V4L2 Request API av1 hwaccel Add a V4L2 Request API hwaccel for AV1. Support for AV1 is enabled when Linux kernel headers declare the control id V4L2_CID_STATELESS_AV1_FRAME, added in v6.5. Signed-off-by: Jonas Karlman --- configure | 3 + libavcodec/Makefile | 1 + libavcodec/av1dec.c | 10 + libavcodec/hwaccels.h | 1 + libavcodec/v4l2_request_av1.c | 636 ++++++++++++++++++++++++++++++++++ 5 files changed, 651 insertions(+) create mode 100644 libavcodec/v4l2_request_av1.c --- a/configure +++ b/configure @@ -3424,6 +3424,8 @@ av1_dxva2_hwaccel_deps="dxva2 DXVA_PicPa av1_dxva2_hwaccel_select="av1_decoder" av1_nvdec_hwaccel_deps="nvdec CUVIDAV1PICPARAMS" av1_nvdec_hwaccel_select="av1_decoder" +av1_v4l2request_hwaccel_deps="v4l2_request av1_v4l2_request" +av1_v4l2request_hwaccel_select="av1_decoder" av1_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferAV1_bit_depth_idx" av1_vaapi_hwaccel_select="av1_decoder" av1_vdpau_hwaccel_deps="vdpau VdpPictureInfoAV1" @@ -7673,6 +7675,7 @@ if enabled v4l2_m2m; then fi if enabled v4l2_request; then + check_cc av1_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_AV1_SEQUENCE" check_cc h264_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_H264_DECODE_MODE" check_cc hevc_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_HEVC_SPS" check_cc mpeg2_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_MPEG2_SEQUENCE" --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1049,6 +1049,7 @@ OBJS-$(CONFIG_AV1_D3D11VA_HWACCEL) OBJS-$(CONFIG_AV1_DXVA2_HWACCEL) += dxva2_av1.o OBJS-$(CONFIG_AV1_D3D12VA_HWACCEL) += dxva2_av1.o d3d12va_av1.o OBJS-$(CONFIG_AV1_NVDEC_HWACCEL) += nvdec_av1.o +OBJS-$(CONFIG_AV1_V4L2REQUEST_HWACCEL) += v4l2_request_av1.o OBJS-$(CONFIG_AV1_VAAPI_HWACCEL) += vaapi_av1.o OBJS-$(CONFIG_AV1_VDPAU_HWACCEL) += vdpau_av1.o OBJS-$(CONFIG_AV1_VIDEOTOOLBOX_HWACCEL) += videotoolbox_av1.o --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -543,6 +543,7 @@ static int get_pixel_format(AVCodecConte CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ CONFIG_AV1_D3D12VA_HWACCEL + \ CONFIG_AV1_NVDEC_HWACCEL + \ + CONFIG_AV1_V4L2REQUEST_HWACCEL + \ CONFIG_AV1_VAAPI_HWACCEL + \ CONFIG_AV1_VDPAU_HWACCEL + \ CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + \ @@ -579,6 +580,9 @@ static int get_pixel_format(AVCodecConte #if CONFIG_AV1_VULKAN_HWACCEL *fmtp++ = AV_PIX_FMT_VULKAN; #endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + *fmtp++ = AV_PIX_FMT_DRM_PRIME; +#endif break; case AV_PIX_FMT_YUV420P10: #if CONFIG_AV1_DXVA2_HWACCEL @@ -606,6 +610,9 @@ static int get_pixel_format(AVCodecConte #if CONFIG_AV1_VULKAN_HWACCEL *fmtp++ = AV_PIX_FMT_VULKAN; #endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + *fmtp++ = AV_PIX_FMT_DRM_PRIME; +#endif break; case AV_PIX_FMT_YUV420P12: #if CONFIG_AV1_VULKAN_HWACCEL @@ -1567,6 +1574,9 @@ const FFCodec ff_av1_decoder = { #if CONFIG_AV1_VULKAN_HWACCEL HWACCEL_VULKAN(av1), #endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(av1), +#endif NULL }, --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -25,6 +25,7 @@ extern const struct FFHWAccel ff_av1_d3d extern const struct FFHWAccel ff_av1_d3d12va_hwaccel; extern const struct FFHWAccel ff_av1_dxva2_hwaccel; extern const struct FFHWAccel ff_av1_nvdec_hwaccel; +extern const struct FFHWAccel ff_av1_v4l2request_hwaccel; extern const struct FFHWAccel ff_av1_vaapi_hwaccel; extern const struct FFHWAccel ff_av1_vdpau_hwaccel; extern const struct FFHWAccel ff_av1_videotoolbox_hwaccel; --- /dev/null +++ b/libavcodec/v4l2_request_av1.c @@ -0,0 +1,636 @@ +/* + * 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 "config.h" + +#include "libavutil/mem.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "av1dec.h" +#include "internal.h" +#include "v4l2_request.h" + +#define V4L2_AV1_CONTROLS_MAX 4 + +typedef struct V4L2RequestContextAV1 { + V4L2RequestContext base; + bool has_film_grain; +} V4L2RequestContextAV1; + +typedef struct V4L2RequestControlsAV1 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_av1_sequence sequence; + struct v4l2_ctrl_av1_frame frame; + struct v4l2_ctrl_av1_film_grain film_grain; + struct v4l2_ctrl_av1_tile_group_entry tile_group_entry; + struct v4l2_ctrl_av1_tile_group_entry *tile_group_entries; + unsigned int allocated_tile_group_entries; + unsigned int num_tile_group_entries; +} V4L2RequestControlsAV1; + +static int get_bit_depth_from_seq(const AV1RawSequenceHeader *seq) +{ + if (seq->seq_profile == AV_PROFILE_AV1_PROFESSIONAL && + seq->color_config.high_bitdepth) + return seq->color_config.twelve_bit ? 12 : 10; + else + return seq->color_config.high_bitdepth ? 10 : 8; +} + +static void fill_sequence(struct v4l2_ctrl_av1_sequence *ctrl, + const AV1DecContext *s) +{ + const AV1RawSequenceHeader *seq = s->raw_seq; + + *ctrl = (struct v4l2_ctrl_av1_sequence) { + .seq_profile = seq->seq_profile, + .order_hint_bits = seq->enable_order_hint ? + seq->order_hint_bits_minus_1 + 1 : 0, + .bit_depth = get_bit_depth_from_seq(seq), + .max_frame_width_minus_1 = seq->max_frame_width_minus_1, + .max_frame_height_minus_1 = seq->max_frame_height_minus_1, + }; + + if (seq->still_picture) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE; + + if (seq->use_128x128_superblock) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK; + + if (seq->enable_filter_intra) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA; + + if (seq->enable_intra_edge_filter) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER; + + if (seq->enable_interintra_compound) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND; + + if (seq->enable_masked_compound) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND; + + if (seq->enable_warped_motion) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION; + + if (seq->enable_dual_filter) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER; + + if (seq->enable_order_hint) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT; + + if (seq->enable_jnt_comp) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP; + + if (seq->enable_ref_frame_mvs) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS; + + if (seq->enable_superres) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES; + + if (seq->enable_cdef) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF; + + if (seq->enable_restoration) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION; + + if (seq->color_config.mono_chrome) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME; + + if (seq->color_config.color_range) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE; + + if (seq->color_config.subsampling_x) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X; + + if (seq->color_config.subsampling_y) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y; + + if (seq->film_grain_params_present) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT; + + if (seq->color_config.separate_uv_delta_q) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q; +} + +static void fill_frame(struct v4l2_ctrl_av1_frame *ctrl, const AV1DecContext *s) +{ + const AV1RawFrameHeader *frame_header = s->raw_frame_header; + const CodedBitstreamAV1Context *cbctx = s->cbc->priv_data; + uint8_t remap_lr_type[4] = { + V4L2_AV1_FRAME_RESTORE_NONE, + V4L2_AV1_FRAME_RESTORE_SWITCHABLE, + V4L2_AV1_FRAME_RESTORE_WIENER, + V4L2_AV1_FRAME_RESTORE_SGRPROJ, + }; + int i, j; + + *ctrl = (struct v4l2_ctrl_av1_frame) { + .tile_info = { + .context_update_tile_id = frame_header->context_update_tile_id, + .tile_cols = frame_header->tile_cols, + .tile_rows = frame_header->tile_rows, + .tile_size_bytes = frame_header->tile_cols_log2 > 0 || + frame_header->tile_rows_log2 > 0 ? + frame_header->tile_size_bytes_minus1 + 1 : 0, + }, + + .quantization = { + .base_q_idx = frame_header->base_q_idx, + .delta_q_y_dc = frame_header->delta_q_y_dc, + .delta_q_u_dc = frame_header->delta_q_u_dc, + .delta_q_u_ac = frame_header->delta_q_u_ac, + .delta_q_v_dc = frame_header->delta_q_v_dc, + .delta_q_v_ac = frame_header->delta_q_v_ac, + .qm_y = frame_header->qm_y, + .qm_u = frame_header->qm_u, + .qm_v = frame_header->qm_v, + .delta_q_res = frame_header->delta_q_res, + }, + + .loop_filter = { + .level[0] = frame_header->loop_filter_level[0], + .level[1] = frame_header->loop_filter_level[1], + .level[2] = frame_header->loop_filter_level[2], + .level[3] = frame_header->loop_filter_level[3], + .sharpness = frame_header->loop_filter_sharpness, + .mode_deltas[0] = frame_header->loop_filter_mode_deltas[0], + .mode_deltas[1] = frame_header->loop_filter_mode_deltas[1], + .delta_lf_res = frame_header->delta_lf_res, + }, + + .cdef = { + .damping_minus_3 = frame_header->cdef_damping_minus_3, + .bits = frame_header->cdef_bits, + }, + + .loop_restoration = { + .lr_unit_shift = frame_header->lr_unit_shift, + .lr_uv_shift = frame_header->lr_uv_shift, + }, + + .superres_denom = frame_header->use_superres ? + frame_header->coded_denom + AV1_SUPERRES_DENOM_MIN : + AV1_SUPERRES_NUM, + .skip_mode_frame[0] = frame_header->skip_mode_present ? + s->cur_frame.skip_mode_frame_idx[0] : 0, + .skip_mode_frame[1] = frame_header->skip_mode_present ? + s->cur_frame.skip_mode_frame_idx[1] : 0, + .primary_ref_frame = frame_header->primary_ref_frame, + .frame_type = frame_header->frame_type, + .order_hint = frame_header->order_hint, + .upscaled_width = cbctx->upscaled_width, + .interpolation_filter = frame_header->interpolation_filter, + .tx_mode = frame_header->tx_mode, + .frame_width_minus_1 = cbctx->frame_width - 1, + .frame_height_minus_1 = cbctx->frame_height - 1, + .render_width_minus_1 = cbctx->render_width - 1, + .render_height_minus_1 = cbctx->render_height - 1, + .current_frame_id = frame_header->current_frame_id, + .refresh_frame_flags = frame_header->refresh_frame_flags, + }; + + if (frame_header->segmentation_enabled) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_ENABLED; + + if (frame_header->segmentation_update_map) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP; + + if (frame_header->segmentation_temporal_update) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE; + + if (frame_header->segmentation_update_data) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA; + + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + for (j = 0; j < AV1_SEG_LVL_MAX; j++) { + if (frame_header->feature_enabled[i][j]) { + ctrl->segmentation.feature_enabled[i] |= V4L2_AV1_SEGMENT_FEATURE_ENABLED(j); + ctrl->segmentation.last_active_seg_id = i; + if (j >= AV1_SEG_LVL_REF_FRAME) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP; + } + ctrl->segmentation.feature_data[i][j] = frame_header->feature_value[i][j]; + } + } + + if (frame_header->uniform_tile_spacing_flag) + ctrl->tile_info.flags |= V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING; + + for (i = 0; i < frame_header->tile_cols; i++) { + ctrl->tile_info.mi_col_starts[i] = frame_header->tile_start_col_sb[i]; + ctrl->tile_info.width_in_sbs_minus_1[i] = frame_header->width_in_sbs_minus_1[i]; + } + ctrl->tile_info.mi_col_starts[i] = 2 * ((cbctx->frame_width + 7) >> 3); + + for (i = 0; i < frame_header->tile_rows; i++) { + ctrl->tile_info.mi_row_starts[i] = frame_header->tile_start_row_sb[i]; + ctrl->tile_info.height_in_sbs_minus_1[i] = frame_header->height_in_sbs_minus_1[i]; + } + ctrl->tile_info.mi_row_starts[i] = 2 * ((cbctx->frame_height + 7) >> 3); + + if (frame_header->diff_uv_delta) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA; + + if (frame_header->using_qmatrix) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX; + + if (frame_header->delta_q_present) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT; + + if (frame_header->loop_filter_delta_enabled) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED; + + if (frame_header->loop_filter_delta_update) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE; + + if (frame_header->delta_lf_present) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT; + + if (frame_header->delta_lf_multi) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI; + + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + ctrl->loop_filter.ref_deltas[i] = frame_header->loop_filter_ref_deltas[i]; + } + + for (i = 0; i < cbctx->num_planes; i++) { + ctrl->loop_restoration.frame_restoration_type[i] = + remap_lr_type[frame_header->lr_type[i]]; + if (frame_header->lr_type[i] != AV1_RESTORE_NONE) { + ctrl->loop_restoration.flags |= V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR; + if (i > 0) + ctrl->loop_restoration.flags |= V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR; + } + } + if (ctrl->loop_restoration.flags & V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR) { + ctrl->loop_restoration.loop_restoration_size[0] = + 1 << (6 + frame_header->lr_unit_shift); + ctrl->loop_restoration.loop_restoration_size[1] = + 1 << (6 + frame_header->lr_unit_shift - frame_header->lr_uv_shift); + ctrl->loop_restoration.loop_restoration_size[2] = + 1 << (6 + frame_header->lr_unit_shift - frame_header->lr_uv_shift); + } + + for (i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + ctrl->global_motion.type[i] = s->cur_frame.gm_type[i]; + for (j = 0; j < 6; ++j) { + ctrl->global_motion.params[i][j] = s->cur_frame.gm_params[i][j]; + if (s->cur_frame.gm_invalid[i]) + ctrl->global_motion.invalid |= V4L2_AV1_GLOBAL_MOTION_IS_INVALID(i); + } + + if (frame_header->is_global[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_GLOBAL; + + if (frame_header->is_rot_zoom[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_ROT_ZOOM; + + if (frame_header->is_translation[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_TRANSLATION; + } + + for (i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + AVFrame *ref = s->ref[i].f; + + ctrl->order_hints[i] = s->cur_frame.order_hints[i]; + if (ref) + ctrl->reference_frame_ts[i] = ff_v4l2_request_get_capture_timestamp(ref); + if (i < AV1_REFS_PER_FRAME) + ctrl->ref_frame_idx[i] = frame_header->ref_frame_idx[i]; + } + + for (i = 0; i < (1 << frame_header->cdef_bits); i++) { + ctrl->cdef.y_pri_strength[i] = frame_header->cdef_y_pri_strength[i]; + ctrl->cdef.y_sec_strength[i] = frame_header->cdef_y_sec_strength[i]; + ctrl->cdef.uv_pri_strength[i] = frame_header->cdef_uv_pri_strength[i]; + ctrl->cdef.uv_sec_strength[i] = frame_header->cdef_uv_sec_strength[i]; + } + + if (frame_header->show_frame) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SHOW_FRAME; + + if (frame_header->showable_frame) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME; + + if (frame_header->error_resilient_mode) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE; + + if (frame_header->disable_cdf_update) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE; + + if (frame_header->allow_screen_content_tools) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS; + + if (s->cur_frame.force_integer_mv) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV; + + if (frame_header->allow_intrabc) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC; + + if (frame_header->use_superres) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_USE_SUPERRES; + + if (frame_header->allow_high_precision_mv) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV; + + if (frame_header->is_motion_mode_switchable) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE; + + if (frame_header->use_ref_frame_mvs) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS; + + if (frame_header->disable_frame_end_update_cdf) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF; + + if (frame_header->allow_warped_motion) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION; + + if (frame_header->reference_select) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT; + + if (frame_header->reduced_tx_set) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET; + + if (frame_header->skip_mode_present) { + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED; // FIXME + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT; + } + + if (frame_header->frame_size_override_flag) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE; + + if (frame_header->buffer_removal_time_present_flag) { + ctrl->flags |= V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT; + for (i = 0; i < AV1_MAX_OPERATING_POINTS; i++) + ctrl->buffer_removal_time[i] = frame_header->buffer_removal_time[i]; + } + + if (frame_header->frame_refs_short_signaling) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING; +} + +static void fill_film_grain(struct v4l2_ctrl_av1_film_grain *ctrl, + const AV1DecContext *s) +{ + const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + int i; + + *ctrl = (struct v4l2_ctrl_av1_film_grain) { + .cr_mult = film_grain->cr_mult, + .grain_seed = film_grain->grain_seed, + .film_grain_params_ref_idx = film_grain->film_grain_params_ref_idx, + .num_y_points = film_grain->num_y_points, + .num_cb_points = film_grain->num_cb_points, + .num_cr_points = film_grain->num_cr_points, + .grain_scaling_minus_8 = film_grain->grain_scaling_minus_8, + .ar_coeff_lag = film_grain->ar_coeff_lag, + .ar_coeff_shift_minus_6 = film_grain->ar_coeff_shift_minus_6, + .grain_scale_shift = film_grain->grain_scale_shift, + .cb_mult = film_grain->cb_mult, + .cb_luma_mult = film_grain->cb_luma_mult, + .cr_luma_mult = film_grain->cr_luma_mult, + .cb_offset = film_grain->cb_offset, + .cr_offset = film_grain->cr_offset, + }; + + if (film_grain->apply_grain) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN; + + if (film_grain->update_grain) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_UPDATE_GRAIN; + + if (film_grain->chroma_scaling_from_luma) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA; + + if (film_grain->overlap_flag) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_OVERLAP; + + if (film_grain->clip_to_restricted_range) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_CLIP_TO_RESTRICTED_RANGE; + + if (!film_grain->apply_grain) + return; + + for (i = 0; i < film_grain->num_y_points; i++) { + ctrl->point_y_value[i] = film_grain->point_y_value[i]; + ctrl->point_y_scaling[i] = film_grain->point_y_scaling[i]; + } + + for (i = 0; i < film_grain->num_cb_points; i++) { + ctrl->point_cb_value[i] = film_grain->point_cb_value[i]; + ctrl->point_cb_scaling[i] = film_grain->point_cb_scaling[i]; + } + + for (i = 0; i < film_grain->num_cr_points; i++) { + ctrl->point_cr_value[i] = film_grain->point_cr_value[i]; + ctrl->point_cr_scaling[i] = film_grain->point_cr_scaling[i]; + } + + for (i = 0; i < 24; i++) { + ctrl->ar_coeffs_y_plus_128[i] = film_grain->ar_coeffs_y_plus_128[i]; + } + + for (i = 0; i < 25; i++) { + ctrl->ar_coeffs_cb_plus_128[i] = film_grain->ar_coeffs_cb_plus_128[i]; + ctrl->ar_coeffs_cr_plus_128[i] = film_grain->ar_coeffs_cr_plus_128[i]; + } +} + +static int v4l2_request_av1_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const AV1DecContext *s = avctx->priv_data; + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, s->cur_frame.f); + if (ret) + return ret; + + fill_sequence(&controls->sequence, s); + fill_frame(&controls->frame, s); + + if (ctx->has_film_grain) + fill_film_grain(&controls->film_grain, s); + + controls->tile_group_entries = &controls->tile_group_entry; + controls->allocated_tile_group_entries = 0; + controls->num_tile_group_entries = 0; + + return 0; +} + +static int v4l2_request_av1_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const AV1DecContext *s = avctx->priv_data; + const AV1RawFrameHeader *fh = s->raw_frame_header; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + + controls->num_tile_group_entries = fh->tile_cols * fh->tile_rows; + if (controls->num_tile_group_entries > V4L2_AV1_MAX_TILE_COUNT) + return AVERROR(EINVAL); + + if (controls->num_tile_group_entries > 1 && + controls->num_tile_group_entries > controls->allocated_tile_group_entries) { + struct v4l2_ctrl_av1_tile_group_entry *tile_group_entries; + + tile_group_entries = av_realloc_array(controls->allocated_tile_group_entries ? + controls->tile_group_entries : NULL, + controls->num_tile_group_entries, + sizeof(*controls->tile_group_entries)); + if (!tile_group_entries) + return AVERROR(ENOMEM); + + if (!controls->allocated_tile_group_entries) + memcpy(tile_group_entries, controls->tile_group_entries, + sizeof(*controls->tile_group_entries)); + + controls->tile_group_entries = tile_group_entries; + controls->allocated_tile_group_entries = controls->num_tile_group_entries; + } + + for (int i = 0; i < controls->num_tile_group_entries; i++) { + controls->tile_group_entries[i] = (struct v4l2_ctrl_av1_tile_group_entry) { + .tile_offset = controls->pic.output->bytesused + + s->tile_group_info[i].tile_offset, + .tile_size = s->tile_group_info[i].tile_size, + .tile_row = s->tile_group_info[i].tile_row, + .tile_col = s->tile_group_info[i].tile_column, + }; + } + + return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); +} + +static int v4l2_request_av1_end_frame(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + int count = 0; + + struct v4l2_ext_control control[V4L2_AV1_CONTROLS_MAX] = {}; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_SEQUENCE, + .ptr = &controls->sequence, + .size = sizeof(controls->sequence), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_FRAME, + .ptr = &controls->frame, + .size = sizeof(controls->frame), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY, + .ptr = controls->tile_group_entries, + .size = sizeof(*controls->tile_group_entries) * + FFMAX(controls->num_tile_group_entries, 1), + }; + + if (ctx->has_film_grain) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_FILM_GRAIN, + .ptr = &controls->film_grain, + .size = sizeof(controls->film_grain), + }; + } + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, control, count); +} + +static void v4l2_request_av1_free_frame_priv(AVRefStructOpaque hwctx, void *data) +{ + V4L2RequestControlsAV1 *controls = data; + + if (controls->allocated_tile_group_entries) + av_freep(&controls->tile_group_entries); +} + +static int v4l2_request_av1_post_frames_ctx(AVCodecContext *avctx) +{ + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_query_ext_ctrl film_grain = { + .id = V4L2_CID_STATELESS_AV1_FILM_GRAIN, + }; + + // TODO: check V4L2_CID_MPEG_VIDEO_AV1_PROFILE + // TODO: check V4L2_CID_MPEG_VIDEO_AV1_LEVEL + + if (!ff_v4l2_request_query_control(avctx, &film_grain)) + ctx->has_film_grain = true; + else + ctx->has_film_grain = false; + + return 0; +} + +static int v4l2_request_av1_init(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + struct v4l2_ctrl_av1_sequence sequence; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_AV1_SEQUENCE, + .ptr = &sequence, + .size = sizeof(sequence), + }, + }; + + fill_sequence(&sequence, s); + + return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control), + v4l2_request_av1_post_frames_ctx); +} + +static int v4l2_request_av1_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + const AV1DecContext *s = avctx->priv_data; + const AV1RawSequenceHeader *seq = s ? s->raw_seq : NULL; + uint8_t bit_depth = seq ? get_bit_depth_from_seq(seq) : 0; + + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_AV1_FRAME, bit_depth); +} + +const FFHWAccel ff_av1_v4l2request_hwaccel = { + .p.name = "av1_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_av1_start_frame, + .decode_slice = v4l2_request_av1_decode_slice, + .end_frame = v4l2_request_av1_end_frame, + .flush = ff_v4l2_request_flush, + .free_frame_priv = v4l2_request_av1_free_frame_priv, + .frame_priv_data_size = sizeof(V4L2RequestControlsAV1), + .init = v4l2_request_av1_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextAV1), + .frame_params = v4l2_request_av1_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +};