/* * Direct3D 12 HW acceleration video encoder * * Copyright (c) 2024 Intel Corporation * * 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 "libavutil/opt.h" #include "libavutil/common.h" #include "libavutil/mem.h" #include "libavutil/pixdesc.h" #include "libavutil/hwcontext_d3d12va_internal.h" #include "config_components.h" #include "avcodec.h" #include "cbs.h" #include "cbs_av1.h" #include "av1_levels.h" #include "codec_internal.h" #include "d3d12va_encode.h" #include "encode.h" #include "hw_base_encode.h" #include #include #ifndef D3D12_VIDEO_ENCODER_AV1_INVALID_DPB_RESOURCE_INDEX #define D3D12_VIDEO_ENCODER_AV1_INVALID_DPB_RESOURCE_INDEX ( 0xff ) #endif typedef struct D3D12VAHWBaseEncodeAV1 { AV1RawOBU raw_sequence_header; AV1RawOBU raw_frame_header; AV1RawOBU raw_tile_group; } D3D12VAHWBaseEncodeAV1; typedef struct D3D12VAHWBaseEncodeAV1Opts { int tier; // 0: Main tier, 1: High tier int level; // AV1 level (2.0-7.3 map to 0-23) int enable_cdef; // Constrained Directional Enhancement Filter int enable_restoration; // loop restoration int enable_superres; // super-resolution int enable_ref_frame_mvs; int enable_jnt_comp; int enable_128x128_superblock; int enable_warped_motion; int enable_intra_edge_filter; int enable_interintra_compound; int enable_masked_compound; int enable_filter_intra; int enable_loop_filter; int enable_loop_filter_delta; int enable_dual_filter; int enable_palette; int enable_intra_block_copy; } D3D12VAHWBaseEncodeAV1Opts; typedef struct D3D12VAEncodeAV1Picture { uint8_t temporal_id; uint8_t spatial_id; uint8_t show_frame; uint8_t frame_type; uint16_t last_idr_frame; uint8_t slot; } D3D12VAEncodeAV1Picture; typedef struct D3D12VAEncodeAV1Context { D3D12VAEncodeContext common; // User options. int qp; int profile; int level; int tier; uint8_t q_idx_idr; uint8_t q_idx_p; // Writer structures. D3D12VAHWBaseEncodeAV1 units; D3D12VAHWBaseEncodeAV1Opts unit_opts; CodedBitstreamContext *cbc; CodedBitstreamFragment current_obu; D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAGS post_encode_values_flag; AVFifo *picture_header_list; } D3D12VAEncodeAV1Context; typedef struct D3D12VAEncodeAV1Level { uint8_t level; D3D12_VIDEO_ENCODER_AV1_LEVELS d3d12_level; } D3D12VAEncodeAV1Level; static const D3D12VAEncodeAV1Level av1_levels[] = { { 0, D3D12_VIDEO_ENCODER_AV1_LEVELS_2_0 }, { 1, D3D12_VIDEO_ENCODER_AV1_LEVELS_2_1 }, { 2, D3D12_VIDEO_ENCODER_AV1_LEVELS_2_2 }, { 3, D3D12_VIDEO_ENCODER_AV1_LEVELS_2_3 }, { 4, D3D12_VIDEO_ENCODER_AV1_LEVELS_3_0 }, { 5, D3D12_VIDEO_ENCODER_AV1_LEVELS_3_1 }, { 6, D3D12_VIDEO_ENCODER_AV1_LEVELS_3_2 }, { 7, D3D12_VIDEO_ENCODER_AV1_LEVELS_3_3 }, { 8, D3D12_VIDEO_ENCODER_AV1_LEVELS_4_0 }, { 9, D3D12_VIDEO_ENCODER_AV1_LEVELS_4_1 }, { 10, D3D12_VIDEO_ENCODER_AV1_LEVELS_4_2 }, { 11, D3D12_VIDEO_ENCODER_AV1_LEVELS_4_3 }, { 12, D3D12_VIDEO_ENCODER_AV1_LEVELS_5_0 }, { 13, D3D12_VIDEO_ENCODER_AV1_LEVELS_5_1 }, { 14, D3D12_VIDEO_ENCODER_AV1_LEVELS_5_2 }, { 15, D3D12_VIDEO_ENCODER_AV1_LEVELS_5_3 }, { 16, D3D12_VIDEO_ENCODER_AV1_LEVELS_6_0 }, { 17, D3D12_VIDEO_ENCODER_AV1_LEVELS_6_1 }, { 18, D3D12_VIDEO_ENCODER_AV1_LEVELS_6_2 }, { 19, D3D12_VIDEO_ENCODER_AV1_LEVELS_6_3 }, { 20, D3D12_VIDEO_ENCODER_AV1_LEVELS_7_0 }, { 21, D3D12_VIDEO_ENCODER_AV1_LEVELS_7_1 }, { 22, D3D12_VIDEO_ENCODER_AV1_LEVELS_7_2 }, { 23, D3D12_VIDEO_ENCODER_AV1_LEVELS_7_3 }, }; static const D3D12_VIDEO_ENCODER_AV1_PROFILE profile_main = D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN; static const D3D12_VIDEO_ENCODER_AV1_PROFILE profile_high = D3D12_VIDEO_ENCODER_AV1_PROFILE_HIGH; static const D3D12_VIDEO_ENCODER_AV1_PROFILE profile_professional = D3D12_VIDEO_ENCODER_AV1_PROFILE_PROFESSIONAL; #define D3D_PROFILE_DESC(name) \ { sizeof(D3D12_VIDEO_ENCODER_AV1_PROFILE), { .pAV1Profile = (D3D12_VIDEO_ENCODER_AV1_PROFILE *)&profile_ ## name } } static const D3D12VAEncodeProfile d3d12va_encode_av1_profiles[] = { { AV_PROFILE_AV1_MAIN, 8, 3, 1, 1, D3D_PROFILE_DESC(main) }, { AV_PROFILE_AV1_MAIN, 10, 3, 1, 1, D3D_PROFILE_DESC(main) }, { AV_PROFILE_AV1_HIGH, 10, 3, 1, 1, D3D_PROFILE_DESC(high) }, { AV_PROFILE_AV1_PROFESSIONAL, 8, 3, 1, 1, D3D_PROFILE_DESC(professional) }, { AV_PROFILE_AV1_PROFESSIONAL, 10, 3, 1, 1, D3D_PROFILE_DESC(professional) }, { AV_PROFILE_AV1_PROFESSIONAL, 12, 3, 1, 1, D3D_PROFILE_DESC(professional) }, { AV_PROFILE_UNKNOWN }, }; static int d3d12va_encode_av1_write_obu(AVCodecContext *avctx, char *data, size_t *data_len, CodedBitstreamFragment *obu) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; int err = 0; err = ff_cbs_write_fragment_data(priv->cbc, obu); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to write packed OBU data.\n"); return err; } memcpy(data, obu->data, obu->data_size); *data_len = (8 * obu->data_size) - obu->data_bit_padding; return 0; } static int d3d12va_encode_av1_add_obu(AVCodecContext* avctx, CodedBitstreamFragment* au, CodedBitstreamUnitType obu_type, void* obu_unit) { int err = 0; err = ff_cbs_insert_unit_content(au, -1, obu_type, obu_unit, NULL); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to add OBU unit: " "type = %d.\n", obu_type); return err; } return 0; } static int d3d12va_encode_av1_write_sequence_header(AVCodecContext *avctx, char *data, size_t *data_len) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; CodedBitstreamFragment *obu = &priv->current_obu; int err = 0; priv->units.raw_sequence_header.header.obu_type = AV1_OBU_SEQUENCE_HEADER; err = d3d12va_encode_av1_add_obu(avctx, obu, AV1_OBU_SEQUENCE_HEADER, &priv->units.raw_sequence_header); if (err < 0) goto fail; err = d3d12va_encode_av1_write_obu(avctx, data, data_len, obu); fail: ff_cbs_fragment_reset(obu); return err; } static int d3d12va_encode_av1_update_current_frame_picture_header(AVCodecContext *avctx, D3D12VAEncodePicture *pic, AV1RawOBU *frameheader_obu) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; AV1RawFrameHeader *fh = &frameheader_obu->obu.frame_header; uint8_t *data = NULL; HRESULT hr = S_OK; int err = 0; D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES *post_encode_values = NULL; // Update the frame header according to the picture post_encode_values hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void **)&data); if (FAILED(hr)) { err = AVERROR_UNKNOWN; return err; } post_encode_values = (D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES*) (data + sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA) + sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES)); if (priv->post_encode_values_flag & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION) { fh->base_q_idx = post_encode_values->Quantization.BaseQIndex; fh->delta_q_y_dc = post_encode_values->Quantization.YDCDeltaQ; fh->delta_q_u_dc = post_encode_values->Quantization.UDCDeltaQ; fh->delta_q_u_ac = post_encode_values->Quantization.UACDeltaQ; fh->delta_q_v_dc = post_encode_values->Quantization.VDCDeltaQ; fh->delta_q_v_ac = post_encode_values->Quantization.VACDeltaQ; fh->using_qmatrix = post_encode_values->Quantization.UsingQMatrix; fh->qm_y = post_encode_values->Quantization.QMY; fh->qm_u = post_encode_values->Quantization.QMU; fh->qm_v = post_encode_values->Quantization.QMV; } if (priv->post_encode_values_flag & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER) { fh->loop_filter_level[0] = post_encode_values->LoopFilter.LoopFilterLevel[0]; fh->loop_filter_level[1] = post_encode_values->LoopFilter.LoopFilterLevel[1]; fh->loop_filter_level[2] = post_encode_values->LoopFilter.LoopFilterLevelU; fh->loop_filter_level[3] = post_encode_values->LoopFilter.LoopFilterLevelV; fh->loop_filter_sharpness = post_encode_values->LoopFilter.LoopFilterSharpnessLevel; fh->loop_filter_delta_enabled = post_encode_values->LoopFilter.LoopFilterDeltaEnabled; if (fh->loop_filter_delta_enabled) { for (int i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { fh->loop_filter_ref_deltas[i] = post_encode_values->LoopFilter.RefDeltas[i]; fh->update_ref_delta[i] = post_encode_values->LoopFilter.RefDeltas[i]; } for (int i = 0; i < 2; i++) { fh->loop_filter_mode_deltas[i] = post_encode_values->LoopFilter.ModeDeltas[i]; fh->update_mode_delta[i] = post_encode_values->LoopFilter.ModeDeltas[i]; } } } if (priv->post_encode_values_flag & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CDEF_DATA) { fh->cdef_damping_minus_3 = post_encode_values->CDEF.CdefDampingMinus3; fh->cdef_bits = post_encode_values->CDEF.CdefBits; for (int i = 0; i < 8; i++) { fh->cdef_y_pri_strength[i] = post_encode_values->CDEF.CdefYPriStrength[i]; fh->cdef_y_sec_strength[i] = post_encode_values->CDEF.CdefYSecStrength[i]; fh->cdef_uv_pri_strength[i] = post_encode_values->CDEF.CdefUVPriStrength[i]; fh->cdef_uv_sec_strength[i] = post_encode_values->CDEF.CdefUVSecStrength[i]; } } if (priv->post_encode_values_flag & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION_DELTA) { fh->delta_q_present = post_encode_values->QuantizationDelta.DeltaQPresent; fh->delta_q_res = post_encode_values->QuantizationDelta.DeltaQRes; } if (priv->post_encode_values_flag & D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_REFERENCE_INDICES) { for (int i = 0; i < AV1_REFS_PER_FRAME; i++) { fh->ref_frame_idx[i] = post_encode_values->ReferenceIndices[i]; } } ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); return 0; } static int d3d12va_encode_av1_write_picture_header(AVCodecContext *avctx, D3D12VAEncodePicture *pic, char *data, size_t *data_len) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; CodedBitstreamFragment *obu = &priv->current_obu; AV1RawOBU *frameheader_obu = av_mallocz(sizeof(AV1RawOBU)); int err = 0; av_fifo_read(priv->picture_header_list, frameheader_obu, 1); err = d3d12va_encode_av1_update_current_frame_picture_header(avctx, pic,frameheader_obu); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to update current frame picture header: %d.\n", err); return err; } // Add the frame header OBU frameheader_obu->header.obu_has_size_field = 1; err = d3d12va_encode_av1_add_obu(avctx, obu, AV1_OBU_FRAME_HEADER, frameheader_obu); if (err < 0) goto fail; err = d3d12va_encode_av1_write_obu(avctx, data, data_len, obu); fail: ff_cbs_fragment_reset(obu); av_freep(&frameheader_obu); return err; } static int d3d12va_encode_av1_write_tile_group(AVCodecContext *avctx, uint8_t* tile_group, uint32_t tile_group_size, char *data, size_t *data_len) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; CodedBitstreamFragment *obu = &priv->current_obu; AV1RawOBU *tile_group_obu = &priv->units.raw_tile_group; AV1RawTileGroup *tg = &tile_group_obu->obu.tile_group; int err = 0; tg->tile_data.data = tile_group; tg->tile_data.data_ref = NULL; tg->tile_data.data_size = tile_group_size; tile_group_obu->header.obu_has_size_field = 1; tile_group_obu->header.obu_type = AV1_OBU_TILE_GROUP; err = d3d12va_encode_av1_add_obu(avctx, obu, AV1_OBU_TILE_GROUP, tile_group_obu); if (err < 0) goto fail; err = d3d12va_encode_av1_write_obu(avctx, data, data_len, obu); fail: ff_cbs_fragment_reset(obu); return err; } static int d3d12va_encode_av1_get_buffer_size(AVCodecContext *avctx, D3D12VAEncodePicture *pic, size_t *size) { D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA *subregion_meta = NULL; uint8_t *data = NULL; HRESULT hr = S_OK; int err = 0; hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void **)&data); if (FAILED(hr)) { err = AVERROR_UNKNOWN; return err; } subregion_meta = (D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA*)(data + sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA)); if (subregion_meta->bSize == 0) { av_log(avctx, AV_LOG_ERROR, "No subregion metadata found\n"); err = AVERROR(EINVAL); return err; } *size = subregion_meta->bSize; ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); return 0; } static int d3d12va_encode_av1_get_coded_data(AVCodecContext *avctx, D3D12VAEncodePicture *pic, AVPacket *pkt) { int err = 0; uint8_t *ptr = NULL; uint8_t *mapped_data = NULL; size_t total_size = 0; HRESULT hr = S_OK; size_t av1_pic_hd_size = 0; int tile_group_extra_size = 0; size_t bit_len = 0; D3D12VAEncodeContext *ctx = avctx->priv_data; char pic_hd_data[MAX_PARAM_BUFFER_SIZE] = { 0 }; err = d3d12va_encode_av1_get_buffer_size(avctx, pic, &total_size); if (err < 0) goto end; // Update the picture header and calculate the picture header size memset(pic_hd_data, 0, sizeof(pic_hd_data)); err = d3d12va_encode_av1_write_picture_header(avctx, pic, pic_hd_data, &av1_pic_hd_size); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to write picture header: %d.\n", err); return err; } av1_pic_hd_size /= 8; av_log(avctx, AV_LOG_DEBUG, "AV1 picture header size: %zu bytes.\n", av1_pic_hd_size); tile_group_extra_size = (av_log2(total_size) + 7) / 7 + 1; // 1 byte for obu header, rest for tile group LEB128 size av_log(avctx, AV_LOG_DEBUG, "Tile group extra size: %d bytes.\n", tile_group_extra_size); total_size += (pic->header_size + tile_group_extra_size + av1_pic_hd_size); av_log(avctx, AV_LOG_DEBUG, "Output buffer size %zu\n", total_size); hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&mapped_data); if (FAILED(hr)) { err = AVERROR_UNKNOWN; goto end; } err = ff_get_encode_buffer(avctx, pkt, total_size, 0); if (err < 0) goto end; ptr = pkt->data; memcpy(ptr, mapped_data, pic->header_size); ptr += pic->header_size; mapped_data += pic->aligned_header_size; total_size -= pic->header_size; memcpy(ptr, pic_hd_data, av1_pic_hd_size); ptr += av1_pic_hd_size; total_size -= av1_pic_hd_size; av_log(avctx, AV_LOG_DEBUG, "AV1 total_size after write picture header: %d.\n", total_size); total_size -= tile_group_extra_size; err = d3d12va_encode_av1_write_tile_group(avctx, mapped_data, total_size, ptr, &bit_len); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to write tile group: %d.\n", err); goto end; } assert((total_size + tile_group_extra_size) * 8 == bit_len); ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); end: av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = NULL; return err; } static int d3d12va_hw_base_encode_init_params_av1(FFHWBaseEncodeContext *base_ctx, AVCodecContext *avctx, D3D12VAHWBaseEncodeAV1 *common, D3D12VAHWBaseEncodeAV1Opts *opts) { AV1RawOBU *seqheader_obu = &common->raw_sequence_header; AV1RawSequenceHeader *seq = &seqheader_obu->obu.sequence_header; const AVPixFmtDescriptor *desc; seq->seq_profile = avctx->profile; if (!seq->seq_force_screen_content_tools) seq->seq_force_integer_mv = AV1_SELECT_INTEGER_MV; seq->seq_tier[0] = opts->tier; desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); seq->color_config = (AV1RawColorConfig){ .high_bitdepth = desc->comp[0].depth == 8 ? 0 : 1, .color_primaries = avctx->color_primaries, .transfer_characteristics = avctx->color_trc, .matrix_coefficients = avctx->colorspace, .color_description_present_flag = (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || avctx->color_trc != AVCOL_TRC_UNSPECIFIED || avctx->colorspace != AVCOL_SPC_UNSPECIFIED), .color_range = avctx->color_range == AVCOL_RANGE_JPEG, .subsampling_x = desc->log2_chroma_w, .subsampling_y = desc->log2_chroma_h, }; switch (avctx->chroma_sample_location) { case AVCHROMA_LOC_LEFT: seq->color_config.chroma_sample_position = AV1_CSP_VERTICAL; break; case AVCHROMA_LOC_TOPLEFT: seq->color_config.chroma_sample_position = AV1_CSP_COLOCATED; break; default: seq->color_config.chroma_sample_position = AV1_CSP_UNKNOWN; break; } if (avctx->level != AV_LEVEL_UNKNOWN) { seq->seq_level_idx[0] = avctx->level; } else { const AV1LevelDescriptor *level; float framerate; if (avctx->framerate.num > 0 && avctx->framerate.den > 0) framerate = avctx->framerate.num / avctx->framerate.den; else framerate = 0; //currently only supporting 1 tile level = ff_av1_guess_level(avctx->bit_rate, opts->tier, base_ctx->surface_width, base_ctx->surface_height, /*priv->tile_rows*/1 * 1/*priv->tile_cols*/, /*priv->tile_cols*/1, framerate); if (level) { av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name); seq->seq_level_idx[0] = level->level_idx; } else { av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to " "any normal level, using maximum parameters level by default.\n"); seq->seq_level_idx[0] = 31; seq->seq_tier[0] = 1; } } // Still picture mode seq->still_picture = (base_ctx->gop_size == 1); seq->reduced_still_picture_header = seq->still_picture; // Feature flags seq->enable_filter_intra = opts->enable_filter_intra; seq->enable_intra_edge_filter = opts->enable_intra_edge_filter; seq->enable_interintra_compound = opts->enable_interintra_compound; seq->enable_masked_compound = opts->enable_masked_compound; seq->enable_warped_motion = opts->enable_warped_motion; seq->enable_dual_filter = opts->enable_dual_filter; seq->enable_order_hint = !seq->still_picture; if (seq->enable_order_hint) { seq->order_hint_bits_minus_1 = 7; } seq->enable_jnt_comp = opts->enable_jnt_comp && seq->enable_order_hint; seq->enable_ref_frame_mvs = opts->enable_ref_frame_mvs && seq->enable_order_hint; seq->enable_superres = opts->enable_superres; seq->enable_cdef = opts->enable_cdef; seq->enable_restoration = opts->enable_restoration; return 0; } static int d3d12va_encode_av1_init_sequence_params(AVCodecContext *avctx) { FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx; AV1RawOBU *seqheader_obu = &priv->units.raw_sequence_header; AV1RawSequenceHeader *seq = &priv->units.raw_sequence_header.obu.sequence_header; D3D12_VIDEO_ENCODER_AV1_PROFILE profile = D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN; D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS level = { 0 }; HRESULT hr; int err; D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1 support = { .NodeIndex = 0, .Codec = D3D12_VIDEO_ENCODER_CODEC_AV1, .InputFormat = hwctx->format, .RateControl = ctx->rc, .IntraRefresh = ctx->intra_refresh.Mode, .SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, .ResolutionsListCount = 1, .pResolutionList = &ctx->resolution, .CodecGopSequence = ctx->gop, .MaxReferenceFramesInDPB = AV1_NUM_REF_FRAMES, .CodecConfiguration = ctx->codec_conf, .SuggestedProfile.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PROFILE), .SuggestedProfile.pAV1Profile = &profile, .SuggestedLevel.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS), .SuggestedLevel.pAV1LevelSetting = &level, .pResolutionDependentSupport = &ctx->res_limits, .SubregionFrameEncodingData.pTilesPartition_AV1 = ctx->subregions_layout.pTilesPartition_AV1, }; hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1, &support, sizeof(support)); if (FAILED(hr)) { av_log(avctx, AV_LOG_ERROR, "Failed to check encoder support(%lx).\n", (long)hr); return AVERROR(EINVAL); } if (!(support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK)) { av_log(avctx, AV_LOG_ERROR, "Driver does not support some request D3D12VA AV1 features. %#x\n", support.ValidationFlags); return AVERROR(EINVAL); } if (support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) { ctx->is_texture_array = 1; av_log(avctx, AV_LOG_DEBUG, "D3D12 video encode on this device uses texture array mode.\n"); } memset(seqheader_obu, 0, sizeof(*seqheader_obu)); seq->seq_profile = profile; seq->seq_level_idx[0] = level.Level; seq->seq_tier[0] = level.Tier; seq->max_frame_width_minus_1 = ctx->resolution.Width - 1; seq->max_frame_height_minus_1 = ctx->resolution.Height - 1; seq->frame_width_bits_minus_1 = av_log2(ctx->resolution.Width); seq->frame_height_bits_minus_1 = av_log2(ctx->resolution.Height); seqheader_obu->header.obu_type = AV1_OBU_SEQUENCE_HEADER; err = d3d12va_hw_base_encode_init_params_av1(base_ctx, avctx, &priv->units, &priv->unit_opts); if (err < 0) return err; if (avctx->level == AV_LEVEL_UNKNOWN) avctx->level = level.Level; return 0; } static int d3d12va_encode_av1_get_encoder_caps(AVCodecContext *avctx) { HRESULT hr = S_OK; FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION *config; D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT av1_caps; D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codec_caps = { .NodeIndex = 0, .Codec = D3D12_VIDEO_ENCODER_CODEC_AV1, .Profile = ctx->profile->d3d12_profile, .CodecSupportLimits.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION_SUPPORT), }; codec_caps.CodecSupportLimits.pAV1Support = &av1_caps; hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT, &codec_caps, sizeof(codec_caps)); if (!(SUCCEEDED(hr) && codec_caps.IsSupported)) return AVERROR(EINVAL); ctx->codec_conf.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_CODEC_CONFIGURATION); ctx->codec_conf.pAV1Config = av_mallocz(ctx->codec_conf.DataSize); if (!ctx->codec_conf.pAV1Config) return AVERROR(ENOMEM); priv->post_encode_values_flag = av1_caps.PostEncodeValuesFlags; config = ctx->codec_conf.pAV1Config; config->FeatureFlags = D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE; if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK) { config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_128x128_SUPERBLOCK; priv->unit_opts.enable_128x128_superblock = 1; } base_ctx->surface_width = FFALIGN(avctx->width, priv->unit_opts.enable_128x128_superblock ? 128 : 64); base_ctx->surface_height = FFALIGN(avctx->height, priv->unit_opts.enable_128x128_superblock ? 128 : 64); if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER) { config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER; priv->unit_opts.enable_loop_filter = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING) { config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING; priv->unit_opts.enable_palette = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY) { config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY; priv->unit_opts.enable_intra_block_copy = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS) { // Loop filter deltas config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_FILTER_DELTAS; priv->unit_opts.enable_loop_filter_delta = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING) { // CDEF (Constrained Directional Enhancement Filter) config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING; priv->unit_opts.enable_cdef = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER) { // Dual filter config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_DUAL_FILTER; priv->unit_opts.enable_dual_filter = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP) { // Joint compound prediction config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_JNT_COMP; priv->unit_opts.enable_jnt_comp = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS) { // Frame reference motion vectors config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_FRAME_REFERENCE_MOTION_VECTORS; priv->unit_opts.enable_ref_frame_mvs = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION) { // Super-resolution config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_SUPER_RESOLUTION; priv->unit_opts.enable_superres = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION) { // Warped motion config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_WARPED_MOTION; priv->unit_opts.enable_warped_motion = 1; } if (av1_caps.SupportedFeatureFlags & D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND) { // Inter-intra compound prediction config->FeatureFlags |= D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTERINTRA_COMPOUND; priv->unit_opts.enable_interintra_compound = 1; } return 0; } static int d3d12va_encode_av1_configure(AVCodecContext *avctx) { FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; int err = 0; int fixed_qp_key, fixed_qp_inter; err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_AV1, avctx); if (err < 0) return err; if (ctx->rc.Mode == D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP) { D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP *cqp_ctl; fixed_qp_inter = av_clip_uintp2(ctx->rc_quality, 8); if (avctx->i_quant_factor > 0.0) fixed_qp_key = av_clip_uintp2((avctx->i_quant_factor * fixed_qp_inter + avctx->i_quant_offset) + 0.5, 8); else fixed_qp_key = fixed_qp_inter; av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " "%d / %d for Key / Inter frames.\n", fixed_qp_key, fixed_qp_inter); ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP); cqp_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); if (!cqp_ctl) return AVERROR(ENOMEM); cqp_ctl->ConstantQP_FullIntracodedFrame = fixed_qp_key; cqp_ctl->ConstantQP_InterPredictedFrame_PrevRefOnly = fixed_qp_inter; cqp_ctl->ConstantQP_InterPredictedFrame_BiDirectionalRef = fixed_qp_inter; ctx->rc.ConfigParams.pConfiguration_CQP = cqp_ctl; priv->q_idx_idr = fixed_qp_key; priv->q_idx_p = fixed_qp_inter; } // GOP configuration for AV1 ctx->gop.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_SEQUENCE_STRUCTURE); ctx->gop.pAV1SequenceStructure = av_mallocz(ctx->gop.DataSize); if (!ctx->gop.pAV1SequenceStructure) return AVERROR(ENOMEM); ctx->gop.pAV1SequenceStructure->IntraDistance = base_ctx->gop_size; ctx->gop.pAV1SequenceStructure->InterFramePeriod = base_ctx->b_per_p + 1; return 0; } static int d3d12va_encode_av1_set_level(AVCodecContext *avctx) { D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; int i = 0; ctx->level.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS); ctx->level.pAV1LevelSetting = av_mallocz(ctx->level.DataSize); if (!ctx->level.pAV1LevelSetting) return AVERROR(ENOMEM); if (avctx->level != AV_LEVEL_UNKNOWN) { for (i = 0; i < FF_ARRAY_ELEMS(av1_levels); i++) { if (avctx->level == av1_levels[i].level) { ctx->level.pAV1LevelSetting->Level = av1_levels[i].d3d12_level; break; } } if (i == FF_ARRAY_ELEMS(av1_levels) ) { av_log(avctx, AV_LOG_ERROR, "Invalid AV1 level %d.\n", avctx->level); return AVERROR(EINVAL); } } else { ctx->level.pAV1LevelSetting->Level = D3D12_VIDEO_ENCODER_AV1_LEVELS_5_2; avctx->level = D3D12_VIDEO_ENCODER_AV1_LEVELS_5_2; av_log(avctx, AV_LOG_DEBUG, "Using default AV1 level 5.2\n"); } if (priv->tier == 1 || avctx->bit_rate > 30000000) { ctx->level.pAV1LevelSetting->Tier = D3D12_VIDEO_ENCODER_AV1_TIER_HIGH; av_log(avctx, AV_LOG_DEBUG, "Using AV1 High tier\n"); } else { ctx->level.pAV1LevelSetting->Tier = D3D12_VIDEO_ENCODER_AV1_TIER_MAIN; av_log(avctx, AV_LOG_DEBUG, "Using AV1 Main tier\n"); } if (priv->tier >= 0) { ctx->level.pAV1LevelSetting->Tier = priv->tier == 0 ? D3D12_VIDEO_ENCODER_AV1_TIER_MAIN : D3D12_VIDEO_ENCODER_AV1_TIER_HIGH; } av_log(avctx, AV_LOG_DEBUG, "AV1 level set to %d, tier: %s\n", ctx->level.pAV1LevelSetting->Level, ctx->level.pAV1LevelSetting->Tier == D3D12_VIDEO_ENCODER_AV1_TIER_MAIN ? "Main" : "High"); return 0; } static int d3d12va_encode_av1_set_tile(AVCodecContext *avctx) { D3D12VAEncodeContext *ctx = avctx->priv_data; ctx->subregions_layout.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES); D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES *tiles_layout = av_mallocz(ctx->subregions_layout.DataSize); ctx->subregions_layout.pTilesPartition_AV1 = tiles_layout; // Currently only support 1 tile tiles_layout->RowCount = 1; tiles_layout->ColCount = 1; return 0; } static void d3d12va_encode_av1_free_picture_params(D3D12VAEncodePicture *pic) { if (!pic->pic_ctl.pAV1PicData) return; av_freep(&pic->pic_ctl.pAV1PicData); } static int d3d12va_encode_av1_init_picture_params(AVCodecContext *avctx, FFHWBaseEncodePicture *pic) { FFHWBaseEncodeContext *base_ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodePicture *d3d12va_pic = pic->priv; D3D12VAEncodeAV1Picture *hpic = pic->codec_priv; CodedBitstreamAV1Context *cbctx = priv->cbc->priv_data; AV1RawOBU *frameheader_obu = &priv->units.raw_frame_header; AV1RawFrameHeader *fh = &frameheader_obu->obu.frame_header; FFHWBaseEncodePicture *ref; D3D12VAEncodeAV1Picture *href; int i; static const int8_t default_loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME] = { 1, 0, 0, 0, -1, 0, -1, -1 }; memset(frameheader_obu, 0, sizeof(*frameheader_obu)); frameheader_obu->header.obu_type = AV1_OBU_FRAME_HEADER; d3d12va_pic->pic_ctl.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA); d3d12va_pic->pic_ctl.pAV1PicData = av_mallocz(d3d12va_pic->pic_ctl.DataSize); if (!d3d12va_pic->pic_ctl.pAV1PicData) return AVERROR(ENOMEM); // Initialize frame type and reference frame management switch(pic->type) { case FF_HW_PICTURE_TYPE_IDR: fh->frame_type = AV1_FRAME_KEY; fh->refresh_frame_flags = 0xFF; fh->base_q_idx = priv->q_idx_idr; hpic->slot = 0; hpic->last_idr_frame = pic->display_order; fh->tx_mode = AV1_TX_MODE_LARGEST; break; case FF_HW_PICTURE_TYPE_P: fh->frame_type = AV1_FRAME_INTER; fh->base_q_idx = priv->q_idx_p; fh->tx_mode = AV1_TX_MODE_SELECT; ref = pic->refs[0][pic->nb_refs[0] - 1]; href = ref->codec_priv; /** * The encoder uses a simple alternating reference frame strategy: * - For P-frames, it uses the last reconstructed frame as a reference. * - To simplify the reference model of the encoder, the encoder alternates between * two reference frame slots (typically slot 0 and slot 1) for storing reconstructed * images and providing prediction references for the next frame. */ if (base_ctx->ref_l0 > 1) { hpic->slot = !href->slot; } else { hpic->slot = 0; } hpic->last_idr_frame = href->last_idr_frame; fh->refresh_frame_flags = 1 << hpic->slot; // Set the nearest frame in L0 as all reference frame. for (i = 0; i < AV1_REFS_PER_FRAME; i++) fh->ref_frame_idx[i] = href->slot; fh->primary_ref_frame = href->slot; fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; // Set the 2nd nearest frame in L0 as Golden frame. if (pic->nb_refs[0] > 1) { ref = pic->refs[0][pic->nb_refs[0] - 2]; href = ref->codec_priv; // Reference frame index 3 is the GOLDEN_FRAME fh->ref_frame_idx[3] = href->slot; fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; } else if (base_ctx->ref_l0 == 1) { fh->ref_order_hint[!href->slot] = cbctx->ref[!href->slot].order_hint; } break; case FF_HW_PICTURE_TYPE_B: av_log(avctx, AV_LOG_ERROR, "D3D12 AV1 video encode on this device requires B-frame support, " "but it's not implemented.\n"); return AVERROR_PATCHWELCOME; default: av_log(avctx, AV_LOG_ERROR, "Unsupported picture type %d.\n", pic->type); } cbctx->seen_frame_header = 0; fh->show_frame = pic->display_order <= pic->encode_order; fh->showable_frame = fh->frame_type != AV1_FRAME_KEY; fh->order_hint = pic->display_order - hpic->last_idr_frame; fh->frame_width_minus_1 = ctx->resolution.Width - 1; fh->frame_height_minus_1 = ctx->resolution.Height - 1; fh->render_width_minus_1 = fh->frame_width_minus_1; fh->render_height_minus_1 = fh->frame_height_minus_1; fh->is_filter_switchable = 1; fh->interpolation_filter = AV1_INTERPOLATION_FILTER_SWITCHABLE; fh->uniform_tile_spacing_flag = 1; fh->width_in_sbs_minus_1[0] = (ctx->resolution.Width + 63 >> 6) -1; // 64x64 superblock size fh->height_in_sbs_minus_1[0] = (ctx->resolution.Height + 63 >> 6) -1; // 64x64 superblock size memcpy(fh->loop_filter_ref_deltas, default_loop_filter_ref_deltas, AV1_TOTAL_REFS_PER_FRAME * sizeof(int8_t)); if (fh->frame_type == AV1_FRAME_KEY && fh->show_frame) fh->error_resilient_mode = 1; if (fh->frame_type == AV1_FRAME_KEY || fh->error_resilient_mode) fh->primary_ref_frame = AV1_PRIMARY_REF_NONE; d3d12va_pic->pic_ctl.pAV1PicData->FrameType = fh->frame_type; d3d12va_pic->pic_ctl.pAV1PicData->TxMode = fh->tx_mode; d3d12va_pic->pic_ctl.pAV1PicData->RefreshFrameFlags = fh->refresh_frame_flags; d3d12va_pic->pic_ctl.pAV1PicData->TemporalLayerIndexPlus1 = hpic->temporal_id + 1; d3d12va_pic->pic_ctl.pAV1PicData->SpatialLayerIndexPlus1 = hpic->spatial_id + 1; d3d12va_pic->pic_ctl.pAV1PicData->PictureIndex = pic->display_order; d3d12va_pic->pic_ctl.pAV1PicData->InterpolationFilter = D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_SWITCHABLE; d3d12va_pic->pic_ctl.pAV1PicData->PrimaryRefFrame = fh->primary_ref_frame; if (fh->error_resilient_mode) d3d12va_pic->pic_ctl.pAV1PicData->Flags |= D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_FLAG_ENABLE_ERROR_RESILIENT_MODE; if (pic->type == FF_HW_PICTURE_TYPE_IDR) { for (int i = 0; i < AV1_NUM_REF_FRAMES; i++) { d3d12va_pic->pic_ctl.pAV1PicData->ReferenceFramesReconPictureDescriptors[i].ReconstructedPictureResourceIndex = D3D12_VIDEO_ENCODER_AV1_INVALID_DPB_RESOURCE_INDEX; } } else if (pic->type == FF_HW_PICTURE_TYPE_P) { for (i = 0; i < pic->nb_refs[0]; i++) { FFHWBaseEncodePicture *ref_pic = pic->refs[0][i]; d3d12va_pic->pic_ctl.pAV1PicData->ReferenceFramesReconPictureDescriptors[i].ReconstructedPictureResourceIndex = ((D3D12VAEncodeAV1Picture*)ref_pic->codec_priv)->slot; } } // Set reference frame management memset(d3d12va_pic->pic_ctl.pAV1PicData->ReferenceIndices, 0, sizeof(UINT) * AV1_REFS_PER_FRAME); if (pic->type == FF_HW_PICTURE_TYPE_P) { for (i = 0; i < AV1_REFS_PER_FRAME; i++) d3d12va_pic->pic_ctl.pAV1PicData->ReferenceIndices[i] = fh->ref_frame_idx[i]; } int ret = av_fifo_write(priv->picture_header_list, &priv->units.raw_frame_header, 1); return 0; } static const D3D12VAEncodeType d3d12va_encode_type_av1 = { .profiles = d3d12va_encode_av1_profiles, .d3d12_codec = D3D12_VIDEO_ENCODER_CODEC_AV1, .flags = FF_HW_FLAG_B_PICTURES | FF_HW_FLAG_B_PICTURE_REFERENCES | FF_HW_FLAG_NON_IDR_KEY_PICTURES, .default_quality = 25, .get_encoder_caps = &d3d12va_encode_av1_get_encoder_caps, .configure = &d3d12va_encode_av1_configure, .set_level = &d3d12va_encode_av1_set_level, .set_tile = &d3d12va_encode_av1_set_tile, .picture_priv_data_size = sizeof(D3D12VAEncodeAV1Picture), .init_sequence_params = &d3d12va_encode_av1_init_sequence_params, .init_picture_params = &d3d12va_encode_av1_init_picture_params, .free_picture_params = &d3d12va_encode_av1_free_picture_params, .write_sequence_header = &d3d12va_encode_av1_write_sequence_header, #ifdef CONFIG_AV1_D3D12VA_ENCODER .get_coded_data = &d3d12va_encode_av1_get_coded_data, #endif }; static int d3d12va_encode_av1_init(AVCodecContext *avctx) { D3D12VAEncodeContext *ctx = avctx->priv_data; D3D12VAEncodeAV1Context *priv = avctx->priv_data; ctx->codec = &d3d12va_encode_type_av1; if (avctx->profile == AV_PROFILE_UNKNOWN) avctx->profile = priv->profile; if (avctx->level == AV_LEVEL_UNKNOWN) avctx->level = priv->level; if (avctx->level != AV_LEVEL_UNKNOWN && avctx->level & ~0xff) { av_log(avctx, AV_LOG_ERROR, "Invalid level %d: must fit " "in 8-bit unsigned integer.\n", avctx->level); return AVERROR(EINVAL); } if (priv->qp > 0) ctx->explicit_qp = priv->qp; priv->picture_header_list = av_fifo_alloc2(2, sizeof(AV1RawOBU), AV_FIFO_FLAG_AUTO_GROW); return ff_d3d12va_encode_init(avctx); } static int d3d12va_encode_av1_close(AVCodecContext *avctx) { D3D12VAEncodeAV1Context *priv = avctx->priv_data; ff_cbs_fragment_free(&priv->current_obu); ff_cbs_close(&priv->cbc); av_freep(&priv->common.codec_conf.pAV1Config); av_freep(&priv->common.gop.pAV1SequenceStructure); av_freep(&priv->common.level.pAV1LevelSetting); av_freep(&priv->common.subregions_layout.pTilesPartition_AV1); av_fifo_freep2(&priv->picture_header_list); return ff_d3d12va_encode_close(avctx); } #define OFFSET(x) offsetof(D3D12VAEncodeAV1Context, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) static const AVOption d3d12va_encode_av1_options[] = { HW_BASE_ENCODE_COMMON_OPTIONS, D3D12VA_ENCODE_COMMON_OPTIONS, D3D12VA_ENCODE_RC_OPTIONS, { "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, FLAGS }, { "profile", "Set profile (general_profile_idc)", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" }, #define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ { .i64 = value }, 0, 0, FLAGS, "profile" { PROFILE("main", AV_PROFILE_AV1_MAIN) }, { PROFILE("high", AV_PROFILE_AV1_HIGH) }, { PROFILE("professional", AV_PROFILE_AV1_PROFESSIONAL) }, #undef PROFILE { "tier", "Set tier (general_tier_flag)", OFFSET(unit_opts.tier), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "tier" }, { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "tier" }, { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "tier" }, { "level", "Set level (general_level_idc)", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AV_LEVEL_UNKNOWN }, AV_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, #define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ { .i64 = value }, 0, 0, FLAGS, "level" { LEVEL("2.0", 0) }, { LEVEL("2.1", 1) }, { LEVEL("2.2", 2) }, { LEVEL("2.3", 3) }, { LEVEL("3.0", 4) }, { LEVEL("3.1", 5) }, { LEVEL("3.2", 6) }, { LEVEL("3.3", 7) }, { LEVEL("4.0", 8) }, { LEVEL("4.1", 9) }, { LEVEL("4.2", 10) }, { LEVEL("4.3", 11) }, { LEVEL("5.0", 12) }, { LEVEL("5.1", 13) }, { LEVEL("5.2", 14) }, { LEVEL("5.3", 15) }, { LEVEL("6.0", 16) }, { LEVEL("6.1", 17) }, { LEVEL("6.2", 18) }, { LEVEL("6.3", 19) }, { LEVEL("7.0", 20) }, { LEVEL("7.1", 21) }, { LEVEL("7.2", 22) }, { LEVEL("7.3", 23) }, #undef LEVEL { NULL }, }; static const FFCodecDefault d3d12va_encode_av1_defaults[] = { { "b", "0" }, { "bf", "0" }, { "g", "120" }, { "i_qfactor", "1" }, { "i_qoffset", "0" }, { "b_qfactor", "1" }, { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, { "refs", "0" }, { NULL }, }; static const AVClass d3d12va_encode_av1_class = { .class_name = "av1_d3d12va", .item_name = av_default_item_name, .option = d3d12va_encode_av1_options, .version = LIBAVUTIL_VERSION_INT, }; const FFCodec ff_av1_d3d12va_encoder = { .p.name = "av1_d3d12va", CODEC_LONG_NAME("D3D12VA av1 encoder"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_AV1, .priv_data_size = sizeof(D3D12VAEncodeAV1Context), .init = &d3d12va_encode_av1_init, FF_CODEC_RECEIVE_PACKET_CB(&ff_d3d12va_encode_receive_packet), .close = &d3d12va_encode_av1_close, .p.priv_class = &d3d12va_encode_av1_class, .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .defaults = d3d12va_encode_av1_defaults, CODEC_PIXFMTS(AV_PIX_FMT_D3D12), .hw_configs = ff_d3d12va_encode_hw_configs, .p.wrapper_name = "d3d12va", };