android_external_ffmpeg/libavcodec/v4l2_request_av1.c
Jonas Karlman eea5530b31 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 <jonas@kwiboo.se>
2025-12-20 23:50:13 +00:00

636 lines
24 KiB
C

/*
* 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,
};