From: ArazIusubov via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: ArazIusubov <code@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] avcodec/d3d12va_encode: D3D12 AV1 encoding support (PR #20552) Date: Fri, 19 Sep 2025 12:55:11 -0000 Message-ID: <175828651220.25.5790363289384854844@463a07221176> (raw) PR #20552 opened by ArazIusubov URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20552 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20552.patch Implement AV1 hardware encoding using Direct3D 12 Video API (D3D12VA). D3D12 AV1 headers are available starting with Windows SDK 10.0.26100, so the encoder is only built when these definitions are present. >From fc085ea2f3df7ed7725277a359890b9465fbb4cf Mon Sep 17 00:00:00 2001 From: Araz Iusubov <Primeadvice@gmail.com> Date: Fri, 19 Sep 2025 14:43:08 +0200 Subject: [PATCH] avcodec/d3d12va_encode: D3D12 AV1 encoding support Implement AV1 hardware encoding using Direct3D 12 Video API (D3D12VA). --- Changelog | 1 + configure | 3 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 1 + libavcodec/d3d12va_encode.c | 124 ++++- libavcodec/d3d12va_encode.h | 18 + libavcodec/d3d12va_encode_av1.c | 940 ++++++++++++++++++++++++++++++++ libavcodec/hw_base_encode_av1.c | 115 ++++ libavcodec/hw_base_encode_av1.h | 76 +++ 9 files changed, 1264 insertions(+), 16 deletions(-) create mode 100644 libavcodec/d3d12va_encode_av1.c create mode 100644 libavcodec/hw_base_encode_av1.c create mode 100644 libavcodec/hw_base_encode_av1.h diff --git a/Changelog b/Changelog index 88e1ce6659..c38b852be6 100644 --- a/Changelog +++ b/Changelog @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. version <next>: +- D3D12 AV1 encoder - ffprobe -codec option - EXIF Metadata Parsing - gfxcapture: Windows.Graphics.Capture based window/monitor capture diff --git a/configure b/configure index 8def62a5d9..8890f4c7e9 100755 --- a/configure +++ b/configure @@ -3433,6 +3433,8 @@ amrwb_mediacodec_decoder_select="amr_parser" av1_amf_encoder_deps="amf" av1_amf_decoder_deps="amf" av1_cuvid_decoder_deps="cuvid CUVIDAV1PICPARAMS" +av1_d3d12va_encoder_deps="d3d12va d3d12va_av1_headers" +av1_d3d12va_encoder_select="cbs_av1 d3d12va_encode" av1_mediacodec_decoder_deps="mediacodec" av1_mediacodec_encoder_deps="mediacodec" av1_mediacodec_encoder_select="extract_extradata_bsf" @@ -6941,6 +6943,7 @@ check_type "windows.h d3d12video.h" "ID3D12VideoDecoder" check_type "windows.h d3d12video.h" "ID3D12VideoEncoder" test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATURE_VIDEO_ENCODER_CODEC" && \ test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req" && enable d3d12_encoder_feature +test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_CODEC c = D3D12_VIDEO_ENCODER_CODEC_AV1; (void)c;" && enable d3d12va_av1_headers check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 check_type "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo -DSECURITY_WIN32 check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 diff --git a/libavcodec/Makefile b/libavcodec/Makefile index a321cda3a5..863a9f81ac 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -273,6 +273,8 @@ OBJS-$(CONFIG_AURA_DECODER) += cyuv.o OBJS-$(CONFIG_AURA2_DECODER) += aura.o OBJS-$(CONFIG_AV1_DECODER) += av1dec.o av1_parse.o OBJS-$(CONFIG_AV1_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_AV1_D3D12VA_ENCODER) += d3d12va_encode_av1.o av1_levels.o \ + hw_base_encode_av1.o OBJS-$(CONFIG_AV1_MEDIACODEC_DECODER) += mediacodecdec.o OBJS-$(CONFIG_AV1_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_AV1_NVENC_ENCODER) += nvenc_av1.o nvenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index f5ec2e01e8..02b15eee4a 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -845,6 +845,7 @@ extern const FFCodec ff_libaom_av1_decoder; /* hwaccel hooks only, so prefer external decoders */ extern const FFCodec ff_av1_decoder; extern const FFCodec ff_av1_cuvid_decoder; +extern const FFCodec ff_av1_d3d12va_encoder; extern const FFCodec ff_av1_mediacodec_decoder; extern const FFCodec ff_av1_mediacodec_encoder; extern const FFCodec ff_av1_nvenc_encoder; diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c index f34605ec41..1a0276ff79 100644 --- a/libavcodec/d3d12va_encode.c +++ b/libavcodec/d3d12va_encode.c @@ -29,6 +29,7 @@ #include "libavutil/hwcontext_d3d12va_internal.h" #include "libavutil/hwcontext_d3d12va.h" +#include "config_components.h" #include "avcodec.h" #include "d3d12va_encode.h" #include "encode.h" @@ -144,6 +145,12 @@ static int d3d12va_encode_create_metadata_buffers(AVCodecContext *avctx, { D3D12VAEncodeContext *ctx = avctx->priv_data; int width = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA); +#if CONFIG_AV1_D3D12VA_ENCODER + if (ctx->codec->d3d12_codec == D3D12_VIDEO_ENCODER_CODEC_AV1) { + width += sizeof(D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES) + + sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES); + } +#endif D3D12_HEAP_PROPERTIES encoded_meta_props = { .Type = D3D12_HEAP_TYPE_DEFAULT }, resolved_meta_props; D3D12_HEAP_TYPE resolved_heap_type = D3D12_HEAP_TYPE_READBACK; HRESULT hr; @@ -211,7 +218,7 @@ static int d3d12va_encode_issue(AVCodecContext *avctx, .RateControl = ctx->rc, .PictureTargetResolution = ctx->resolution, .SelectedLayoutMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, - .FrameSubregionsLayoutData = { 0 }, + .FrameSubregionsLayoutData = ctx->subregions_layout, .CodecGopSequence = ctx->gop, }, .pInputFrame = pic->input_surface->texture, @@ -655,7 +662,9 @@ static int d3d12va_encode_free(AVCodecContext *avctx, FFHWBaseEncodePicture *pic static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx, D3D12VAEncodePicture *pic, size_t *size) { - D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA* subregion_meta = NULL; uint8_t *data; HRESULT hr; int err; @@ -666,22 +675,37 @@ static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx, return err; } - meta = (D3D12_VIDEO_ENCODER_OUTPUT_METADATA *)data; +#if CONFIG_AV1_D3D12VA_ENCODER + if (ctx->codec->d3d12_codec == D3D12_VIDEO_ENCODER_CODEC_AV1){ - if (meta->EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) { - av_log(avctx, AV_LOG_ERROR, "Encode failed %"PRIu64"\n", meta->EncodeErrorFlags); - err = AVERROR(EINVAL); - 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; + + } else +#endif + { + meta = (D3D12_VIDEO_ENCODER_OUTPUT_METADATA *)data; + + if (meta->EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) { + av_log(avctx, AV_LOG_ERROR, "Encode failed %"PRIu64"\n", meta->EncodeErrorFlags); + err = AVERROR(EINVAL); + return err; + } + + if (meta->EncodedBitstreamWrittenBytesCount == 0) { + av_log(avctx, AV_LOG_ERROR, "No bytes were written to encoded bitstream\n"); + err = AVERROR(EINVAL); + return err; + } + + *size = meta->EncodedBitstreamWrittenBytesCount; } - if (meta->EncodedBitstreamWrittenBytesCount == 0) { - av_log(avctx, AV_LOG_ERROR, "No bytes were written to encoded bitstream\n"); - err = AVERROR(EINVAL); - return err; - } - - *size = meta->EncodedBitstreamWrittenBytesCount; - ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); return 0; @@ -694,12 +718,39 @@ static int d3d12va_encode_get_coded_data(AVCodecContext *avctx, uint8_t *ptr, *mapped_data; size_t total_size = 0; HRESULT hr; - +#if CONFIG_AV1_D3D12VA_ENCODER + D3D12VAEncodeContext *ctx = avctx->priv_data; + size_t av1_pic_hd_size = 0; + int tile_group_extra_size = 0; + int bit_len = 0; + char pic_hd_data[MAX_PARAM_BUFFER_SIZE]; +#endif err = d3d12va_encode_get_buffer_size(avctx, pic, &total_size); if (err < 0) goto end; +#if CONFIG_AV1_D3D12VA_ENCODER + // Update the picture header and calculate the picture header size + if (ctx->codec->write_picture_header) { + memset(pic_hd_data, 0, sizeof(pic_hd_data)); + err = ctx->codec->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); + } + + if (ctx->codec->write_tile_group) { + 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); +#else total_size += pic->header_size; +#endif av_log(avctx, AV_LOG_DEBUG, "Output buffer size %"SIZE_SPECIFIER"\n", total_size); hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&mapped_data); @@ -719,6 +770,24 @@ static int d3d12va_encode_get_coded_data(AVCodecContext *avctx, mapped_data += pic->aligned_header_size; total_size -= pic->header_size; +#if CONFIG_AV1_D3D12VA_ENCODER + if (ctx->codec->write_picture_header) { + 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); + } + + if (ctx->codec->write_tile_group) { + total_size -= tile_group_extra_size; + err = ctx->codec->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); + } else +#endif memcpy(ptr, mapped_data, total_size); ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); @@ -1138,6 +1207,9 @@ static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) union { D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 h264; D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_HEVC hevc; +#if CONFIG_AV1_D3D12VA_ENCODER + D3D12_VIDEO_ENCODER_CODEC_AV1_PICTURE_CONTROL_SUPPORT av1; +#endif } codec_support; support.NodeIndex = 0; @@ -1155,6 +1227,13 @@ static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) support.PictureSupport.pHEVCSupport = &codec_support.hevc; break; +#if CONFIG_AV1_D3D12VA_ENCODER + case D3D12_VIDEO_ENCODER_CODEC_AV1: + memset(&codec_support.av1, 0, sizeof(codec_support.av1)); + support.PictureSupport.DataSize = sizeof(codec_support.av1); + support.PictureSupport.pAV1Support = &codec_support.av1; + break; +#endif default: av_assert0(0); } @@ -1180,6 +1259,13 @@ static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) ref_l1 = support.PictureSupport.pHEVCSupport->MaxL1ReferencesForB; break; +#if CONFIG_AV1_D3D12VA_ENCODER + case D3D12_VIDEO_ENCODER_CODEC_AV1: + ref_l0 = support.PictureSupport.pAV1Support->MaxUniqueReferencesPerFrame; + // AV1 doesn't use traditional L1 references like H.264/HEVC + ref_l1 = 0; + break; +#endif default: av_assert0(0); } @@ -1530,6 +1616,12 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) if (err < 0) goto fail; + if (ctx->codec->set_tile) { + err = ctx->codec->set_tile(avctx); + if (err < 0) + goto fail; + } + err = d3d12va_encode_init_rate_control(avctx); if (err < 0) goto fail; diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h index 5bd1eedb7f..c136488bf4 100644 --- a/libavcodec/d3d12va_encode.h +++ b/libavcodec/d3d12va_encode.h @@ -264,6 +264,8 @@ typedef struct D3D12VAEncodeContext { D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop; D3D12_VIDEO_ENCODER_LEVEL_SETTING level; + + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregions_layout; } D3D12VAEncodeContext; typedef struct D3D12VAEncodeType { @@ -306,6 +308,11 @@ typedef struct D3D12VAEncodeType { */ int (*set_level)(AVCodecContext *avctx); + /** + * Set codec-specific tile setting. + */ + int (*set_tile)(AVCodecContext *avctx); + /** * The size of any private data structure associated with each * picture (can be zero if not required). @@ -327,6 +334,17 @@ typedef struct D3D12VAEncodeType { */ int (*write_sequence_header)(AVCodecContext *avctx, char *data, size_t *data_len); + + int (*write_picture_header)(AVCodecContext *avctx, + D3D12VAEncodePicture *pic, + char *data, size_t *data_len); + + int (*write_tile_group)(AVCodecContext *avctx, + uint8_t* tile_group, + uint32_t tile_group_size, + char *data, + size_t *data_len); + } D3D12VAEncodeType; int ff_d3d12va_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt); diff --git a/libavcodec/d3d12va_encode_av1.c b/libavcodec/d3d12va_encode_av1.c new file mode 100644 index 0000000000..07d6d054ab --- /dev/null +++ b/libavcodec/d3d12va_encode_av1.c @@ -0,0 +1,940 @@ +/* + * 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 "avcodec.h" +#include "cbs.h" +#include "cbs_av1.h" +#include "hw_base_encode_av1.h" +#include "av1_levels.h" +#include "codec_internal.h" +#include "d3d12video.h" +#include "d3d12va_encode.h" + +#ifndef D3D12_VIDEO_ENCODER_AV1_INVALID_DPB_RESOURCE_INDEX +#define D3D12_VIDEO_ENCODER_AV1_INVALID_DPB_RESOURCE_INDEX ( 0xff ) +#endif + +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. + FFHWBaseEncodeAV1 units; + FFHWBaseEncodeAV1Opts 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; + + 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; + 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; + + 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; + HRESULT hr; + int err; + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES *post_encode_values; + + // 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; + + 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; + + 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_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 = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, + .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 = ff_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; + 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 fixed_qp_key, fixed_qp_inter; + int err; + + 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(ctx->rc_quality, 0, 255); + + if (avctx->i_quant_factor > 0.0) + fixed_qp_key = av_clip((avctx->i_quant_factor * fixed_qp_inter + + avctx->i_quant_offset) + 0.5, 0, 255); + 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; + + 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) +{ + 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. + */ + hpic->slot = !href->slot; + 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; + } + 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, + + .write_picture_header = &d3d12va_encode_av1_write_picture_header, + + .write_tile_group = &d3d12va_encode_av1_write_tile_group, +}; + +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_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", +}; diff --git a/libavcodec/hw_base_encode_av1.c b/libavcodec/hw_base_encode_av1.c new file mode 100644 index 0000000000..c10a9c3358 --- /dev/null +++ b/libavcodec/hw_base_encode_av1.c @@ -0,0 +1,115 @@ +/* + * 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 <d3d12.h> +#include <d3d12video.h> + +#include "hw_base_encode_av1.h" +#include "av1_levels.h" +#include "libavutil/pixdesc.h" + +int ff_hw_base_encode_init_params_av1(FFHWBaseEncodeContext *base_ctx, + AVCodecContext *avctx, + FFHWBaseEncodeAV1 *common, + FFHWBaseEncodeAV1Opts *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; +} diff --git a/libavcodec/hw_base_encode_av1.h b/libavcodec/hw_base_encode_av1.h new file mode 100644 index 0000000000..67db4c4b0f --- /dev/null +++ b/libavcodec/hw_base_encode_av1.h @@ -0,0 +1,76 @@ +/* + * 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 + */ + +#ifndef AVCODEC_HW_BASE_ENCODE_AV1_H +#define AVCODEC_HW_BASE_ENCODE_AV1_H + +#include "hw_base_encode.h" +#include "cbs_av1.h" + +typedef struct FFHWBaseEncodeAV1 { + AV1RawOBU raw_sequence_header; + AV1RawOBU raw_temporal_delimiter; + AV1RawOBU raw_frame_header; + AV1RawOBU raw_metadata; + AV1RawOBU raw_tile_group; + + int metadata_hdr_cll_present; + int metadata_hdr_mdcv_present; + int metadata_scalability_present; + int metadata_itut_t35_present; + int metadata_timecode_present; +} FFHWBaseEncodeAV1; + + +typedef struct FFHWBaseEncodeAV1Opts { + int tier; // 0: Main tier, 1: High tier + int level; // AV1 level (2.0-7.3 map to 0-23) + + int tile_cols_log2; // log2(tile columns + int tile_rows_log2; // log2(tile rows) + int nb_tiles; // Tile number (1-64) + + 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; +} FFHWBaseEncodeAV1Opts; + + +int ff_hw_base_encode_init_params_av1(FFHWBaseEncodeContext *base_ctx, + AVCodecContext *avctx, + FFHWBaseEncodeAV1 *common, + FFHWBaseEncodeAV1Opts *opts); + +#endif /* AVCODEC_HW_BASE_ENCODE_AV1_H */ -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
reply other threads:[~2025-09-19 12:55 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=175828651220.25.5790363289384854844@463a07221176 \ --to=ffmpeg-devel@ffmpeg.org \ --cc=code@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git