Message ID | 20240314081456.379-11-tong1.wu@intel.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel,v7,01/12] avcodec/vaapi_encode: move pic->input_surface initialization to encode_alloc | expand |
Context | Check | Description |
---|---|---|
yinshiyou/make_loongarch64 | success | Make finished |
yinshiyou/make_fate_loongarch64 | success | Make fate finished |
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
Kindly ping. Is there any more comment on v7? >-----Original Message----- >From: Wu, Tong1 <tong1.wu@intel.com> >Sent: Thursday, March 14, 2024 4:15 PM >To: ffmpeg-devel@ffmpeg.org >Cc: Wu, Tong1 <tong1.wu@intel.com> >Subject: [FFmpeg-devel][PATCH v7 11/12] avcodec: add D3D12VA hardware >HEVC encoder > >From: Tong Wu <tong1.wu@intel.com> > >This implementation is based on D3D12 Video Encoding Spec: >https://microsoft.github.io/DirectX-Specs/d3d/D3D12VideoEncoding.html > >Sample command line for transcoding: >ffmpeg.exe -hwaccel d3d12va -hwaccel_output_format d3d12 -i input.mp4 >-c:v hevc_d3d12va output.mp4 > >Signed-off-by: Tong Wu <tong1.wu@intel.com> >--- > configure | 6 + > libavcodec/Makefile | 4 +- > libavcodec/allcodecs.c | 1 + > libavcodec/d3d12va_encode.c | 1550 ++++++++++++++++++++++++++++++ > libavcodec/d3d12va_encode.h | 321 +++++++ > libavcodec/d3d12va_encode_hevc.c | 957 ++++++++++++++++++ > 6 files changed, 2838 insertions(+), 1 deletion(-) > create mode 100644 libavcodec/d3d12va_encode.c > create mode 100644 libavcodec/d3d12va_encode.h > create mode 100644 libavcodec/d3d12va_encode_hevc.c > >diff --git a/configure b/configure >index c34bdd13f5..53076fbf22 100755 >--- a/configure >+++ b/configure >@@ -2570,6 +2570,7 @@ CONFIG_EXTRA=" > tpeldsp > vaapi_1 > vaapi_encode >+ d3d12va_encode > vc1dsp > videodsp > vp3dsp >@@ -3214,6 +3215,7 @@ wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" > wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" > > # hardware-accelerated codecs >+d3d12va_encode_deps="d3d12va ID3D12VideoEncoder >d3d12_encoder_feature" > mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" > omx_deps="libdl pthreads" > omx_rpi_select="omx" >@@ -3280,6 +3282,7 @@ h264_v4l2m2m_encoder_deps="v4l2_m2m >h264_v4l2_m2m" > hevc_amf_encoder_deps="amf" > hevc_cuvid_decoder_deps="cuvid" > hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" >+hevc_d3d12va_encoder_select="cbs_h265 d3d12va_encode" > hevc_mediacodec_decoder_deps="mediacodec" > hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" > hevc_mediacodec_encoder_deps="mediacodec" >@@ -6620,6 +6623,9 @@ check_type "windows.h d3d11.h" >"ID3D11VideoDecoder" > check_type "windows.h d3d11.h" "ID3D11VideoContext" > check_type "windows.h d3d12.h" "ID3D12Device" > 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 > check_type "windows.h" "DPI_AWARENESS_CONTEXT" - >D_WIN32_WINNT=0x0A00 > check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode - >D_WIN32_WINNT=0x0602 > check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat >diff --git a/libavcodec/Makefile b/libavcodec/Makefile >index cbfae5f182..cdda3f0d0a 100644 >--- a/libavcodec/Makefile >+++ b/libavcodec/Makefile >@@ -84,6 +84,7 @@ OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o > OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o > OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o > OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o >+OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o >hw_base_encode.o > OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o > OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o > OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o >@@ -435,6 +436,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o >hevc_mvs.o \ > h274.o > OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o > OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o >+OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o > OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o > OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o > OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o >@@ -1263,7 +1265,7 @@ SKIPHEADERS += %_tablegen.h >\ > > SKIPHEADERS-$(CONFIG_AMF) += amfenc.h > SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h >-SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h >+SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h >d3d12va_encode.h > SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h > SKIPHEADERS-$(CONFIG_JNI) += ffjni.h > SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h >diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c >index 2386b450a6..7b5093233c 100644 >--- a/libavcodec/allcodecs.c >+++ b/libavcodec/allcodecs.c >@@ -855,6 +855,7 @@ extern const FFCodec ff_h264_vaapi_encoder; > extern const FFCodec ff_h264_videotoolbox_encoder; > extern const FFCodec ff_hevc_amf_encoder; > extern const FFCodec ff_hevc_cuvid_decoder; >+extern const FFCodec ff_hevc_d3d12va_encoder; > extern const FFCodec ff_hevc_mediacodec_decoder; > extern const FFCodec ff_hevc_mediacodec_encoder; > extern const FFCodec ff_hevc_mf_encoder; >diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c >new file mode 100644 >index 0000000000..88a08efa76 >--- /dev/null >+++ b/libavcodec/d3d12va_encode.c >@@ -0,0 +1,1550 @@ >+/* >+ * 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/avassert.h" >+#include "libavutil/common.h" >+#include "libavutil/internal.h" >+#include "libavutil/log.h" >+#include "libavutil/pixdesc.h" >+#include "libavutil/hwcontext_d3d12va_internal.h" >+#include "libavutil/hwcontext_d3d12va.h" >+ >+#include "avcodec.h" >+#include "d3d12va_encode.h" >+#include "encode.h" >+ >+const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[] = { >+ HW_CONFIG_ENCODER_FRAMES(D3D12, D3D12VA), >+ NULL, >+}; >+ >+static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx) >+{ >+ uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx- >>fence); >+ if (completion < psync_ctx->fence_value) { >+ if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, >psync_ctx->fence_value, psync_ctx->event))) >+ return AVERROR(EINVAL); >+ >+ WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE); >+ } >+ >+ return 0; >+} >+ >+static int d3d12va_sync_with_gpu(AVCodecContext *avctx) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ >+ DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx- >>sync_ctx.fence, ++ctx->sync_ctx.fence_value)); >+ return d3d12va_fence_completion(&ctx->sync_ctx); >+ >+fail: >+ return AVERROR(EINVAL); >+} >+ >+typedef struct CommandAllocator { >+ ID3D12CommandAllocator *command_allocator; >+ uint64_t fence_value; >+} CommandAllocator; >+ >+static int d3d12va_get_valid_command_allocator(AVCodecContext *avctx, >ID3D12CommandAllocator **ppAllocator) >+{ >+ HRESULT hr; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ CommandAllocator allocator; >+ >+ if (av_fifo_peek(ctx->allocator_queue, &allocator, 1, 0) >= 0) { >+ uint64_t completion = ID3D12Fence_GetCompletedValue(ctx- >>sync_ctx.fence); >+ if (completion >= allocator.fence_value) { >+ *ppAllocator = allocator.command_allocator; >+ av_fifo_read(ctx->allocator_queue, &allocator, 1); >+ return 0; >+ } >+ } >+ >+ hr = ID3D12Device_CreateCommandAllocator(ctx->hwctx->device, >D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, >+ &IID_ID3D12CommandAllocator, (void >**)ppAllocator); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create a new command >allocator!\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ return 0; >+} >+ >+static int d3d12va_discard_command_allocator(AVCodecContext *avctx, >ID3D12CommandAllocator *pAllocator, uint64_t fence_value) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ >+ CommandAllocator allocator = { >+ .command_allocator = pAllocator, >+ .fence_value = fence_value, >+ }; >+ >+ av_fifo_write(ctx->allocator_queue, &allocator, 1); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_wait(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; >+ uint64_t completion; >+ >+ av_assert0(base_pic->encode_issued); >+ >+ if (base_pic->encode_complete) { >+ // Already waited for this picture. >+ return 0; >+ } >+ >+ completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence); >+ if (completion < pic->fence_value) { >+ if (FAILED(ID3D12Fence_SetEventOnCompletion(ctx->sync_ctx.fence, pic- >>fence_value, >+ ctx->sync_ctx.event))) >+ return AVERROR(EINVAL); >+ >+ WaitForSingleObjectEx(ctx->sync_ctx.event, INFINITE, FALSE); >+ } >+ >+ av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" " >+ "(input surface %p).\n", base_pic->display_order, >+ base_pic->encode_order, pic->input_surface->texture); >+ >+ av_frame_free(&base_pic->input_image); >+ >+ base_pic->encode_complete = 1; >+ return 0; >+} >+ >+static int d3d12va_encode_create_metadata_buffers(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ int width = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + >sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA); >+ 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; >+ >+ D3D12_RESOURCE_DESC meta_desc = { >+ .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, >+ .Alignment = 0, >+ .Width = ctx->req.MaxEncoderOutputMetadataBufferSize, >+ .Height = 1, >+ .DepthOrArraySize = 1, >+ .MipLevels = 1, >+ .Format = DXGI_FORMAT_UNKNOWN, >+ .SampleDesc = { .Count = 1, .Quality = 0 }, >+ .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, >+ .Flags = D3D12_RESOURCE_FLAG_NONE, >+ }; >+ >+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, >&encoded_meta_props, D3D12_HEAP_FLAG_NONE, >+ &meta_desc, D3D12_RESOURCE_STATE_COMMON, >NULL, >+ &IID_ID3D12Resource, (void **)&pic- >>encoded_metadata); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create metadata buffer.\n"); >+ return AVERROR_UNKNOWN; >+ } >+ >+ ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx- >>device, &resolved_meta_props, 0, resolved_heap_type); >+ >+ meta_desc.Width = width; >+ >+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, >&resolved_meta_props, D3D12_HEAP_FLAG_NONE, >+ &meta_desc, D3D12_RESOURCE_STATE_COMMON, >NULL, >+ &IID_ID3D12Resource, (void **)&pic- >>resolved_metadata); >+ >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create output metadata >buffer.\n"); >+ return AVERROR_UNKNOWN; >+ } >+ >+ return 0; >+} >+ >+static int d3d12va_encode_issue(AVCodecContext *avctx, >+ const HWBaseEncodePicture *base_pic) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames- >>hwctx; >+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; >+ int err, i, j; >+ HRESULT hr; >+ char data[MAX_PARAM_BUFFER_SIZE]; >+ void *ptr; >+ size_t bit_len; >+ ID3D12CommandAllocator *command_allocator = NULL; >+ ID3D12VideoEncodeCommandList2 *cmd_list = ctx->command_list; >+ D3D12_RESOURCE_BARRIER barriers[32] = { 0 }; >+ D3D12_VIDEO_ENCODE_REFERENCE_FRAMES d3d12_refs = { 0 }; >+ >+ D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args >= { >+ .SequenceControlDesc = { >+ .Flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE, >+ .IntraRefreshConfig = { 0 }, >+ .RateControl = ctx->rc, >+ .PictureTargetResolution = ctx->resolution, >+ .SelectedLayoutMode = >D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, >+ .FrameSubregionsLayoutData = { 0 }, >+ .CodecGopSequence = ctx->gop, >+ }, >+ .pInputFrame = pic->input_surface->texture, >+ .InputFrameSubresource = 0, >+ }; >+ >+ D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS >output_args = { 0 }; >+ >+ D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS >input_metadata = { >+ .EncoderCodec = ctx->codec->d3d12_codec, >+ .EncoderProfile = ctx->profile->d3d12_profile, >+ .EncoderInputFormat = frames_hwctx->format, >+ .EncodedPictureEffectiveResolution = ctx->resolution, >+ }; >+ >+ D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS >output_metadata = { 0 }; >+ >+ memset(data, 0, sizeof(data)); >+ >+ av_log(avctx, AV_LOG_DEBUG, "Issuing encode for >pic %"PRId64"/%"PRId64" " >+ "as type %s.\n", base_pic->display_order, base_pic->encode_order, >+ ff_hw_base_encode_get_pictype_name(base_pic->type)); >+ if (base_pic->nb_refs[0] == 0 && base_pic->nb_refs[1] == 0) { >+ av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n"); >+ } else { >+ av_log(avctx, AV_LOG_DEBUG, "L0 refers to"); >+ for (i = 0; i < base_pic->nb_refs[0]; i++) { >+ av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, >+ base_pic->refs[0][i]->display_order, base_pic->refs[0][i]- >>encode_order); >+ } >+ av_log(avctx, AV_LOG_DEBUG, ".\n"); >+ >+ if (base_pic->nb_refs[1]) { >+ av_log(avctx, AV_LOG_DEBUG, "L1 refers to"); >+ for (i = 0; i < base_pic->nb_refs[1]; i++) { >+ av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, >+ base_pic->refs[1][i]->display_order, base_pic->refs[1][i]- >>encode_order); >+ } >+ av_log(avctx, AV_LOG_DEBUG, ".\n"); >+ } >+ } >+ >+ av_assert0(!base_pic->encode_issued); >+ for (i = 0; i < base_pic->nb_refs[0]; i++) { >+ av_assert0(base_pic->refs[0][i]); >+ av_assert0(base_pic->refs[0][i]->encode_issued); >+ } >+ for (i = 0; i < base_pic->nb_refs[1]; i++) { >+ av_assert0(base_pic->refs[1][i]); >+ av_assert0(base_pic->refs[1][i]->encode_issued); >+ } >+ >+ av_log(avctx, AV_LOG_DEBUG, "Input surface is %p.\n", pic->input_surface- >>texture); >+ >+ err = av_hwframe_get_buffer(base_ctx->recon_frames_ref, base_pic- >>recon_image, 0); >+ if (err < 0) { >+ err = AVERROR(ENOMEM); >+ goto fail; >+ } >+ >+ pic->recon_surface = (AVD3D12VAFrame *)base_pic->recon_image- >>data[0]; >+ av_log(avctx, AV_LOG_DEBUG, "Recon surface is %p.\n", >+ pic->recon_surface->texture); >+ >+ pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool); >+ if (!pic->output_buffer_ref) { >+ err = AVERROR(ENOMEM); >+ goto fail; >+ } >+ pic->output_buffer = (ID3D12Resource *)pic->output_buffer_ref->data; >+ av_log(avctx, AV_LOG_DEBUG, "Output buffer is %p.\n", >+ pic->output_buffer); >+ >+ err = d3d12va_encode_create_metadata_buffers(avctx, pic); >+ if (err < 0) >+ goto fail; >+ >+ if (ctx->codec->init_picture_params) { >+ err = ctx->codec->init_picture_params(avctx, pic); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise picture " >+ "parameters: %d.\n", err); >+ goto fail; >+ } >+ } >+ >+ if (base_pic->type == PICTURE_TYPE_IDR) { >+ if (ctx->codec->write_sequence_header) { >+ bit_len = 8 * sizeof(data); >+ err = ctx->codec->write_sequence_header(avctx, data, &bit_len); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to write per-sequence " >+ "header: %d.\n", err); >+ goto fail; >+ } >+ } >+ >+ pic->header_size = (int)bit_len / 8; >+ pic->header_size = pic->header_size % ctx- >>req.CompressedBitstreamBufferAccessAlignment ? >+ FFALIGN(pic->header_size, ctx- >>req.CompressedBitstreamBufferAccessAlignment) : >+ pic->header_size; >+ >+ hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&ptr); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ memcpy(ptr, data, pic->header_size); >+ ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); >+ } >+ >+ d3d12_refs.NumTexture2Ds = base_pic->nb_refs[0] + base_pic->nb_refs[1]; >+ if (d3d12_refs.NumTexture2Ds) { >+ d3d12_refs.ppTexture2Ds = av_calloc(d3d12_refs.NumTexture2Ds, >+ sizeof(*d3d12_refs.ppTexture2Ds)); >+ if (!d3d12_refs.ppTexture2Ds) { >+ err = AVERROR(ENOMEM); >+ goto fail; >+ } >+ >+ i = 0; >+ for (j = 0; j < base_pic->nb_refs[0]; j++) >+ d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic- >>refs[0][j])->recon_surface->texture; >+ for (j = 0; j < base_pic->nb_refs[1]; j++) >+ d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic- >>refs[1][j])->recon_surface->texture; >+ } >+ >+ input_args.PictureControlDesc.IntraRefreshFrameIndex = 0; >+ if (base_pic->is_reference) >+ input_args.PictureControlDesc.Flags |= >D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PI >CTURE; >+ >+ input_args.PictureControlDesc.PictureControlCodecData = pic->pic_ctl; >+ input_args.PictureControlDesc.ReferenceFrames = d3d12_refs; >+ input_args.CurrentFrameBitstreamMetadataSize = pic->header_size; >+ >+ output_args.Bitstream.pBuffer = pic->output_buffer; >+ output_args.Bitstream.FrameStartOffset = pic->header_size; >+ output_args.ReconstructedPicture.pReconstructedPicture = pic- >>recon_surface->texture; >+ output_args.ReconstructedPicture.ReconstructedPictureSubresource = 0; >+ output_args.EncoderOutputMetadata.pBuffer = pic- >>encoded_metadata; >+ output_args.EncoderOutputMetadata.Offset = 0; >+ >+ input_metadata.HWLayoutMetadata.pBuffer = pic->encoded_metadata; >+ input_metadata.HWLayoutMetadata.Offset = 0; >+ >+ output_metadata.ResolvedLayoutMetadata.pBuffer = pic- >>resolved_metadata; >+ output_metadata.ResolvedLayoutMetadata.Offset = 0; >+ >+ err = d3d12va_get_valid_command_allocator(avctx, >&command_allocator); >+ if (err < 0) >+ goto fail; >+ >+ hr = ID3D12CommandAllocator_Reset(command_allocator); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12VideoEncodeCommandList2_Reset(cmd_list, >command_allocator); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+#define TRANSITION_BARRIER(res, before, after) \ >+ (D3D12_RESOURCE_BARRIER) { \ >+ .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, \ >+ .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, \ >+ .Transition = { \ >+ .pResource = res, \ >+ .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, \ >+ .StateBefore = before, \ >+ .StateAfter = after, \ >+ }, \ >+ } >+ >+ barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, >+ D3D12_RESOURCE_STATE_COMMON, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); >+ barriers[1] = TRANSITION_BARRIER(pic->output_buffer, >+ D3D12_RESOURCE_STATE_COMMON, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); >+ barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, >+ D3D12_RESOURCE_STATE_COMMON, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); >+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, >+ D3D12_RESOURCE_STATE_COMMON, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); >+ barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, >+ D3D12_RESOURCE_STATE_COMMON, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); >+ >+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); >+ >+ if (d3d12_refs.NumTexture2Ds) { >+ D3D12_RESOURCE_BARRIER refs_barriers[3]; >+ >+ for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) >+ refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], >+ D3D12_RESOURCE_STATE_COMMON, >+ >D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); >+ >+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, >d3d12_refs.NumTexture2Ds, >+ refs_barriers); >+ } >+ >+ ID3D12VideoEncodeCommandList2_EncodeFrame(cmd_list, ctx->encoder, >ctx->encoder_heap, >+ &input_args, &output_args); >+ >+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); >+ >+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 1, >&barriers[3]); >+ >+ >ID3D12VideoEncodeCommandList2_ResolveEncoderOutputMetadata(cmd_list >, &input_metadata, &output_metadata); >+ >+ if (d3d12_refs.NumTexture2Ds) { >+ D3D12_RESOURCE_BARRIER refs_barriers[3]; >+ >+ for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) >+ refs_barriers[i] = >TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], >+ >D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, >+ D3D12_RESOURCE_STATE_COMMON); >+ >+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, >d3d12_refs.NumTexture2Ds, >+ refs_barriers); >+ } >+ >+ barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, >+ D3D12_RESOURCE_STATE_COMMON); >+ barriers[1] = TRANSITION_BARRIER(pic->output_buffer, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, >+ D3D12_RESOURCE_STATE_COMMON); >+ barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, >+ D3D12_RESOURCE_STATE_COMMON); >+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, >+ D3D12_RESOURCE_STATE_COMMON); >+ barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, >+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, >+ D3D12_RESOURCE_STATE_COMMON); >+ >+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); >+ >+ hr = ID3D12VideoEncodeCommandList2_Close(cmd_list); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12CommandQueue_Wait(ctx->command_queue, pic- >>input_surface->sync_ctx.fence, >+ pic->input_surface->sync_ctx.fence_value); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, >(ID3D12CommandList **)&ctx->command_list); >+ >+ hr = ID3D12CommandQueue_Signal(ctx->command_queue, pic- >>input_surface->sync_ctx.fence, >+ ++pic->input_surface->sync_ctx.fence_value); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12CommandQueue_Signal(ctx->command_queue, ctx- >>sync_ctx.fence, ++ctx->sync_ctx.fence_value); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx- >>sync_ctx.fence_value); >+ if (err < 0) >+ goto fail; >+ >+ pic->fence_value = ctx->sync_ctx.fence_value; >+ >+ if (d3d12_refs.ppTexture2Ds) >+ av_freep(&d3d12_refs.ppTexture2Ds); >+ >+ return 0; >+ >+fail: >+ if (command_allocator) >+ d3d12va_discard_command_allocator(avctx, command_allocator, ctx- >>sync_ctx.fence_value); >+ >+ if (d3d12_refs.ppTexture2Ds) >+ av_freep(&d3d12_refs.ppTexture2Ds); >+ >+ if (ctx->codec->free_picture_params) >+ ctx->codec->free_picture_params(pic); >+ >+ av_buffer_unref(&pic->output_buffer_ref); >+ pic->output_buffer = NULL; >+ D3D12_OBJECT_RELEASE(pic->encoded_metadata); >+ D3D12_OBJECT_RELEASE(pic->resolved_metadata); >+ return err; >+} >+ >+static int d3d12va_encode_discard(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic) >+{ >+ HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; >+ d3d12va_encode_wait(avctx, pic); >+ >+ if (pic->output_buffer_ref) { >+ av_log(avctx, AV_LOG_DEBUG, "Discard output for pic " >+ "%"PRId64"/%"PRId64".\n", >+ base_pic->display_order, base_pic->encode_order); >+ >+ av_buffer_unref(&pic->output_buffer_ref); >+ pic->output_buffer = NULL; >+ } >+ >+ D3D12_OBJECT_RELEASE(pic->encoded_metadata); >+ D3D12_OBJECT_RELEASE(pic->resolved_metadata); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_free_rc_params(AVCodecContext *avctx) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ >+ switch (ctx->rc.Mode) >+ { >+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP: >+ av_freep(&ctx->rc.ConfigParams.pConfiguration_CQP); >+ break; >+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: >+ av_freep(&ctx->rc.ConfigParams.pConfiguration_CBR); >+ break; >+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: >+ av_freep(&ctx->rc.ConfigParams.pConfiguration_VBR); >+ break; >+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: >+ av_freep(&ctx->rc.ConfigParams.pConfiguration_QVBR); >+ break; >+ default: >+ break; >+ } >+ >+ return 0; >+} >+ >+static HWBaseEncodePicture *d3d12va_encode_alloc(AVCodecContext >*avctx, >+ const AVFrame *frame) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodePicture *pic; >+ >+ pic = av_mallocz(sizeof(*pic)); >+ if (!pic) >+ return NULL; >+ >+ if (ctx->codec->picture_priv_data_size > 0) { >+ pic->base.priv_data = av_mallocz(ctx->codec->picture_priv_data_size); >+ if (!pic->base.priv_data) { >+ av_freep(&pic); >+ return NULL; >+ } >+ } >+ >+ pic->input_surface = (AVD3D12VAFrame *)frame->data[0]; >+ >+ return (HWBaseEncodePicture *)pic; >+} >+ >+static int d3d12va_encode_free(AVCodecContext *avctx, >+ HWBaseEncodePicture *base_pic) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; >+ >+ if (base_pic->encode_issued) >+ d3d12va_encode_discard(avctx, pic); >+ >+ if (ctx->codec->free_picture_params) >+ ctx->codec->free_picture_params(pic); >+ >+ ff_hw_base_encode_free(avctx, base_pic); >+ >+ av_free(pic); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic, size_t *size) >+{ >+ D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; >+ uint8_t *data; >+ HRESULT hr; >+ int err; >+ >+ hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void >**)&data); >+ if (FAILED(hr)) { >+ err = AVERROR_UNKNOWN; >+ return err; >+ } >+ >+ 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; >+ >+ ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_get_coded_data(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic, AVPacket *pkt) >+{ >+ int err; >+ uint8_t *ptr, *mapped_data; >+ size_t total_size = 0; >+ HRESULT hr; >+ >+ err = d3d12va_encode_get_buffer_size(avctx, pic, &total_size); >+ if (err < 0) >+ goto end; >+ >+ total_size += pic->header_size; >+ av_log(avctx, AV_LOG_DEBUG, "Output buffer size %"PRId64"\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, total_size); >+ >+ ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); >+ >+end: >+ av_buffer_unref(&pic->output_buffer_ref); >+ pic->output_buffer = NULL; >+ return err; >+} >+ >+static int d3d12va_encode_output(AVCodecContext *avctx, >+ const HWBaseEncodePicture *base_pic, AVPacket *pkt) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; >+ AVPacket *pkt_ptr = pkt; >+ int err; >+ >+ err = d3d12va_encode_wait(avctx, pic); >+ if (err < 0) >+ return err; >+ >+ err = d3d12va_encode_get_coded_data(avctx, pic, pkt); >+ if (err < 0) >+ return err; >+ >+ av_log(avctx, AV_LOG_DEBUG, "Output read for >pic %"PRId64"/%"PRId64".\n", >+ base_pic->display_order, base_pic->encode_order); >+ >+ ff_hw_base_encode_set_output_property(avctx, (HWBaseEncodePicture >*)base_pic, pkt_ptr, 0); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_set_profile(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ const D3D12VAEncodeProfile *profile; >+ const AVPixFmtDescriptor *desc; >+ int i, depth; >+ >+ desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); >+ if (!desc) { >+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n", >+ base_ctx->input_frames->sw_format); >+ return AVERROR(EINVAL); >+ } >+ >+ depth = desc->comp[0].depth; >+ for (i = 1; i < desc->nb_components; i++) { >+ if (desc->comp[i].depth != depth) { >+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n", >+ desc->name); >+ return AVERROR(EINVAL); >+ } >+ } >+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n", >+ desc->name); >+ >+ av_assert0(ctx->codec->profiles); >+ for (i = 0; (ctx->codec->profiles[i].av_profile != >+ AV_PROFILE_UNKNOWN); i++) { >+ profile = &ctx->codec->profiles[i]; >+ if (depth != profile->depth || >+ desc->nb_components != profile->nb_components) >+ continue; >+ if (desc->nb_components > 1 && >+ (desc->log2_chroma_w != profile->log2_chroma_w || >+ desc->log2_chroma_h != profile->log2_chroma_h)) >+ continue; >+ if (avctx->profile != profile->av_profile && >+ avctx->profile != AV_PROFILE_UNKNOWN) >+ continue; >+ >+ ctx->profile = profile; >+ break; >+ } >+ if (!ctx->profile) { >+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n"); >+ return AVERROR(ENOSYS); >+ } >+ >+ avctx->profile = profile->av_profile; >+ return 0; >+} >+ >+static const D3D12VAEncodeRCMode d3d12va_encode_rc_modes[] = { >+ // Bitrate Quality >+ // | Maxrate | HRD/VBV >+ { 0 }, // | | | | >+ { RC_MODE_CQP, "CQP", 0, 0, 1, 0, 1, >D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP }, >+ { RC_MODE_CBR, "CBR", 1, 0, 0, 1, 1, >D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR }, >+ { RC_MODE_VBR, "VBR", 1, 1, 0, 1, 1, >D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR }, >+ { RC_MODE_QVBR, "QVBR", 1, 1, 1, 1, 1, >D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR }, >+}; >+ >+static int check_rate_control_support(AVCodecContext *avctx, const >D3D12VAEncodeRCMode *rc_mode) >+{ >+ HRESULT hr; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE >d3d12_rc_mode = { >+ .Codec = ctx->codec->d3d12_codec, >+ }; >+ >+ if (!rc_mode->d3d12_mode) >+ return 0; >+ >+ d3d12_rc_mode.IsSupported = 0; >+ d3d12_rc_mode.RateControlMode = rc_mode->d3d12_mode; >+ >+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, >+ >D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE, >+ &d3d12_rc_mode, sizeof(d3d12_rc_mode)); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to check rate control support.\n"); >+ return 0; >+ } >+ >+ return d3d12_rc_mode.IsSupported; >+} >+ >+static int d3d12va_encode_init_rate_control(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ int64_t rc_target_bitrate; >+ int64_t rc_peak_bitrate; >+ int rc_quality; >+ int64_t hrd_buffer_size; >+ int64_t hrd_initial_buffer_fullness; >+ int fr_num, fr_den; >+ const D3D12VAEncodeRCMode *rc_mode; >+ >+ // Rate control mode selection: >+ // * If the user has set a mode explicitly with the rc_mode option, >+ // use it and fail if it is not available. >+ // * If an explicit QP option has been set, use CQP. >+ // * If the codec is CQ-only, use CQP. >+ // * If the QSCALE avcodec option is set, use CQP. >+ // * If bitrate and quality are both set, try QVBR. >+ // * If quality is set, try CQP. >+ // * If bitrate and maxrate are set and have the same value, try CBR. >+ // * If a bitrate is set, try VBR, then CBR. >+ // * If no bitrate is set, try CQP. >+ >+#define TRY_RC_MODE(mode, fail) do { \ >+ rc_mode = &d3d12va_encode_rc_modes[mode]; \ >+ if (!(rc_mode->d3d12_mode && check_rate_control_support(avctx, >rc_mode))) { \ >+ if (fail) { \ >+ av_log(avctx, AV_LOG_ERROR, "Driver does not support %s " \ >+ "RC mode.\n", rc_mode->name); \ >+ return AVERROR(EINVAL); \ >+ } \ >+ av_log(avctx, AV_LOG_DEBUG, "Driver does not support %s " \ >+ "RC mode.\n", rc_mode->name); \ >+ rc_mode = NULL; \ >+ } else { \ >+ goto rc_mode_found; \ >+ } \ >+ } while (0) >+ >+ if (base_ctx->explicit_rc_mode) >+ TRY_RC_MODE(base_ctx->explicit_rc_mode, 1); >+ >+ if (base_ctx->explicit_qp) >+ TRY_RC_MODE(RC_MODE_CQP, 1); >+ >+ if (ctx->codec->flags & FLAG_CONSTANT_QUALITY_ONLY) >+ TRY_RC_MODE(RC_MODE_CQP, 1); >+ >+ if (avctx->flags & AV_CODEC_FLAG_QSCALE) >+ TRY_RC_MODE(RC_MODE_CQP, 1); >+ >+ if (avctx->bit_rate > 0 && avctx->global_quality > 0) >+ TRY_RC_MODE(RC_MODE_QVBR, 0); >+ >+ if (avctx->global_quality > 0) { >+ TRY_RC_MODE(RC_MODE_CQP, 0); >+ } >+ >+ if (avctx->bit_rate > 0 && avctx->rc_max_rate == avctx->bit_rate) >+ TRY_RC_MODE(RC_MODE_CBR, 0); >+ >+ if (avctx->bit_rate > 0) { >+ TRY_RC_MODE(RC_MODE_VBR, 0); >+ TRY_RC_MODE(RC_MODE_CBR, 0); >+ } else { >+ TRY_RC_MODE(RC_MODE_CQP, 0); >+ } >+ >+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any " >+ "RC mode compatible with selected options.\n"); >+ return AVERROR(EINVAL); >+ >+rc_mode_found: >+ if (rc_mode->bitrate) { >+ if (avctx->bit_rate <= 0) { >+ av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s " >+ "RC mode.\n", rc_mode->name); >+ return AVERROR(EINVAL); >+ } >+ >+ if (rc_mode->maxrate) { >+ if (avctx->rc_max_rate > 0) { >+ if (avctx->rc_max_rate < avctx->bit_rate) { >+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: " >+ "bitrate (%"PRId64") must not be greater than " >+ "maxrate (%"PRId64").\n", avctx->bit_rate, >+ avctx->rc_max_rate); >+ return AVERROR(EINVAL); >+ } >+ rc_target_bitrate = avctx->bit_rate; >+ rc_peak_bitrate = avctx->rc_max_rate; >+ } else { >+ // We only have a target bitrate, but this mode requires >+ // that a maximum rate be supplied as well. Since the >+ // user does not want this to be a constraint, arbitrarily >+ // pick a maximum rate of double the target rate. >+ rc_target_bitrate = avctx->bit_rate; >+ rc_peak_bitrate = 2 * avctx->bit_rate; >+ } >+ } else { >+ if (avctx->rc_max_rate > avctx->bit_rate) { >+ av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored " >+ "in %s RC mode.\n", rc_mode->name); >+ } >+ rc_target_bitrate = avctx->bit_rate; >+ rc_peak_bitrate = 0; >+ } >+ } else { >+ rc_target_bitrate = 0; >+ rc_peak_bitrate = 0; >+ } >+ >+ if (rc_mode->quality) { >+ if (base_ctx->explicit_qp) { >+ rc_quality = base_ctx->explicit_qp; >+ } else if (avctx->global_quality > 0) { >+ rc_quality = avctx->global_quality; >+ } else { >+ rc_quality = ctx->codec->default_quality; >+ av_log(avctx, AV_LOG_WARNING, "No quality level set; " >+ "using default (%d).\n", rc_quality); >+ } >+ } else { >+ rc_quality = 0; >+ } >+ >+ if (rc_mode->hrd) { >+ if (avctx->rc_buffer_size) >+ hrd_buffer_size = avctx->rc_buffer_size; >+ else if (avctx->rc_max_rate > 0) >+ hrd_buffer_size = avctx->rc_max_rate; >+ else >+ hrd_buffer_size = avctx->bit_rate; >+ if (avctx->rc_initial_buffer_occupancy) { >+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) { >+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: " >+ "must have initial buffer size (%d) <= " >+ "buffer size (%"PRId64").\n", >+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size); >+ return AVERROR(EINVAL); >+ } >+ hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; >+ } else { >+ hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; >+ } >+ } else { >+ if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) { >+ av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored " >+ "in %s RC mode.\n", rc_mode->name); >+ } >+ >+ hrd_buffer_size = 0; >+ hrd_initial_buffer_fullness = 0; >+ } >+ >+ if (rc_target_bitrate > UINT32_MAX || >+ hrd_buffer_size > UINT32_MAX || >+ hrd_initial_buffer_fullness > UINT32_MAX) { >+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or " >+ "greater are not supported by D3D12.\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ base_ctx->rc_quality = rc_quality; >+ >+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name); >+ >+ if (rc_mode->quality) >+ av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_quality); >+ >+ if (rc_mode->hrd) { >+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, " >+ "initial fullness %"PRId64" bits.\n", >+ hrd_buffer_size, hrd_initial_buffer_fullness); >+ } >+ >+ if (avctx->framerate.num > 0 && avctx->framerate.den > 0) >+ av_reduce(&fr_num, &fr_den, >+ avctx->framerate.num, avctx->framerate.den, 65535); >+ else >+ av_reduce(&fr_num, &fr_den, >+ avctx->time_base.den, avctx->time_base.num, 65535); >+ >+ av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n", >+ fr_num, fr_den, (double)fr_num / fr_den); >+ >+ ctx->rc.Flags = >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE; >+ ctx->rc.TargetFrameRate.Numerator = fr_num; >+ ctx->rc.TargetFrameRate.Denominator = fr_den; >+ ctx->rc.Mode = rc_mode->d3d12_mode; >+ >+ switch (rc_mode->mode) { >+ case RC_MODE_CQP: >+ // cqp ConfigParams will be updated in ctx->codec->configure. >+ break; >+ >+ case RC_MODE_CBR: >+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR *cbr_ctl; >+ >+ ctx->rc.ConfigParams.DataSize = >sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR); >+ cbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); >+ if (!cbr_ctl) >+ return AVERROR(ENOMEM); >+ >+ cbr_ctl->TargetBitRate = rc_target_bitrate; >+ cbr_ctl->VBVCapacity = hrd_buffer_size; >+ cbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; >+ ctx->rc.Flags |= >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; >+ >+ if (avctx->qmin > 0 || avctx->qmax > 0) { >+ cbr_ctl->MinQP = avctx->qmin; >+ cbr_ctl->MaxQP = avctx->qmax; >+ ctx->rc.Flags |= >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; >+ } >+ >+ ctx->rc.ConfigParams.pConfiguration_CBR = cbr_ctl; >+ break; >+ >+ case RC_MODE_VBR: >+ D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR *vbr_ctl; >+ >+ ctx->rc.ConfigParams.DataSize = >sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR); >+ vbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); >+ if (!vbr_ctl) >+ return AVERROR(ENOMEM); >+ >+ vbr_ctl->TargetAvgBitRate = rc_target_bitrate; >+ vbr_ctl->PeakBitRate = rc_peak_bitrate; >+ vbr_ctl->VBVCapacity = hrd_buffer_size; >+ vbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; >+ ctx->rc.Flags |= >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; >+ >+ if (avctx->qmin > 0 || avctx->qmax > 0) { >+ vbr_ctl->MinQP = avctx->qmin; >+ vbr_ctl->MaxQP = avctx->qmax; >+ ctx->rc.Flags |= >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; >+ } >+ >+ ctx->rc.ConfigParams.pConfiguration_VBR = vbr_ctl; >+ break; >+ >+ case RC_MODE_QVBR: >+ D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR *qvbr_ctl; >+ >+ ctx->rc.ConfigParams.DataSize = >sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR); >+ qvbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); >+ if (!qvbr_ctl) >+ return AVERROR(ENOMEM); >+ >+ qvbr_ctl->TargetAvgBitRate = rc_target_bitrate; >+ qvbr_ctl->PeakBitRate = rc_peak_bitrate; >+ qvbr_ctl->ConstantQualityTarget = rc_quality; >+ >+ if (avctx->qmin > 0 || avctx->qmax > 0) { >+ qvbr_ctl->MinQP = avctx->qmin; >+ qvbr_ctl->MaxQP = avctx->qmax; >+ ctx->rc.Flags |= >D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; >+ } >+ >+ ctx->rc.ConfigParams.pConfiguration_QVBR = qvbr_ctl; >+ break; >+ >+ default: >+ break; >+ } >+ return 0; >+} >+ >+static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ uint32_t ref_l0, ref_l1; >+ int err; >+ HRESULT hr; >+ >D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPO >RT support; >+ union { >+ D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 >h264; >+ D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_HEVC >hevc; >+ } codec_support; >+ >+ support.NodeIndex = 0; >+ support.Codec = ctx->codec->d3d12_codec; >+ support.Profile = ctx->profile->d3d12_profile; >+ >+ switch (ctx->codec->d3d12_codec) { >+ case D3D12_VIDEO_ENCODER_CODEC_H264: >+ support.PictureSupport.DataSize = sizeof(codec_support.h264); >+ support.PictureSupport.pH264Support = &codec_support.h264; >+ break; >+ >+ case D3D12_VIDEO_ENCODER_CODEC_HEVC: >+ support.PictureSupport.DataSize = sizeof(codec_support.hevc); >+ support.PictureSupport.pHEVCSupport = &codec_support.hevc; >+ break; >+ } >+ >+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, >D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT, >+ &support, sizeof(support)); >+ if (FAILED(hr)) >+ return AVERROR(EINVAL); >+ >+ if (support.IsSupported) { >+ switch (ctx->codec->d3d12_codec) { >+ case D3D12_VIDEO_ENCODER_CODEC_H264: >+ ref_l0 = FFMIN(support.PictureSupport.pH264Support- >>MaxL0ReferencesForP, >+ support.PictureSupport.pH264Support- >>MaxL1ReferencesForB); >+ ref_l1 = support.PictureSupport.pH264Support- >>MaxL1ReferencesForB; >+ break; >+ >+ case D3D12_VIDEO_ENCODER_CODEC_HEVC: >+ ref_l0 = FFMIN(support.PictureSupport.pHEVCSupport- >>MaxL0ReferencesForP, >+ support.PictureSupport.pHEVCSupport- >>MaxL1ReferencesForB); >+ ref_l1 = support.PictureSupport.pHEVCSupport- >>MaxL1ReferencesForB; >+ break; >+ } >+ } else { >+ ref_l0 = ref_l1 = 0; >+ } >+ >+ if (ref_l0 > 0 && ref_l1 > 0 && ctx->bi_not_empty) { >+ base_ctx->p_to_gpb = 1; >+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support P-frames, " >+ "replacing them with B-frames.\n"); >+ } >+ >+ err = ff_hw_base_init_gop_structure(avctx, ref_l0, ref_l1, ctx->codec->flags, >0); >+ if (err < 0) >+ return err; >+ >+ return 0; >+} >+ >+static int d3d12va_create_encoder(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames- >>hwctx; >+ HRESULT hr; >+ >+ D3D12_VIDEO_ENCODER_DESC desc = { >+ .NodeMask = 0, >+ .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, >+ .EncodeCodec = ctx->codec->d3d12_codec, >+ .EncodeProfile = ctx->profile->d3d12_profile, >+ .InputFormat = frames_hwctx->format, >+ .CodecConfiguration = ctx->codec_conf, >+ .MaxMotionEstimationPrecision = >D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMU >M, >+ }; >+ >+ hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, >&IID_ID3D12VideoEncoder, >+ (void **)&ctx->encoder); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create encoder.\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ return 0; >+} >+ >+static int d3d12va_create_encoder_heap(AVCodecContext* avctx) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ HRESULT hr; >+ >+ D3D12_VIDEO_ENCODER_HEAP_DESC desc = { >+ .NodeMask = 0, >+ .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, >+ .EncodeCodec = ctx->codec->d3d12_codec, >+ .EncodeProfile = ctx->profile->d3d12_profile, >+ .EncodeLevel = ctx->level, >+ .ResolutionsListCount = 1, >+ .pResolutionList = &ctx->resolution, >+ }; >+ >+ hr = ID3D12VideoDevice3_CreateVideoEncoderHeap(ctx->video_device3, >&desc, >+ &IID_ID3D12VideoEncoderHeap, (void **)&ctx- >>encoder_heap); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create encoder heap.\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ return 0; >+} >+ >+static void d3d12va_encode_free_buffer(void *opaque, uint8_t *data) >+{ >+ ID3D12Resource *pResource; >+ >+ pResource = (ID3D12Resource *)data; >+ D3D12_OBJECT_RELEASE(pResource); >+} >+ >+static AVBufferRef *d3d12va_encode_alloc_output_buffer(void *opaque, >size_t size) >+{ >+ AVCodecContext *avctx = opaque; >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ ID3D12Resource *pResource = NULL; >+ HRESULT hr; >+ AVBufferRef *ref; >+ D3D12_HEAP_PROPERTIES heap_props; >+ D3D12_HEAP_TYPE heap_type = D3D12_HEAP_TYPE_READBACK; >+ >+ D3D12_RESOURCE_DESC desc = { >+ .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, >+ .Alignment = 0, >+ .Width = FFALIGN(3 * base_ctx->surface_width * base_ctx- >>surface_height + (1 << 16), >+ D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT), >+ .Height = 1, >+ .DepthOrArraySize = 1, >+ .MipLevels = 1, >+ .Format = DXGI_FORMAT_UNKNOWN, >+ .SampleDesc = { .Count = 1, .Quality = 0 }, >+ .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, >+ .Flags = D3D12_RESOURCE_FLAG_NONE, >+ }; >+ >+ ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx- >>device, &heap_props, 0, heap_type); >+ >+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, >&heap_props, D3D12_HEAP_FLAG_NONE, >+ &desc, D3D12_RESOURCE_STATE_COMMON, NULL, >&IID_ID3D12Resource, >+ (void **)&pResource); >+ >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create d3d12 buffer.\n"); >+ return NULL; >+ } >+ >+ ref = av_buffer_create((uint8_t *)(uintptr_t)pResource, >+ sizeof(pResource), >+ &d3d12va_encode_free_buffer, >+ avctx, AV_BUFFER_FLAG_READONLY); >+ if (!ref) { >+ D3D12_OBJECT_RELEASE(pResource); >+ return NULL; >+ } >+ >+ return ref; >+} >+ >+static int d3d12va_encode_prepare_output_buffers(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ AVD3D12VAFramesContext *frames_ctx = base_ctx->input_frames->hwctx; >+ HRESULT hr; >+ >+ ctx->req.NodeIndex = 0; >+ ctx->req.Codec = ctx->codec->d3d12_codec; >+ ctx->req.Profile = ctx->profile->d3d12_profile; >+ ctx->req.InputFormat = frames_ctx->format; >+ ctx->req.PictureTargetResolution = ctx->resolution; >+ >+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, >+ >D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS, >+ &ctx->req, sizeof(ctx->req)); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to check encoder resource >requirements support.\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ if (!ctx->req.IsSupported) { >+ av_log(avctx, AV_LOG_ERROR, "Encoder resource requirements >unsupported.\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ ctx->output_buffer_pool = av_buffer_pool_init2(sizeof(ID3D12Resource *), >avctx, >+ &d3d12va_encode_alloc_output_buffer, NULL); >+ if (!ctx->output_buffer_pool) >+ return AVERROR(ENOMEM); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_create_command_objects(AVCodecContext >*avctx) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ ID3D12CommandAllocator *command_allocator = NULL; >+ int err; >+ HRESULT hr; >+ >+ D3D12_COMMAND_QUEUE_DESC queue_desc = { >+ .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, >+ .Priority = 0, >+ .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, >+ .NodeMask = 0, >+ }; >+ >+ ctx->allocator_queue = >av_fifo_alloc2(D3D12VA_VIDEO_ENC_ASYNC_DEPTH, >+ sizeof(CommandAllocator), >AV_FIFO_FLAG_AUTO_GROW); >+ if (!ctx->allocator_queue) >+ return AVERROR(ENOMEM); >+ >+ hr = ID3D12Device_CreateFence(ctx->hwctx->device, 0, >D3D12_FENCE_FLAG_NONE, >+ &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create fence(%lx)\n", (long)hr); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); >+ if (!ctx->sync_ctx.event) >+ goto fail; >+ >+ err = d3d12va_get_valid_command_allocator(avctx, >&command_allocator); >+ if (err < 0) >+ goto fail; >+ >+ hr = ID3D12Device_CreateCommandQueue(ctx->hwctx->device, >&queue_desc, >+ &IID_ID3D12CommandQueue, (void **)&ctx- >>command_queue); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create command queue(%lx)\n", >(long)hr); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12Device_CreateCommandList(ctx->hwctx->device, 0, >queue_desc.Type, >+ command_allocator, NULL, &IID_ID3D12CommandList, >+ (void **)&ctx->command_list); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to create command list(%lx)\n", >(long)hr); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12VideoEncodeCommandList2_Close(ctx->command_list); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to close the command list(%lx)\n", >(long)hr); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, >(ID3D12CommandList **)&ctx->command_list); >+ >+ err = d3d12va_sync_with_gpu(avctx); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx- >>sync_ctx.fence_value); >+ if (err < 0) >+ goto fail; >+ >+ return 0; >+ >+fail: >+ D3D12_OBJECT_RELEASE(command_allocator); >+ return err; >+} >+ >+static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ AVD3D12VAFramesContext *hwctx; >+ enum AVPixelFormat recon_format; >+ int err; >+ >+ err = ff_hw_base_get_recon_format(avctx, NULL, &recon_format); >+ if (err < 0) >+ return err; >+ >+ base_ctx->recon_frames_ref = av_hwframe_ctx_alloc(base_ctx- >>device_ref); >+ if (!base_ctx->recon_frames_ref) >+ return AVERROR(ENOMEM); >+ >+ base_ctx->recon_frames = (AVHWFramesContext *)base_ctx- >>recon_frames_ref->data; >+ hwctx = (AVD3D12VAFramesContext *)base_ctx->recon_frames->hwctx; >+ >+ base_ctx->recon_frames->format = AV_PIX_FMT_D3D12; >+ base_ctx->recon_frames->sw_format = recon_format; >+ base_ctx->recon_frames->width = base_ctx->surface_width; >+ base_ctx->recon_frames->height = base_ctx->surface_height; >+ >+ hwctx->flags = >D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | >+ D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; >+ >+ err = av_hwframe_ctx_init(base_ctx->recon_frames_ref); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise reconstructed " >+ "frame context: %d.\n", err); >+ return err; >+ } >+ >+ return 0; >+} >+ >+static const HWEncodePictureOperation d3d12va_type = { >+ .alloc = &d3d12va_encode_alloc, >+ >+ .issue = &d3d12va_encode_issue, >+ >+ .output = &d3d12va_encode_output, >+ >+ .free = &d3d12va_encode_free, >+}; >+ >+int ff_d3d12va_encode_init(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT support = { 0 }; >+ int err; >+ HRESULT hr; >+ >+ err = ff_hw_base_encode_init(avctx); >+ if (err < 0) >+ goto fail; >+ >+ base_ctx->op = &d3d12va_type; >+ >+ ctx->hwctx = base_ctx->device->hwctx; >+ >+ ctx->resolution.Width = base_ctx->input_frames->width; >+ ctx->resolution.Height = base_ctx->input_frames->height; >+ >+ hr = ID3D12Device_QueryInterface(ctx->hwctx->device, >&IID_ID3D12Device3, (void **)&ctx->device3); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "ID3D12Device3 interface is not >supported.\n"); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ hr = ID3D12Device3_QueryInterface(ctx->device3, >&IID_ID3D12VideoDevice3, (void **)&ctx->video_device3); >+ if (FAILED(hr)) { >+ av_log(avctx, AV_LOG_ERROR, "ID3D12VideoDevice3 interface is not >supported.\n"); >+ err = AVERROR_UNKNOWN; >+ goto fail; >+ } >+ >+ if (FAILED(ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, >D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT, >+ &support, sizeof(support))) >&& !support.VideoEncodeSupport) { >+ av_log(avctx, AV_LOG_ERROR, "D3D12 video device has no video >encoder support.\n"); >+ err = AVERROR(EINVAL); >+ goto fail; >+ } >+ >+ err = d3d12va_encode_set_profile(avctx); >+ if (err < 0) >+ goto fail; >+ >+ if (ctx->codec->get_encoder_caps) { >+ err = ctx->codec->get_encoder_caps(avctx); >+ if (err < 0) >+ goto fail; >+ } >+ >+ err = d3d12va_encode_init_rate_control(avctx); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_init_gop_structure(avctx); >+ if (err < 0) >+ goto fail; >+ >+ if (!(ctx->codec->flags & FLAG_SLICE_CONTROL) && avctx->slices > 0) { >+ av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested " >+ "but this codec does not support controlling slices.\n"); >+ } >+ >+ err = d3d12va_encode_create_command_objects(avctx); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_create_recon_frames(avctx); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_prepare_output_buffers(avctx); >+ if (err < 0) >+ goto fail; >+ >+ if (ctx->codec->configure) { >+ err = ctx->codec->configure(avctx); >+ if (err < 0) >+ goto fail; >+ } >+ >+ if (ctx->codec->init_sequence_params) { >+ err = ctx->codec->init_sequence_params(avctx); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Codec sequence initialisation " >+ "failed: %d.\n", err); >+ goto fail; >+ } >+ } >+ >+ if (ctx->codec->set_level) { >+ err = ctx->codec->set_level(avctx); >+ if (err < 0) >+ goto fail; >+ } >+ >+ base_ctx->output_delay = base_ctx->b_per_p; >+ base_ctx->decode_delay = base_ctx->max_b_depth; >+ >+ err = d3d12va_create_encoder(avctx); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_create_encoder_heap(avctx); >+ if (err < 0) >+ goto fail; >+ >+ base_ctx->async_encode = 1; >+ base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth, >+ sizeof(D3D12VAEncodePicture *), 0); >+ if (!base_ctx->encode_fifo) >+ return AVERROR(ENOMEM); >+ >+ return 0; >+ >+fail: >+ return err; >+} >+ >+int ff_d3d12va_encode_close(AVCodecContext *avctx) >+{ >+ int num_allocator = 0; >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ HWBaseEncodePicture *pic, *next; >+ CommandAllocator allocator; >+ >+ if (!base_ctx->frame) >+ return 0; >+ >+ for (pic = base_ctx->pic_start; pic; pic = next) { >+ next = pic->next; >+ d3d12va_encode_free(avctx, pic); >+ } >+ >+ d3d12va_encode_free_rc_params(avctx); >+ >+ av_buffer_pool_uninit(&ctx->output_buffer_pool); >+ >+ D3D12_OBJECT_RELEASE(ctx->command_list); >+ D3D12_OBJECT_RELEASE(ctx->command_queue); >+ >+ if (ctx->allocator_queue) { >+ while (av_fifo_read(ctx->allocator_queue, &allocator, 1) >= 0) { >+ num_allocator++; >+ D3D12_OBJECT_RELEASE(allocator.command_allocator); >+ } >+ >+ av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators >reused: %d\n", num_allocator); >+ } >+ >+ av_fifo_freep2(&ctx->allocator_queue); >+ >+ D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence); >+ if (ctx->sync_ctx.event) >+ CloseHandle(ctx->sync_ctx.event); >+ >+ D3D12_OBJECT_RELEASE(ctx->encoder_heap); >+ D3D12_OBJECT_RELEASE(ctx->encoder); >+ D3D12_OBJECT_RELEASE(ctx->video_device3); >+ D3D12_OBJECT_RELEASE(ctx->device3); >+ >+ ff_hw_base_encode_close(avctx); >+ >+ return 0; >+} >diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h >new file mode 100644 >index 0000000000..10e2d87035 >--- /dev/null >+++ b/libavcodec/d3d12va_encode.h >@@ -0,0 +1,321 @@ >+/* >+ * 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 >+ */ >+ >+#ifndef AVCODEC_D3D12VA_ENCODE_H >+#define AVCODEC_D3D12VA_ENCODE_H >+ >+#include "libavutil/fifo.h" >+#include "libavutil/hwcontext.h" >+#include "libavutil/hwcontext_d3d12va_internal.h" >+#include "libavutil/hwcontext_d3d12va.h" >+#include "avcodec.h" >+#include "internal.h" >+#include "hwconfig.h" >+#include "hw_base_encode.h" >+ >+struct D3D12VAEncodeType; >+ >+extern const AVCodecHWConfigInternal *const >ff_d3d12va_encode_hw_configs[]; >+ >+#define MAX_PARAM_BUFFER_SIZE 4096 >+#define D3D12VA_VIDEO_ENC_ASYNC_DEPTH 8 >+ >+typedef struct D3D12VAEncodePicture { >+ HWBaseEncodePicture base; >+ >+ int header_size; >+ >+ AVD3D12VAFrame *input_surface; >+ AVD3D12VAFrame *recon_surface; >+ >+ AVBufferRef *output_buffer_ref; >+ ID3D12Resource *output_buffer; >+ >+ ID3D12Resource *encoded_metadata; >+ ID3D12Resource *resolved_metadata; >+ >+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA pic_ctl; >+ >+ int fence_value; >+} D3D12VAEncodePicture; >+ >+typedef struct D3D12VAEncodeProfile { >+ /** >+ * lavc profile value (AV_PROFILE_*). >+ */ >+ int av_profile; >+ >+ /** >+ * Supported bit depth. >+ */ >+ int depth; >+ >+ /** >+ * Number of components. >+ */ >+ int nb_components; >+ >+ /** >+ * Chroma subsampling in width dimension. >+ */ >+ int log2_chroma_w; >+ >+ /** >+ * Chroma subsampling in height dimension. >+ */ >+ int log2_chroma_h; >+ >+ /** >+ * D3D12 profile value. >+ */ >+ D3D12_VIDEO_ENCODER_PROFILE_DESC d3d12_profile; >+} D3D12VAEncodeProfile; >+ >+enum { >+ RC_MODE_AUTO, >+ RC_MODE_CQP, >+ RC_MODE_CBR, >+ RC_MODE_VBR, >+ RC_MODE_QVBR, >+ RC_MODE_MAX = RC_MODE_QVBR, >+}; >+ >+ >+typedef struct D3D12VAEncodeRCMode { >+ /** >+ * Mode from above enum (RC_MODE_*). >+ */ >+ int mode; >+ >+ /** >+ * Name. >+ * >+ */ >+ const char *name; >+ >+ /** >+ * Uses bitrate parameters. >+ * >+ */ >+ int bitrate; >+ >+ /** >+ * Supports maxrate distinct from bitrate. >+ * >+ */ >+ int maxrate; >+ >+ /** >+ * Uses quality value. >+ * >+ */ >+ int quality; >+ >+ /** >+ * Supports HRD/VBV parameters. >+ * >+ */ >+ int hrd; >+ >+ /** >+ * Supported by D3D12 HW. >+ */ >+ int supported; >+ >+ /** >+ * D3D12 mode value. >+ */ >+ D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_mode; >+} D3D12VAEncodeRCMode; >+ >+typedef struct D3D12VAEncodeContext { >+ HWBaseEncodeContext base; >+ >+ /** >+ * Codec-specific hooks. >+ */ >+ const struct D3D12VAEncodeType *codec; >+ >+ /** >+ * Chosen encoding profile details. >+ */ >+ const D3D12VAEncodeProfile *profile; >+ >+ AVD3D12VADeviceContext *hwctx; >+ >+ /** >+ * ID3D12Device3 interface. >+ */ >+ ID3D12Device3 *device3; >+ >+ /** >+ * ID3D12VideoDevice3 interface. >+ */ >+ ID3D12VideoDevice3 *video_device3; >+ >+ /** >+ * Pool of (reusable) bitstream output buffers. >+ */ >+ AVBufferPool *output_buffer_pool; >+ >+ /** >+ * D3D12 video encoder. >+ */ >+ AVBufferRef *encoder_ref; >+ >+ ID3D12VideoEncoder *encoder; >+ >+ /** >+ * D3D12 video encoder heap. >+ */ >+ ID3D12VideoEncoderHeap *encoder_heap; >+ >+ /** >+ * A cached queue for reusing the D3D12 command allocators. >+ * >+ * @see https://learn.microsoft.com/en- >us/windows/win32/direct3d12/recording-command-lists-and- >bundles#id3d12commandallocator >+ */ >+ AVFifo *allocator_queue; >+ >+ /** >+ * D3D12 command queue. >+ */ >+ ID3D12CommandQueue *command_queue; >+ >+ /** >+ * D3D12 video encode command list. >+ */ >+ ID3D12VideoEncodeCommandList2 *command_list; >+ >+ /** >+ * The sync context used to sync command queue. >+ */ >+ AVD3D12VASyncContext sync_ctx; >+ >+ /** >+ * The bi_not_empty feature. >+ */ >+ int bi_not_empty; >+ >+ /** >+ * D3D12_FEATURE structures. >+ */ >+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req; >+ >+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS >res_limits; >+ >+ /** >+ * D3D12_VIDEO_ENCODER structures. >+ */ >+ D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolution; >+ >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codec_conf; >+ >+ D3D12_VIDEO_ENCODER_RATE_CONTROL rc; >+ >+ D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop; >+ >+ D3D12_VIDEO_ENCODER_LEVEL_SETTING level; >+} D3D12VAEncodeContext; >+ >+typedef struct D3D12VAEncodeType { >+ /** >+ * List of supported profiles. >+ */ >+ const D3D12VAEncodeProfile *profiles; >+ >+ /** >+ * D3D12 codec name. >+ */ >+ D3D12_VIDEO_ENCODER_CODEC d3d12_codec; >+ >+ /** >+ * Codec feature flags. >+ */ >+ int flags; >+ >+ /** >+ * Default quality for this codec - used as quantiser or RC quality >+ * factor depending on RC mode. >+ */ >+ int default_quality; >+ >+ /** >+ * Query codec configuration and determine encode parameters like >+ * block sizes for surface alignment and slices. If not set, assume >+ * that all blocks are 16x16 and that surfaces should be aligned to match >+ * this. >+ */ >+ int (*get_encoder_caps)(AVCodecContext *avctx); >+ >+ /** >+ * Perform any extra codec-specific configuration. >+ */ >+ int (*configure)(AVCodecContext *avctx); >+ >+ /** >+ * Set codec-specific level setting. >+ */ >+ int (*set_level)(AVCodecContext *avctx); >+ >+ /** >+ * The size of any private data structure associated with each >+ * picture (can be zero if not required). >+ */ >+ size_t picture_priv_data_size; >+ >+ /** >+ * Fill the corresponding parameters. >+ */ >+ int (*init_sequence_params)(AVCodecContext *avctx); >+ >+ int (*init_picture_params)(AVCodecContext *avctx, >+ D3D12VAEncodePicture *pic); >+ >+ void (*free_picture_params)(D3D12VAEncodePicture *pic); >+ >+ /** >+ * Write the packed header data to the provided buffer. >+ */ >+ int (*write_sequence_header)(AVCodecContext *avctx, >+ char *data, size_t *data_len); >+} D3D12VAEncodeType; >+ >+int ff_d3d12va_encode_init(AVCodecContext *avctx); >+int ff_d3d12va_encode_close(AVCodecContext *avctx); >+ >+#define D3D12VA_ENCODE_RC_MODE(name, desc) \ >+ { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \ >+ 0, 0, FLAGS, .unit = "rc_mode" } >+#define D3D12VA_ENCODE_RC_OPTIONS \ >+ { "rc_mode",\ >+ "Set rate control mode", \ >+ OFFSET(common.base.explicit_rc_mode), AV_OPT_TYPE_INT, \ >+ { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, >FLAGS, .unit = "rc_mode" }, \ >+ { "auto", "Choose mode automatically based on other parameters", \ >+ 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, .unit = >"rc_mode" }, \ >+ D3D12VA_ENCODE_RC_MODE(CQP, "Constant-quality"), \ >+ D3D12VA_ENCODE_RC_MODE(CBR, "Constant-bitrate"), \ >+ D3D12VA_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \ >+ D3D12VA_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate") >+ >+#endif /* AVCODEC_D3D12VA_ENCODE_H */ >diff --git a/libavcodec/d3d12va_encode_hevc.c >b/libavcodec/d3d12va_encode_hevc.c >new file mode 100644 >index 0000000000..aec0d9dcec >--- /dev/null >+++ b/libavcodec/d3d12va_encode_hevc.c >@@ -0,0 +1,957 @@ >+/* >+ * 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/pixdesc.h" >+#include "libavutil/hwcontext_d3d12va_internal.h" >+ >+#include "avcodec.h" >+#include "cbs.h" >+#include "cbs_h265.h" >+#include "h2645data.h" >+#include "h265_profile_level.h" >+#include "codec_internal.h" >+#include "d3d12va_encode.h" >+ >+typedef struct D3D12VAEncodeHEVCPicture { >+ int pic_order_cnt; >+ int64_t last_idr_frame; >+} D3D12VAEncodeHEVCPicture; >+ >+typedef struct D3D12VAEncodeHEVCContext { >+ D3D12VAEncodeContext common; >+ >+ // User options. >+ int qp; >+ int profile; >+ int tier; >+ int level; >+ >+ // Writer structures. >+ H265RawVPS raw_vps; >+ H265RawSPS raw_sps; >+ H265RawPPS raw_pps; >+ >+ CodedBitstreamContext *cbc; >+ CodedBitstreamFragment current_access_unit; >+} D3D12VAEncodeHEVCContext; >+ >+typedef struct D3D12VAEncodeHEVCLevel { >+ int level; >+ D3D12_VIDEO_ENCODER_LEVELS_HEVC d3d12_level; >+} D3D12VAEncodeHEVCLevel; >+ >+static const >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC >hevc_config_support_sets[] = >+{ >+ { >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_N >ONE, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, >+ 3, >+ 3, >+ }, >+ { >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_N >ONE, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, >+ 0, >+ 0, >+ }, >+ { >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_N >ONE, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, >+ 2, >+ 2, >+ }, >+ { >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_N >ONE, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, >+ 2, >+ 2, >+ }, >+ { >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_N >ONE, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, >+ 4, >+ 4, >+ }, >+}; >+ >+static const D3D12VAEncodeHEVCLevel hevc_levels[] = { >+ { 30, D3D12_VIDEO_ENCODER_LEVELS_HEVC_1 }, >+ { 60, D3D12_VIDEO_ENCODER_LEVELS_HEVC_2 }, >+ { 63, D3D12_VIDEO_ENCODER_LEVELS_HEVC_21 }, >+ { 90, D3D12_VIDEO_ENCODER_LEVELS_HEVC_3 }, >+ { 93, D3D12_VIDEO_ENCODER_LEVELS_HEVC_31 }, >+ { 120, D3D12_VIDEO_ENCODER_LEVELS_HEVC_4 }, >+ { 123, D3D12_VIDEO_ENCODER_LEVELS_HEVC_41 }, >+ { 150, D3D12_VIDEO_ENCODER_LEVELS_HEVC_5 }, >+ { 153, D3D12_VIDEO_ENCODER_LEVELS_HEVC_51 }, >+ { 156, D3D12_VIDEO_ENCODER_LEVELS_HEVC_52 }, >+ { 180, D3D12_VIDEO_ENCODER_LEVELS_HEVC_6 }, >+ { 183, D3D12_VIDEO_ENCODER_LEVELS_HEVC_61 }, >+ { 186, D3D12_VIDEO_ENCODER_LEVELS_HEVC_62 }, >+}; >+ >+static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main = >D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; >+static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main10 = >D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN10; >+ >+#define D3D_PROFILE_DESC(name) \ >+ { sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), { .pHEVCProfile = >(D3D12_VIDEO_ENCODER_PROFILE_HEVC *)&profile_ ## name } } >+static const D3D12VAEncodeProfile d3d12va_encode_hevc_profiles[] = { >+ { AV_PROFILE_HEVC_MAIN, 8, 3, 1, 1, D3D_PROFILE_DESC(main) }, >+ { AV_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, D3D_PROFILE_DESC(main10) }, >+ { AV_PROFILE_UNKNOWN }, >+}; >+ >+static uint8_t >d3d12va_encode_hevc_map_cusize(D3D12_VIDEO_ENCODER_CODEC_CONFI >GURATION_HEVC_CUSIZE cusize) >+{ >+ switch (cusize) { >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8: >return 8; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_16x16: >return 16; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32: >return 32; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64: >return 64; >+ default: av_assert0(0); >+ } >+ return 0; >+} >+ >+static uint8_t >d3d12va_encode_hevc_map_tusize(D3D12_VIDEO_ENCODER_CODEC_CONFI >GURATION_HEVC_TUSIZE tusize) >+{ >+ switch (tusize) { >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4: >return 4; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_8x8: >return 8; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_16x16: >return 16; >+ case >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32: >return 32; >+ default: av_assert0(0); >+ } >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_write_access_unit(AVCodecContext *avctx, >+ char *data, size_t *data_len, >+ CodedBitstreamFragment *au) >+{ >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ int err; >+ >+ err = ff_cbs_write_fragment_data(priv->cbc, au); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); >+ return err; >+ } >+ >+ if (*data_len < 8 * au->data_size - au->data_bit_padding) { >+ av_log(avctx, AV_LOG_ERROR, "Access unit too large: " >+ "%zu < %zu.\n", *data_len, >+ 8 * au->data_size - au->data_bit_padding); >+ return AVERROR(ENOSPC); >+ } >+ >+ memcpy(data, au->data, au->data_size); >+ *data_len = 8 * au->data_size - au->data_bit_padding; >+ >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_add_nal(AVCodecContext *avctx, >+ CodedBitstreamFragment *au, >+ void *nal_unit) >+{ >+ H265RawNALUnitHeader *header = nal_unit; >+ int err; >+ >+ err = ff_cbs_insert_unit_content(au, -1, >+ header->nal_unit_type, nal_unit, NULL); >+ if (err < 0) { >+ av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: " >+ "type = %d.\n", header->nal_unit_type); >+ return err; >+ } >+ >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_write_sequence_header(AVCodecContext >*avctx, >+ char *data, size_t *data_len) >+{ >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ CodedBitstreamFragment *au = &priv->current_access_unit; >+ int err; >+ >+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_vps); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_sps); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_pps); >+ if (err < 0) >+ goto fail; >+ >+ err = d3d12va_encode_hevc_write_access_unit(avctx, data, data_len, au); >+fail: >+ ff_cbs_fragment_reset(au); >+ return err; >+ >+} >+ >+static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext >*avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx; >+ H265RawVPS *vps = &priv->raw_vps; >+ H265RawSPS *sps = &priv->raw_sps; >+ H265RawPPS *pps = &priv->raw_pps; >+ H265RawProfileTierLevel *ptl = &vps->profile_tier_level; >+ H265RawVUI *vui = &sps->vui; >+ D3D12_VIDEO_ENCODER_PROFILE_HEVC profile = >D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; >+ D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC level = { 0 }; >+ const AVPixFmtDescriptor *desc; >+ uint8_t min_cu_size, max_cu_size, min_tu_size, max_tu_size; >+ int chroma_format, bit_depth; >+ HRESULT hr; >+ int i; >+ >+ D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = { >+ .NodeIndex = 0, >+ .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, >+ .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 = MAX_DPB_SIZE - 1, >+ .CodecConfiguration = ctx->codec_conf, >+ .SuggestedProfile.DataSize = >sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), >+ .SuggestedProfile.pHEVCProfile = &profile, >+ .SuggestedLevel.DataSize = >sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC), >+ .SuggestedLevel.pHEVCLevelSetting = &level, >+ .pResolutionDependentSupport = &ctx->res_limits, >+ }; >+ >+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, >D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, >+ &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 >features. %#x\n", >+ support.ValidationFlags); >+ return AVERROR(EINVAL); >+ } >+ >+ if (support.SupportFlags & >D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUI >RE_TEXTURE_ARRAYS) { >+ av_log(avctx, AV_LOG_ERROR, "D3D12 video encode on this device >requires texture array support, " >+ "but it's not implemented.\n"); >+ return AVERROR_PATCHWELCOME; >+ } >+ >+ memset(vps, 0, sizeof(*vps)); >+ memset(sps, 0, sizeof(*sps)); >+ memset(pps, 0, sizeof(*pps)); >+ >+ desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); >+ av_assert0(desc); >+ if (desc->nb_components == 1) { >+ chroma_format = 0; >+ } else { >+ if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) { >+ chroma_format = 1; >+ } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) { >+ chroma_format = 2; >+ } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) { >+ chroma_format = 3; >+ } else { >+ av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format " >+ "%s is not supported.\n", desc->name); >+ return AVERROR(EINVAL); >+ } >+ } >+ bit_depth = desc->comp[0].depth; >+ >+ min_cu_size = d3d12va_encode_hevc_map_cusize(ctx- >>codec_conf.pHEVCConfig->MinLumaCodingUnitSize); >+ max_cu_size = d3d12va_encode_hevc_map_cusize(ctx- >>codec_conf.pHEVCConfig->MaxLumaCodingUnitSize); >+ min_tu_size = d3d12va_encode_hevc_map_tusize(ctx- >>codec_conf.pHEVCConfig->MinLumaTransformUnitSize); >+ max_tu_size = d3d12va_encode_hevc_map_tusize(ctx- >>codec_conf.pHEVCConfig->MaxLumaTransformUnitSize); >+ >+ // VPS >+ >+ vps->nal_unit_header = (H265RawNALUnitHeader) { >+ .nal_unit_type = HEVC_NAL_VPS, >+ .nuh_layer_id = 0, >+ .nuh_temporal_id_plus1 = 1, >+ }; >+ >+ vps->vps_video_parameter_set_id = 0; >+ >+ vps->vps_base_layer_internal_flag = 1; >+ vps->vps_base_layer_available_flag = 1; >+ vps->vps_max_layers_minus1 = 0; >+ vps->vps_max_sub_layers_minus1 = 0; >+ vps->vps_temporal_id_nesting_flag = 1; >+ >+ ptl->general_profile_space = 0; >+ ptl->general_profile_idc = avctx->profile; >+ ptl->general_tier_flag = priv->tier; >+ >+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; >+ >+ ptl->general_progressive_source_flag = 1; >+ ptl->general_interlaced_source_flag = 0; >+ ptl->general_non_packed_constraint_flag = 1; >+ ptl->general_frame_only_constraint_flag = 1; >+ >+ ptl->general_max_14bit_constraint_flag = bit_depth <= 14; >+ ptl->general_max_12bit_constraint_flag = bit_depth <= 12; >+ ptl->general_max_10bit_constraint_flag = bit_depth <= 10; >+ ptl->general_max_8bit_constraint_flag = bit_depth == 8; >+ >+ ptl->general_max_422chroma_constraint_flag = chroma_format <= 2; >+ ptl->general_max_420chroma_constraint_flag = chroma_format <= 1; >+ ptl->general_max_monochrome_constraint_flag = chroma_format == 0; >+ >+ ptl->general_intra_constraint_flag = base_ctx->gop_size == 1; >+ ptl->general_one_picture_only_constraint_flag = 0; >+ >+ ptl->general_lower_bit_rate_constraint_flag = 1; >+ >+ if (avctx->level != FF_LEVEL_UNKNOWN) { >+ ptl->general_level_idc = avctx->level; >+ } else { >+ const H265LevelDescriptor *level; >+ >+ level = ff_h265_guess_level(ptl, avctx->bit_rate, >+ base_ctx->surface_width, base_ctx->surface_height, >+ 1, 1, 1, (base_ctx->b_per_p > 0) + 1); >+ if (level) { >+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name); >+ ptl->general_level_idc = level->level_idc; >+ } else { >+ av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to " >+ "any normal level; using level 8.5.\n"); >+ ptl->general_level_idc = 255; >+ // The tier flag must be set in level 8.5. >+ ptl->general_tier_flag = 1; >+ } >+ avctx->level = ptl->general_level_idc; >+ } >+ >+ vps->vps_sub_layer_ordering_info_present_flag = 0; >+ vps->vps_max_dec_pic_buffering_minus1[0] = base_ctx->max_b_depth >+ 1; >+ vps->vps_max_num_reorder_pics[0] = base_ctx->max_b_depth; >+ vps->vps_max_latency_increase_plus1[0] = 0; >+ >+ vps->vps_max_layer_id = 0; >+ vps->vps_num_layer_sets_minus1 = 0; >+ vps->layer_id_included_flag[0][0] = 1; >+ >+ vps->vps_timing_info_present_flag = 0; >+ >+ // SPS >+ >+ sps->nal_unit_header = (H265RawNALUnitHeader) { >+ .nal_unit_type = HEVC_NAL_SPS, >+ .nuh_layer_id = 0, >+ .nuh_temporal_id_plus1 = 1, >+ }; >+ >+ sps->sps_video_parameter_set_id = vps->vps_video_parameter_set_id; >+ >+ sps->sps_max_sub_layers_minus1 = vps->vps_max_sub_layers_minus1; >+ sps->sps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag; >+ >+ sps->profile_tier_level = vps->profile_tier_level; >+ >+ sps->sps_seq_parameter_set_id = 0; >+ >+ sps->chroma_format_idc = chroma_format; >+ sps->separate_colour_plane_flag = 0; >+ >+ av_assert0(ctx->res_limits.SubregionBlockPixelsSize % min_cu_size == 0); >+ >+ sps->pic_width_in_luma_samples = FFALIGN(base_ctx->surface_width, >+ ctx->res_limits.SubregionBlockPixelsSize); >+ sps->pic_height_in_luma_samples = FFALIGN(base_ctx->surface_height, >+ ctx->res_limits.SubregionBlockPixelsSize); >+ >+ if (avctx->width != sps->pic_width_in_luma_samples || >+ avctx->height != sps->pic_height_in_luma_samples) { >+ sps->conformance_window_flag = 1; >+ sps->conf_win_left_offset = 0; >+ sps->conf_win_right_offset = >+ (sps->pic_width_in_luma_samples - avctx->width) >> desc- >>log2_chroma_w; >+ sps->conf_win_top_offset = 0; >+ sps->conf_win_bottom_offset = >+ (sps->pic_height_in_luma_samples - avctx->height) >> desc- >>log2_chroma_h; >+ } else { >+ sps->conformance_window_flag = 0; >+ } >+ >+ sps->bit_depth_luma_minus8 = bit_depth - 8; >+ sps->bit_depth_chroma_minus8 = bit_depth - 8; >+ >+ sps->log2_max_pic_order_cnt_lsb_minus4 = ctx- >>gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4; >+ >+ sps->sps_sub_layer_ordering_info_present_flag = >+ vps->vps_sub_layer_ordering_info_present_flag; >+ for (i = 0; i <= sps->sps_max_sub_layers_minus1; i++) { >+ sps->sps_max_dec_pic_buffering_minus1[i] = >+ vps->vps_max_dec_pic_buffering_minus1[i]; >+ sps->sps_max_num_reorder_pics[i] = >+ vps->vps_max_num_reorder_pics[i]; >+ sps->sps_max_latency_increase_plus1[i] = >+ vps->vps_max_latency_increase_plus1[i]; >+ } >+ >+ sps->log2_min_luma_coding_block_size_minus3 = >(uint8_t)(av_log2(min_cu_size) - 3); >+ sps->log2_diff_max_min_luma_coding_block_size = >(uint8_t)(av_log2(max_cu_size) - av_log2(min_cu_size)); >+ sps->log2_min_luma_transform_block_size_minus2 = >(uint8_t)(av_log2(min_tu_size) - 2); >+ sps->log2_diff_max_min_luma_transform_block_size = >(uint8_t)(av_log2(max_tu_size) - av_log2(min_tu_size)); >+ >+ sps->max_transform_hierarchy_depth_inter = ctx- >>codec_conf.pHEVCConfig->max_transform_hierarchy_depth_inter; >+ sps->max_transform_hierarchy_depth_intra = ctx- >>codec_conf.pHEVCConfig->max_transform_hierarchy_depth_intra; >+ >+ sps->amp_enabled_flag = !!(ctx->codec_conf.pHEVCConfig- >>ConfigurationFlags & >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYME >TRIC_MOTION_PARTITION); >+ sps->sample_adaptive_offset_enabled_flag = !!(ctx- >>codec_conf.pHEVCConfig->ConfigurationFlags & >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO >_FILTER); >+ sps->sps_temporal_mvp_enabled_flag = 0; >+ sps->pcm_enabled_flag = 0; >+ >+ sps->vui_parameters_present_flag = 1; >+ >+ // vui default parameters >+ vui->aspect_ratio_idc = 0; >+ vui->video_format = 5; >+ vui->video_full_range_flag = 0; >+ vui->colour_primaries = 2; >+ vui->transfer_characteristics = 2; >+ vui->matrix_coefficients = 2; >+ vui->chroma_sample_loc_type_top_field = 0; >+ vui->chroma_sample_loc_type_bottom_field = 0; >+ vui->tiles_fixed_structure_flag = 0; >+ vui->motion_vectors_over_pic_boundaries_flag = 1; >+ vui->min_spatial_segmentation_idc = 0; >+ vui->max_bytes_per_pic_denom = 2; >+ vui->max_bits_per_min_cu_denom = 1; >+ vui->log2_max_mv_length_horizontal = 15; >+ vui->log2_max_mv_length_vertical = 15; >+ >+ // PPS >+ >+ pps->nal_unit_header = (H265RawNALUnitHeader) { >+ .nal_unit_type = HEVC_NAL_PPS, >+ .nuh_layer_id = 0, >+ .nuh_temporal_id_plus1 = 1, >+ }; >+ >+ pps->pps_pic_parameter_set_id = 0; >+ pps->pps_seq_parameter_set_id = sps->sps_seq_parameter_set_id; >+ >+ pps->cabac_init_present_flag = 1; >+ >+ pps->num_ref_idx_l0_default_active_minus1 = 0; >+ pps->num_ref_idx_l1_default_active_minus1 = 0; >+ >+ pps->init_qp_minus26 = 0; >+ >+ pps->transform_skip_enabled_flag = !!(ctx->codec_conf.pHEVCConfig- >>ConfigurationFlags & >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRA >NSFORM_SKIPPING); >+ >+ // cu_qp_delta always required to be 1 in >https://github.com/microsoft/DirectX- >Specs/blob/master/d3d/D3D12VideoEncoding.md >+ pps->cu_qp_delta_enabled_flag = 1; >+ >+ pps->diff_cu_qp_delta_depth = 0; >+ >+ pps->pps_slice_chroma_qp_offsets_present_flag = 1; >+ >+ pps->tiles_enabled_flag = 0; // no tiling in D3D12 >+ >+ pps->pps_loop_filter_across_slices_enabled_flag = !(ctx- >>codec_conf.pHEVCConfig->ConfigurationFlags & >+ >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LO >OP_FILTER_ACROSS_SLICES); >+ pps->deblocking_filter_control_present_flag = 1; >+ >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_get_encoder_caps(AVCodecContext *avctx) >+{ >+ int i; >+ HRESULT hr; >+ uint8_t min_cu_size, max_cu_size; >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC *config; >+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC >hevc_caps; >+ >+ >D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT >codec_caps = { >+ .NodeIndex = 0, >+ .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, >+ .Profile = ctx->profile->d3d12_profile, >+ .CodecSupportLimits.DataSize = >sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC), >+ }; >+ >+ for (i = 0; i < FF_ARRAY_ELEMS(hevc_config_support_sets); i++) { >+ hevc_caps = hevc_config_support_sets[i]; >+ codec_caps.CodecSupportLimits.pHEVCSupport = &hevc_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) >+ break; >+ } >+ >+ if (i == FF_ARRAY_ELEMS(hevc_config_support_sets)) { >+ av_log(avctx, AV_LOG_ERROR, "Unsupported codec configuration\n"); >+ return AVERROR(EINVAL); >+ } >+ >+ ctx->codec_conf.DataSize = >sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC); >+ ctx->codec_conf.pHEVCConfig = av_mallocz(ctx->codec_conf.DataSize); >+ if (!ctx->codec_conf.pHEVCConfig) >+ return AVERROR(ENOMEM); >+ >+ config = ctx->codec_conf.pHEVCConfig; >+ >+ config->ConfigurationFlags = >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_NONE; >+ config->MinLumaCodingUnitSize = >hevc_caps.MinLumaCodingUnitSize; >+ config->MaxLumaCodingUnitSize = >hevc_caps.MaxLumaCodingUnitSize; >+ config->MinLumaTransformUnitSize = >hevc_caps.MinLumaTransformUnitSize; >+ config->MaxLumaTransformUnitSize = >hevc_caps.MaxLumaTransformUnitSize; >+ config->max_transform_hierarchy_depth_inter = >hevc_caps.max_transform_hierarchy_depth_inter; >+ config->max_transform_hierarchy_depth_intra = >hevc_caps.max_transform_hierarchy_depth_intra; >+ >+ if (hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_AS >YMETRIC_MOTION_PARTITION_SUPPORT || >+ hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_AS >YMETRIC_MOTION_PARTITION_REQUIRED) >+ config->ConfigurationFlags |= >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYME >TRIC_MOTION_PARTITION; >+ >+ if (hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_SA >O_FILTER_SUPPORT) >+ config->ConfigurationFlags |= >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO >_FILTER; >+ >+ if (hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_DI >SABLING_LOOP_FILTER_ACROSS_SLICES_SUPPORT) >+ config->ConfigurationFlags |= >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LO >OP_FILTER_ACROSS_SLICES; >+ >+ if (hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TR >ANSFORM_SKIP_SUPPORT) >+ config->ConfigurationFlags |= >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRA >NSFORM_SKIPPING; >+ >+ if (hevc_caps.SupportFlags & >D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_P_ >FRAMES_IMPLEMENTED_AS_LOW_DELAY_B_FRAMES) >+ ctx->bi_not_empty = 1; >+ >+ // block sizes >+ min_cu_size = >d3d12va_encode_hevc_map_cusize(hevc_caps.MinLumaCodingUnitSize); >+ max_cu_size = >d3d12va_encode_hevc_map_cusize(hevc_caps.MaxLumaCodingUnitSize); >+ >+ av_log(avctx, AV_LOG_VERBOSE, "Using CTU size %dx%d, " >+ "min CB size %dx%d.\n", max_cu_size, max_cu_size, >+ min_cu_size, min_cu_size); >+ >+ base_ctx->surface_width = FFALIGN(avctx->width, min_cu_size); >+ base_ctx->surface_height = FFALIGN(avctx->height, min_cu_size); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_configure(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ int fixed_qp_idr, fixed_qp_p, fixed_qp_b; >+ int err; >+ >+ err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx); >+ if (err < 0) >+ return err; >+ >+ // Rate control >+ if (ctx->rc.Mode == >D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP) { >+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP *cqp_ctl; >+ fixed_qp_p = av_clip(base_ctx->rc_quality, 1, 51); >+ if (avctx->i_quant_factor > 0.0) >+ fixed_qp_idr = av_clip((avctx->i_quant_factor * fixed_qp_p + >+ avctx->i_quant_offset) + 0.5, 1, 51); >+ else >+ fixed_qp_idr = fixed_qp_p; >+ if (avctx->b_quant_factor > 0.0) >+ fixed_qp_b = av_clip((avctx->b_quant_factor * fixed_qp_p + >+ avctx->b_quant_offset) + 0.5, 1, 51); >+ else >+ fixed_qp_b = fixed_qp_p; >+ >+ av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " >+ "%d / %d / %d for IDR- / P- / B-frames.\n", >+ fixed_qp_idr, fixed_qp_p, fixed_qp_b); >+ >+ 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_idr; >+ cqp_ctl->ConstantQP_InterPredictedFrame_PrevRefOnly = fixed_qp_p; >+ cqp_ctl->ConstantQP_InterPredictedFrame_BiDirectionalRef = >fixed_qp_b; >+ >+ ctx->rc.ConfigParams.pConfiguration_CQP = cqp_ctl; >+ } >+ >+ // GOP >+ ctx->gop.DataSize = >sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC); >+ ctx->gop.pHEVCGroupOfPictures = av_mallocz(ctx->gop.DataSize); >+ if (!ctx->gop.pHEVCGroupOfPictures) >+ return AVERROR(ENOMEM); >+ >+ ctx->gop.pHEVCGroupOfPictures->GOPLength = base_ctx->gop_size; >+ ctx->gop.pHEVCGroupOfPictures->PPicturePeriod = base_ctx->b_per_p + 1; >+ // Power of 2 >+ if (base_ctx->gop_size & base_ctx->gop_size - 1 == 0) >+ ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 >= >+ FFMAX(av_log2(base_ctx->gop_size) - 4, 0); >+ else >+ ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 >= >+ FFMAX(av_log2(base_ctx->gop_size) - 3, 0); >+ >+ return 0; >+} >+ >+static int d3d12va_encode_hevc_set_level(AVCodecContext *avctx) >+{ >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ int i; >+ >+ ctx->level.DataSize = >sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC); >+ ctx->level.pHEVCLevelSetting = av_mallocz(ctx->level.DataSize); >+ if (!ctx->level.pHEVCLevelSetting) >+ return AVERROR(ENOMEM); >+ >+ for (i = 0; i < FF_ARRAY_ELEMS(hevc_levels); i++) { >+ if (avctx->level == hevc_levels[i].level) { >+ ctx->level.pHEVCLevelSetting->Level = hevc_levels[i].d3d12_level; >+ break; >+ } >+ } >+ >+ if (i == FF_ARRAY_ELEMS(hevc_levels)) { >+ av_log(avctx, AV_LOG_ERROR, "Invalid level %d.\n", avctx->level); >+ return AVERROR(EINVAL); >+ } >+ >+ ctx->level.pHEVCLevelSetting->Tier = priv- >>raw_vps.profile_tier_level.general_tier_flag == 0 ? >+ D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN : >+ D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH; >+ >+ return 0; >+} >+ >+static void >d3d12va_encode_hevc_free_picture_params(D3D12VAEncodePicture *pic) >+{ >+ if (!pic->pic_ctl.pHEVCPicData) >+ return; >+ >+ av_freep(&pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames); >+ av_freep(&pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames); >+ av_freep(&pic->pic_ctl.pHEVCPicData- >>pReferenceFramesReconPictureDescriptors); >+ av_freep(&pic->pic_ctl.pHEVCPicData); >+} >+ >+static int d3d12va_encode_hevc_init_picture_params(AVCodecContext >*avctx, >+ D3D12VAEncodePicture *pic) >+{ >+ HWBaseEncodePicture *base_pic = (HWBaseEncodePicture >*)pic; >+ D3D12VAEncodeHEVCPicture *hpic = base_pic->priv_data; >+ HWBaseEncodePicture *prev = base_pic->prev; >+ D3D12VAEncodeHEVCPicture *hprev = prev ? prev- >>priv_data : NULL; >+ D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_HEVC *pd = >NULL; >+ UINT *ref_list0 = NULL, *ref_list1 = NULL; >+ int i, idx = 0; >+ >+ pic->pic_ctl.DataSize = >sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC); >+ pic->pic_ctl.pHEVCPicData = av_mallocz(pic->pic_ctl.DataSize); >+ if (!pic->pic_ctl.pHEVCPicData) >+ return AVERROR(ENOMEM); >+ >+ if (base_pic->type == PICTURE_TYPE_IDR) { >+ av_assert0(base_pic->display_order == base_pic->encode_order); >+ hpic->last_idr_frame = base_pic->display_order; >+ } else { >+ av_assert0(prev); >+ hpic->last_idr_frame = hprev->last_idr_frame; >+ } >+ hpic->pic_order_cnt = base_pic->display_order - hpic->last_idr_frame; >+ >+ switch(base_pic->type) { >+ case PICTURE_TYPE_IDR: >+ pic->pic_ctl.pHEVCPicData->FrameType = >D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_IDR_FRAME; >+ break; >+ case PICTURE_TYPE_I: >+ pic->pic_ctl.pHEVCPicData->FrameType = >D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_I_FRAME; >+ break; >+ case PICTURE_TYPE_P: >+ pic->pic_ctl.pHEVCPicData->FrameType = >D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_P_FRAME; >+ break; >+ case PICTURE_TYPE_B: >+ pic->pic_ctl.pHEVCPicData->FrameType = >D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_B_FRAME; >+ break; >+ default: >+ av_assert0(0 && "invalid picture type"); >+ } >+ >+ pic->pic_ctl.pHEVCPicData->slice_pic_parameter_set_id = 0; >+ pic->pic_ctl.pHEVCPicData->PictureOrderCountNumber = hpic- >>pic_order_cnt; >+ >+ if (base_pic->type == PICTURE_TYPE_P || base_pic->type == >PICTURE_TYPE_B) { >+ pd = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*pd)); >+ if (!pd) >+ return AVERROR(ENOMEM); >+ >+ ref_list0 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list0)); >+ if (!ref_list0) >+ return AVERROR(ENOMEM); >+ >+ pic->pic_ctl.pHEVCPicData->List0ReferenceFramesCount = base_pic- >>nb_refs[0]; >+ for (i = 0; i < base_pic->nb_refs[0]; i++) { >+ HWBaseEncodePicture *ref = base_pic->refs[0][i]; >+ D3D12VAEncodeHEVCPicture *href; >+ >+ av_assert0(ref && ref->encode_order < base_pic->encode_order); >+ href = ref->priv_data; >+ >+ ref_list0[i] = idx; >+ pd[idx].ReconstructedPictureResourceIndex = idx; >+ pd[idx].IsRefUsedByCurrentPic = TRUE; >+ pd[idx].PictureOrderCountNumber = href->pic_order_cnt; >+ idx++; >+ } >+ } >+ >+ if (base_pic->type == PICTURE_TYPE_B) { >+ ref_list1 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list1)); >+ if (!ref_list1) >+ return AVERROR(ENOMEM); >+ >+ pic->pic_ctl.pHEVCPicData->List1ReferenceFramesCount = base_pic- >>nb_refs[1]; >+ for (i = 0; i < base_pic->nb_refs[1]; i++) { >+ HWBaseEncodePicture *ref = base_pic->refs[1][i]; >+ D3D12VAEncodeHEVCPicture *href; >+ >+ av_assert0(ref && ref->encode_order < base_pic->encode_order); >+ href = ref->priv_data; >+ >+ ref_list1[i] = idx; >+ pd[idx].ReconstructedPictureResourceIndex = idx; >+ pd[idx].IsRefUsedByCurrentPic = TRUE; >+ pd[idx].PictureOrderCountNumber = href->pic_order_cnt; >+ idx++; >+ } >+ } >+ >+ pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames = ref_list0; >+ pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames = ref_list1; >+ pic->pic_ctl.pHEVCPicData->ReferenceFramesReconPictureDescriptorsCount >= idx; >+ pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors = >pd; >+ >+ return 0; >+} >+ >+static const D3D12VAEncodeType d3d12va_encode_type_hevc = { >+ .profiles = d3d12va_encode_hevc_profiles, >+ >+ .d3d12_codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, >+ >+ .flags = FLAG_B_PICTURES | >+ FLAG_B_PICTURE_REFERENCES | >+ FLAG_NON_IDR_KEY_PICTURES, >+ >+ .default_quality = 25, >+ >+ .get_encoder_caps = &d3d12va_encode_hevc_get_encoder_caps, >+ >+ .configure = &d3d12va_encode_hevc_configure, >+ >+ .set_level = &d3d12va_encode_hevc_set_level, >+ >+ .picture_priv_data_size = sizeof(D3D12VAEncodeHEVCPicture), >+ >+ .init_sequence_params = >&d3d12va_encode_hevc_init_sequence_params, >+ >+ .init_picture_params = &d3d12va_encode_hevc_init_picture_params, >+ >+ .free_picture_params = &d3d12va_encode_hevc_free_picture_params, >+ >+ .write_sequence_header = >&d3d12va_encode_hevc_write_sequence_header, >+}; >+ >+static int d3d12va_encode_hevc_init(AVCodecContext *avctx) >+{ >+ HWBaseEncodeContext *base_ctx = avctx->priv_data; >+ D3D12VAEncodeContext *ctx = avctx->priv_data; >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ >+ ctx->codec = &d3d12va_encode_type_hevc; >+ >+ if (avctx->profile == AV_PROFILE_UNKNOWN) >+ avctx->profile = priv->profile; >+ if (avctx->level == FF_LEVEL_UNKNOWN) >+ avctx->level = priv->level; >+ >+ if (avctx->level != FF_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) >+ base_ctx->explicit_qp = priv->qp; >+ >+ return ff_d3d12va_encode_init(avctx); >+} >+ >+static int d3d12va_encode_hevc_close(AVCodecContext *avctx) >+{ >+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data; >+ >+ ff_cbs_fragment_free(&priv->current_access_unit); >+ ff_cbs_close(&priv->cbc); >+ >+ av_freep(&priv->common.codec_conf.pHEVCConfig); >+ av_freep(&priv->common.gop.pHEVCGroupOfPictures); >+ av_freep(&priv->common.level.pHEVCLevelSetting); >+ >+ return ff_d3d12va_encode_close(avctx); >+} >+ >+#define OFFSET(x) offsetof(D3D12VAEncodeHEVCContext, x) >+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | >AV_OPT_FLAG_ENCODING_PARAM) >+static const AVOption d3d12va_encode_hevc_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_HEVC_MAIN) }, >+ { PROFILE("main10", AV_PROFILE_HEVC_MAIN_10) }, >+ { PROFILE("rext", AV_PROFILE_HEVC_REXT) }, >+#undef PROFILE >+ >+ { "tier", "Set tier (general_tier_flag)", >+ OFFSET(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 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, >+ >+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ >+ { .i64 = value }, 0, 0, FLAGS, "level" >+ { LEVEL("1", 30) }, >+ { LEVEL("2", 60) }, >+ { LEVEL("2.1", 63) }, >+ { LEVEL("3", 90) }, >+ { LEVEL("3.1", 93) }, >+ { LEVEL("4", 120) }, >+ { LEVEL("4.1", 123) }, >+ { LEVEL("5", 150) }, >+ { LEVEL("5.1", 153) }, >+ { LEVEL("5.2", 156) }, >+ { LEVEL("6", 180) }, >+ { LEVEL("6.1", 183) }, >+ { LEVEL("6.2", 186) }, >+#undef LEVEL >+ >+ { NULL }, >+}; >+ >+static const FFCodecDefault d3d12va_encode_hevc_defaults[] = { >+ { "b", "0" }, >+ { "bf", "2" }, >+ { "g", "120" }, >+ { "i_qfactor", "1" }, >+ { "i_qoffset", "0" }, >+ { "b_qfactor", "1" }, >+ { "b_qoffset", "0" }, >+ { "qmin", "-1" }, >+ { "qmax", "-1" }, >+ { NULL }, >+}; >+ >+static const AVClass d3d12va_encode_hevc_class = { >+ .class_name = "hevc_d3d12va", >+ .item_name = av_default_item_name, >+ .option = d3d12va_encode_hevc_options, >+ .version = LIBAVUTIL_VERSION_INT, >+}; >+ >+const FFCodec ff_hevc_d3d12va_encoder = { >+ .p.name = "hevc_d3d12va", >+ CODEC_LONG_NAME("D3D12VA hevc encoder"), >+ .p.type = AVMEDIA_TYPE_VIDEO, >+ .p.id = AV_CODEC_ID_HEVC, >+ .priv_data_size = sizeof(D3D12VAEncodeHEVCContext), >+ .init = &d3d12va_encode_hevc_init, >+ FF_CODEC_RECEIVE_PACKET_CB(&ff_hw_base_encode_receive_packet), >+ .close = &d3d12va_encode_hevc_close, >+ .p.priv_class = &d3d12va_encode_hevc_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_hevc_defaults, >+ .p.pix_fmts = (const enum AVPixelFormat[]) { >+ AV_PIX_FMT_D3D12, >+ AV_PIX_FMT_NONE, >+ }, >+ .hw_configs = ff_d3d12va_encode_hw_configs, >+ .p.wrapper_name = "d3d12va", >+}; >-- >2.41.0.windows.1
On Do, 2024-03-14 at 16:14 +0800, tong1.wu-at-intel.com@ffmpeg.org wrote: > From: Tong Wu <tong1.wu@intel.com> > > This implementation is based on D3D12 Video Encoding Spec: > https://microsoft.github.io/DirectX-Specs/d3d/D3D12VideoEncoding.html > > Sample command line for transcoding: > ffmpeg.exe -hwaccel d3d12va -hwaccel_output_format d3d12 -i input.mp4 > -c:v hevc_d3d12va output.mp4 > > Signed-off-by: Tong Wu <tong1.wu@intel.com> > --- > configure | 6 + > libavcodec/Makefile | 4 +- > libavcodec/allcodecs.c | 1 + > libavcodec/d3d12va_encode.c | 1550 ++++++++++++++++++++++++++++++ > libavcodec/d3d12va_encode.h | 321 +++++++ > libavcodec/d3d12va_encode_hevc.c | 957 ++++++++++++++++++ > 6 files changed, 2838 insertions(+), 1 deletion(-) > create mode 100644 libavcodec/d3d12va_encode.c > create mode 100644 libavcodec/d3d12va_encode.h > create mode 100644 libavcodec/d3d12va_encode_hevc.c > > diff --git a/configure b/configure > index c34bdd13f5..53076fbf22 100755 > --- a/configure > +++ b/configure > @@ -2570,6 +2570,7 @@ CONFIG_EXTRA=" > tpeldsp > vaapi_1 > vaapi_encode > + d3d12va_encode Please add the entry in alphabetic order > vc1dsp > videodsp > vp3dsp > @@ -3214,6 +3215,7 @@ wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" > wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" > > # hardware-accelerated codecs > +d3d12va_encode_deps="d3d12va ID3D12VideoEncoder d3d12_encoder_feature" > mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" > omx_deps="libdl pthreads" > omx_rpi_select="omx" > @@ -3280,6 +3282,7 @@ h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m" > hevc_amf_encoder_deps="amf" > hevc_cuvid_decoder_deps="cuvid" > hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" > +hevc_d3d12va_encoder_select="cbs_h265 d3d12va_encode" > hevc_mediacodec_decoder_deps="mediacodec" > hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" > hevc_mediacodec_encoder_deps="mediacodec" > @@ -6620,6 +6623,9 @@ check_type "windows.h d3d11.h" "ID3D11VideoDecoder" > check_type "windows.h d3d11.h" "ID3D11VideoContext" > check_type "windows.h d3d12.h" "ID3D12Device" > 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 > check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 > check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode - > D_WIN32_WINNT=0x0602 > check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index cbfae5f182..cdda3f0d0a 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -84,6 +84,7 @@ OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o > OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o > OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o > OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o > +OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o > OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o > OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o > OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o > @@ -435,6 +436,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o > hevc_mvs.o \ > h274.o > OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o > OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o > +OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o > OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o > OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o > OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o > @@ -1263,7 +1265,7 @@ SKIPHEADERS += > %_tablegen.h \ > > SKIPHEADERS-$(CONFIG_AMF) += amfenc.h > SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h > -SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h > +SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h d3d12va_encode.h > SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h > SKIPHEADERS-$(CONFIG_JNI) += ffjni.h > SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c > index 2386b450a6..7b5093233c 100644 > --- a/libavcodec/allcodecs.c > +++ b/libavcodec/allcodecs.c > @@ -855,6 +855,7 @@ extern const FFCodec ff_h264_vaapi_encoder; > extern const FFCodec ff_h264_videotoolbox_encoder; > extern const FFCodec ff_hevc_amf_encoder; > extern const FFCodec ff_hevc_cuvid_decoder; > +extern const FFCodec ff_hevc_d3d12va_encoder; > extern const FFCodec ff_hevc_mediacodec_decoder; > extern const FFCodec ff_hevc_mediacodec_encoder; > extern const FFCodec ff_hevc_mf_encoder; > diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c > new file mode 100644 > index 0000000000..88a08efa76 > --- /dev/null > +++ b/libavcodec/d3d12va_encode.c > @@ -0,0 +1,1550 @@ > +/* > + * 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/avassert.h" > +#include "libavutil/common.h" > +#include "libavutil/internal.h" > +#include "libavutil/log.h" > +#include "libavutil/pixdesc.h" > +#include "libavutil/hwcontext_d3d12va_internal.h" > +#include "libavutil/hwcontext_d3d12va.h" > + > +#include "avcodec.h" > +#include "d3d12va_encode.h" > +#include "encode.h" > + > +const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[] = { > + HW_CONFIG_ENCODER_FRAMES(D3D12, D3D12VA), > + NULL, > +}; > + > +static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx) > +{ > + uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence); > + if (completion < psync_ctx->fence_value) { > + if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, > psync_ctx->fence_value, psync_ctx->event))) > + return AVERROR(EINVAL); > + > + WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE); > + } > + > + return 0; > +} > + > +static int d3d12va_sync_with_gpu(AVCodecContext *avctx) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + > + DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx- > >sync_ctx.fence, ++ctx->sync_ctx.fence_value)); > + return d3d12va_fence_completion(&ctx->sync_ctx); > + > +fail: > + return AVERROR(EINVAL); > +} > + > +typedef struct CommandAllocator { > + ID3D12CommandAllocator *command_allocator; > + uint64_t fence_value; > +} CommandAllocator; > + > +static int d3d12va_get_valid_command_allocator(AVCodecContext *avctx, > ID3D12CommandAllocator **ppAllocator) > +{ > + HRESULT hr; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + CommandAllocator allocator; > + > + if (av_fifo_peek(ctx->allocator_queue, &allocator, 1, 0) >= 0) { > + uint64_t completion = ID3D12Fence_GetCompletedValue(ctx- > >sync_ctx.fence); > + if (completion >= allocator.fence_value) { > + *ppAllocator = allocator.command_allocator; > + av_fifo_read(ctx->allocator_queue, &allocator, 1); > + return 0; > + } > + } > + > + hr = ID3D12Device_CreateCommandAllocator(ctx->hwctx->device, > D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, > + &IID_ID3D12CommandAllocator, > (void **)ppAllocator); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create a new command > allocator!\n"); > + return AVERROR(EINVAL); > + } > + > + return 0; > +} > + > +static int d3d12va_discard_command_allocator(AVCodecContext *avctx, > ID3D12CommandAllocator *pAllocator, uint64_t fence_value) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + > + CommandAllocator allocator = { > + .command_allocator = pAllocator, > + .fence_value = fence_value, > + }; > + > + av_fifo_write(ctx->allocator_queue, &allocator, 1); > + > + return 0; > +} > + > +static int d3d12va_encode_wait(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; > + uint64_t completion; > + > + av_assert0(base_pic->encode_issued); > + > + if (base_pic->encode_complete) { > + // Already waited for this picture. > + return 0; > + } > + > + completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence); > + if (completion < pic->fence_value) { > + if (FAILED(ID3D12Fence_SetEventOnCompletion(ctx->sync_ctx.fence, pic- > >fence_value, > + ctx->sync_ctx.event))) > + return AVERROR(EINVAL); > + > + WaitForSingleObjectEx(ctx->sync_ctx.event, INFINITE, FALSE); > + } > + > + av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" " > + "(input surface %p).\n", base_pic->display_order, > + base_pic->encode_order, pic->input_surface->texture); > + > + av_frame_free(&base_pic->input_image); > + > + base_pic->encode_complete = 1; > + return 0; > +} > + > +static int d3d12va_encode_create_metadata_buffers(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + int width = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + > sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA); > + 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; > + > + D3D12_RESOURCE_DESC meta_desc = { > + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, > + .Alignment = 0, > + .Width = ctx->req.MaxEncoderOutputMetadataBufferSize, > + .Height = 1, > + .DepthOrArraySize = 1, > + .MipLevels = 1, > + .Format = DXGI_FORMAT_UNKNOWN, > + .SampleDesc = { .Count = 1, .Quality = 0 }, > + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, > + .Flags = D3D12_RESOURCE_FLAG_NONE, > + }; > + > + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, > &encoded_meta_props, D3D12_HEAP_FLAG_NONE, > + &meta_desc, > D3D12_RESOURCE_STATE_COMMON, NULL, > + &IID_ID3D12Resource, (void > **)&pic->encoded_metadata); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create metadata buffer.\n"); > + return AVERROR_UNKNOWN; > + } > + > + ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, > &resolved_meta_props, 0, resolved_heap_type); > + > + meta_desc.Width = width; > + > + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, > &resolved_meta_props, D3D12_HEAP_FLAG_NONE, > + &meta_desc, > D3D12_RESOURCE_STATE_COMMON, NULL, > + &IID_ID3D12Resource, (void > **)&pic->resolved_metadata); > + > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create output metadata > buffer.\n"); > + return AVERROR_UNKNOWN; > + } > + > + return 0; > +} > + > +static int d3d12va_encode_issue(AVCodecContext *avctx, > + const HWBaseEncodePicture *base_pic) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; > + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; > + int err, i, j; > + HRESULT hr; > + char data[MAX_PARAM_BUFFER_SIZE]; > + void *ptr; > + size_t bit_len; > + ID3D12CommandAllocator *command_allocator = NULL; > + ID3D12VideoEncodeCommandList2 *cmd_list = ctx->command_list; > + D3D12_RESOURCE_BARRIER barriers[32] = { 0 }; > + D3D12_VIDEO_ENCODE_REFERENCE_FRAMES d3d12_refs = { 0 }; > + > + D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = { > + .SequenceControlDesc = { > + .Flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE, > + .IntraRefreshConfig = { 0 }, > + .RateControl = ctx->rc, > + .PictureTargetResolution = ctx->resolution, > + .SelectedLayoutMode = > D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, > + .FrameSubregionsLayoutData = { 0 }, > + .CodecGopSequence = ctx->gop, > + }, > + .pInputFrame = pic->input_surface->texture, > + .InputFrameSubresource = 0, > + }; > + > + D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS output_args = { 0 }; > + > + D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS input_metadata = { > + .EncoderCodec = ctx->codec->d3d12_codec, > + .EncoderProfile = ctx->profile->d3d12_profile, > + .EncoderInputFormat = frames_hwctx->format, > + .EncodedPictureEffectiveResolution = ctx->resolution, > + }; > + > + D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS output_metadata = { > 0 }; > + > + memset(data, 0, sizeof(data)); > + > + av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" " > + "as type %s.\n", base_pic->display_order, base_pic->encode_order, > + ff_hw_base_encode_get_pictype_name(base_pic->type)); > + if (base_pic->nb_refs[0] == 0 && base_pic->nb_refs[1] == 0) { > + av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n"); > + } else { > + av_log(avctx, AV_LOG_DEBUG, "L0 refers to"); > + for (i = 0; i < base_pic->nb_refs[0]; i++) { > + av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, > + base_pic->refs[0][i]->display_order, base_pic->refs[0][i]- > >encode_order); > + } > + av_log(avctx, AV_LOG_DEBUG, ".\n"); > + > + if (base_pic->nb_refs[1]) { > + av_log(avctx, AV_LOG_DEBUG, "L1 refers to"); > + for (i = 0; i < base_pic->nb_refs[1]; i++) { > + av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, > + base_pic->refs[1][i]->display_order, base_pic- > >refs[1][i]->encode_order); > + } > + av_log(avctx, AV_LOG_DEBUG, ".\n"); > + } > + } > + > + av_assert0(!base_pic->encode_issued); > + for (i = 0; i < base_pic->nb_refs[0]; i++) { > + av_assert0(base_pic->refs[0][i]); > + av_assert0(base_pic->refs[0][i]->encode_issued); > + } > + for (i = 0; i < base_pic->nb_refs[1]; i++) { > + av_assert0(base_pic->refs[1][i]); > + av_assert0(base_pic->refs[1][i]->encode_issued); > + } > + > + av_log(avctx, AV_LOG_DEBUG, "Input surface is %p.\n", pic->input_surface- > >texture); > + > + err = av_hwframe_get_buffer(base_ctx->recon_frames_ref, base_pic- > >recon_image, 0); > + if (err < 0) { > + err = AVERROR(ENOMEM); > + goto fail; > + } > + > + pic->recon_surface = (AVD3D12VAFrame *)base_pic->recon_image->data[0]; > + av_log(avctx, AV_LOG_DEBUG, "Recon surface is %p.\n", > + pic->recon_surface->texture); > + > + pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool); > + if (!pic->output_buffer_ref) { > + err = AVERROR(ENOMEM); > + goto fail; > + } > + pic->output_buffer = (ID3D12Resource *)pic->output_buffer_ref->data; > + av_log(avctx, AV_LOG_DEBUG, "Output buffer is %p.\n", > + pic->output_buffer); > + > + err = d3d12va_encode_create_metadata_buffers(avctx, pic); > + if (err < 0) > + goto fail; > + > + if (ctx->codec->init_picture_params) { > + err = ctx->codec->init_picture_params(avctx, pic); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Failed to initialise picture " > + "parameters: %d.\n", err); > + goto fail; > + } > + } > + > + if (base_pic->type == PICTURE_TYPE_IDR) { > + if (ctx->codec->write_sequence_header) { > + bit_len = 8 * sizeof(data); > + err = ctx->codec->write_sequence_header(avctx, data, &bit_len); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Failed to write per-sequence " > + "header: %d.\n", err); > + goto fail; > + } > + } > + > + pic->header_size = (int)bit_len / 8; > + pic->header_size = pic->header_size % ctx- > >req.CompressedBitstreamBufferAccessAlignment ? > + FFALIGN(pic->header_size, ctx- > >req.CompressedBitstreamBufferAccessAlignment) : > + pic->header_size; > + > + hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&ptr); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + memcpy(ptr, data, pic->header_size); > + ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); > + } > + > + d3d12_refs.NumTexture2Ds = base_pic->nb_refs[0] + base_pic->nb_refs[1]; > + if (d3d12_refs.NumTexture2Ds) { > + d3d12_refs.ppTexture2Ds = av_calloc(d3d12_refs.NumTexture2Ds, > + > sizeof(*d3d12_refs.ppTexture2Ds)); > + if (!d3d12_refs.ppTexture2Ds) { > + err = AVERROR(ENOMEM); > + goto fail; > + } > + > + i = 0; > + for (j = 0; j < base_pic->nb_refs[0]; j++) > + d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic- > >refs[0][j])->recon_surface->texture; > + for (j = 0; j < base_pic->nb_refs[1]; j++) > + d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic- > >refs[1][j])->recon_surface->texture; > + } > + > + input_args.PictureControlDesc.IntraRefreshFrameIndex = 0; > + if (base_pic->is_reference) > + input_args.PictureControlDesc.Flags |= > D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE; > + > + input_args.PictureControlDesc.PictureControlCodecData = pic->pic_ctl; > + input_args.PictureControlDesc.ReferenceFrames = d3d12_refs; > + input_args.CurrentFrameBitstreamMetadataSize = pic->header_size; > + > + output_args.Bitstream.pBuffer = pic- > >output_buffer; > + output_args.Bitstream.FrameStartOffset = pic- > >header_size; > + output_args.ReconstructedPicture.pReconstructedPicture = pic- > >recon_surface->texture; > + output_args.ReconstructedPicture.ReconstructedPictureSubresource = 0; > + output_args.EncoderOutputMetadata.pBuffer = pic- > >encoded_metadata; > + output_args.EncoderOutputMetadata.Offset = 0; > + > + input_metadata.HWLayoutMetadata.pBuffer = pic->encoded_metadata; > + input_metadata.HWLayoutMetadata.Offset = 0; > + > + output_metadata.ResolvedLayoutMetadata.pBuffer = pic->resolved_metadata; > + output_metadata.ResolvedLayoutMetadata.Offset = 0; > + > + err = d3d12va_get_valid_command_allocator(avctx, &command_allocator); > + if (err < 0) > + goto fail; > + > + hr = ID3D12CommandAllocator_Reset(command_allocator); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12VideoEncodeCommandList2_Reset(cmd_list, command_allocator); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > +#define TRANSITION_BARRIER(res, before, after) \ > + (D3D12_RESOURCE_BARRIER) { \ > + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, \ > + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, \ > + .Transition = { \ > + .pResource = res, \ > + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, \ > + .StateBefore = before, \ > + .StateAfter = after, \ > + }, \ > + } > + > + barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, > + D3D12_RESOURCE_STATE_COMMON, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); > + barriers[1] = TRANSITION_BARRIER(pic->output_buffer, > + D3D12_RESOURCE_STATE_COMMON, > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); > + barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, > + D3D12_RESOURCE_STATE_COMMON, > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); > + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, > + D3D12_RESOURCE_STATE_COMMON, > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); > + barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, > + D3D12_RESOURCE_STATE_COMMON, > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); > + > + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); > + > + if (d3d12_refs.NumTexture2Ds) { > + D3D12_RESOURCE_BARRIER refs_barriers[3]; > + > + for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) > + refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], > + > D3D12_RESOURCE_STATE_COMMON, > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); > + > + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, > d3d12_refs.NumTexture2Ds, > + refs_barriers); > + } > + > + ID3D12VideoEncodeCommandList2_EncodeFrame(cmd_list, ctx->encoder, ctx- > >encoder_heap, > + &input_args, &output_args); > + > + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); > + > + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 1, &barriers[3]); > + > + ID3D12VideoEncodeCommandList2_ResolveEncoderOutputMetadata(cmd_list, > &input_metadata, &output_metadata); > + > + if (d3d12_refs.NumTexture2Ds) { > + D3D12_RESOURCE_BARRIER refs_barriers[3]; > + > + for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) > + refs_barriers[i] = > TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], > + > D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, > + > D3D12_RESOURCE_STATE_COMMON); > + > + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, > d3d12_refs.NumTexture2Ds, > + refs_barriers); > + } > + > + barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, > + D3D12_RESOURCE_STATE_COMMON); > + barriers[1] = TRANSITION_BARRIER(pic->output_buffer, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, > + D3D12_RESOURCE_STATE_COMMON); > + barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, > + D3D12_RESOURCE_STATE_COMMON); > + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, > + D3D12_RESOURCE_STATE_COMMON); > + barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, > + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, > + D3D12_RESOURCE_STATE_COMMON); > + > + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); > + > + hr = ID3D12VideoEncodeCommandList2_Close(cmd_list); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12CommandQueue_Wait(ctx->command_queue, pic->input_surface- > >sync_ctx.fence, > + pic->input_surface->sync_ctx.fence_value); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, > (ID3D12CommandList **)&ctx->command_list); > + > + hr = ID3D12CommandQueue_Signal(ctx->command_queue, pic->input_surface- > >sync_ctx.fence, > + ++pic->input_surface- > >sync_ctx.fence_value); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, > ++ctx->sync_ctx.fence_value); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx- > >sync_ctx.fence_value); > + if (err < 0) > + goto fail; > + > + pic->fence_value = ctx->sync_ctx.fence_value; > + > + if (d3d12_refs.ppTexture2Ds) > + av_freep(&d3d12_refs.ppTexture2Ds); > + > + return 0; > + > +fail: > + if (command_allocator) > + d3d12va_discard_command_allocator(avctx, command_allocator, ctx- > >sync_ctx.fence_value); > + > + if (d3d12_refs.ppTexture2Ds) > + av_freep(&d3d12_refs.ppTexture2Ds); > + > + if (ctx->codec->free_picture_params) > + ctx->codec->free_picture_params(pic); > + > + av_buffer_unref(&pic->output_buffer_ref); > + pic->output_buffer = NULL; > + D3D12_OBJECT_RELEASE(pic->encoded_metadata); > + D3D12_OBJECT_RELEASE(pic->resolved_metadata); > + return err; > +} > + > +static int d3d12va_encode_discard(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic) > +{ > + HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; > + d3d12va_encode_wait(avctx, pic); > + > + if (pic->output_buffer_ref) { > + av_log(avctx, AV_LOG_DEBUG, "Discard output for pic " > + "%"PRId64"/%"PRId64".\n", > + base_pic->display_order, base_pic->encode_order); > + > + av_buffer_unref(&pic->output_buffer_ref); > + pic->output_buffer = NULL; > + } > + > + D3D12_OBJECT_RELEASE(pic->encoded_metadata); > + D3D12_OBJECT_RELEASE(pic->resolved_metadata); > + > + return 0; > +} > + > +static int d3d12va_encode_free_rc_params(AVCodecContext *avctx) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + > + switch (ctx->rc.Mode) > + { > + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP: > + av_freep(&ctx->rc.ConfigParams.pConfiguration_CQP); > + break; > + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: > + av_freep(&ctx->rc.ConfigParams.pConfiguration_CBR); > + break; > + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: > + av_freep(&ctx->rc.ConfigParams.pConfiguration_VBR); > + break; > + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: > + av_freep(&ctx->rc.ConfigParams.pConfiguration_QVBR); > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static HWBaseEncodePicture *d3d12va_encode_alloc(AVCodecContext *avctx, > + const AVFrame *frame) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodePicture *pic; > + > + pic = av_mallocz(sizeof(*pic)); > + if (!pic) > + return NULL; > + > + if (ctx->codec->picture_priv_data_size > 0) { > + pic->base.priv_data = av_mallocz(ctx->codec->picture_priv_data_size); > + if (!pic->base.priv_data) { > + av_freep(&pic); > + return NULL; > + } > + } > + > + pic->input_surface = (AVD3D12VAFrame *)frame->data[0]; > + > + return (HWBaseEncodePicture *)pic; > +} > + > +static int d3d12va_encode_free(AVCodecContext *avctx, > + HWBaseEncodePicture *base_pic) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; > + > + if (base_pic->encode_issued) > + d3d12va_encode_discard(avctx, pic); > + > + if (ctx->codec->free_picture_params) > + ctx->codec->free_picture_params(pic); > + > + ff_hw_base_encode_free(avctx, base_pic); > + > + av_free(pic); > + > + return 0; > +} > + > +static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic, size_t > *size) > +{ > + D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; > + uint8_t *data; > + HRESULT hr; > + int err; > + > + hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void **)&data); > + if (FAILED(hr)) { > + err = AVERROR_UNKNOWN; > + return err; > + } > + > + 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; > + > + ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); > + > + return 0; > +} > + > +static int d3d12va_encode_get_coded_data(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic, AVPacket > *pkt) > +{ > + int err; > + uint8_t *ptr, *mapped_data; > + size_t total_size = 0; > + HRESULT hr; > + > + err = d3d12va_encode_get_buffer_size(avctx, pic, &total_size); > + if (err < 0) > + goto end; > + > + total_size += pic->header_size; > + av_log(avctx, AV_LOG_DEBUG, "Output buffer size %"PRId64"\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, total_size); > + > + ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); > + > +end: > + av_buffer_unref(&pic->output_buffer_ref); > + pic->output_buffer = NULL; > + return err; > +} > + > +static int d3d12va_encode_output(AVCodecContext *avctx, > + const HWBaseEncodePicture *base_pic, > AVPacket *pkt) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; > + AVPacket *pkt_ptr = pkt; > + int err; > + > + err = d3d12va_encode_wait(avctx, pic); > + if (err < 0) > + return err; > + > + err = d3d12va_encode_get_coded_data(avctx, pic, pkt); > + if (err < 0) > + return err; > + > + av_log(avctx, AV_LOG_DEBUG, "Output read for pic %"PRId64"/%"PRId64".\n", > + base_pic->display_order, base_pic->encode_order); > + > + ff_hw_base_encode_set_output_property(avctx, (HWBaseEncodePicture > *)base_pic, pkt_ptr, 0); > + > + return 0; > +} > + > +static int d3d12va_encode_set_profile(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + const D3D12VAEncodeProfile *profile; > + const AVPixFmtDescriptor *desc; > + int i, depth; > + > + desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); > + if (!desc) { > + av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n", > + base_ctx->input_frames->sw_format); > + return AVERROR(EINVAL); > + } > + > + depth = desc->comp[0].depth; > + for (i = 1; i < desc->nb_components; i++) { > + if (desc->comp[i].depth != depth) { > + av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n", > + desc->name); > + return AVERROR(EINVAL); > + } > + } > + av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n", > + desc->name); > + > + av_assert0(ctx->codec->profiles); > + for (i = 0; (ctx->codec->profiles[i].av_profile != > + AV_PROFILE_UNKNOWN); i++) { > + profile = &ctx->codec->profiles[i]; > + if (depth != profile->depth || > + desc->nb_components != profile->nb_components) > + continue; > + if (desc->nb_components > 1 && > + (desc->log2_chroma_w != profile->log2_chroma_w || > + desc->log2_chroma_h != profile->log2_chroma_h)) > + continue; > + if (avctx->profile != profile->av_profile && > + avctx->profile != AV_PROFILE_UNKNOWN) > + continue; > + > + ctx->profile = profile; > + break; > + } > + if (!ctx->profile) { > + av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n"); > + return AVERROR(ENOSYS); > + } > + > + avctx->profile = profile->av_profile; > + return 0; > +} > + > +static const D3D12VAEncodeRCMode d3d12va_encode_rc_modes[] = { > + // Bitrate Quality > + // | Maxrate | HRD/VBV > + { 0 }, // | | | | > + { RC_MODE_CQP, "CQP", 0, 0, 1, 0, 1, > D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP }, > + { RC_MODE_CBR, "CBR", 1, 0, 0, 1, 1, > D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR }, > + { RC_MODE_VBR, "VBR", 1, 1, 0, 1, 1, > D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR }, > + { RC_MODE_QVBR, "QVBR", 1, 1, 1, 1, 1, > D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR }, > +}; > + > +static int check_rate_control_support(AVCodecContext *avctx, const > D3D12VAEncodeRCMode *rc_mode) > +{ > + HRESULT hr; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_rc_mode = { > + .Codec = ctx->codec->d3d12_codec, > + }; > + > + if (!rc_mode->d3d12_mode) > + return 0; > + > + d3d12_rc_mode.IsSupported = 0; > + d3d12_rc_mode.RateControlMode = rc_mode->d3d12_mode; > + > + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, > + > D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE, > + &d3d12_rc_mode, > sizeof(d3d12_rc_mode)); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to check rate control > support.\n"); > + return 0; > + } > + > + return d3d12_rc_mode.IsSupported; > +} > + > +static int d3d12va_encode_init_rate_control(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + int64_t rc_target_bitrate; > + int64_t rc_peak_bitrate; > + int rc_quality; > + int64_t hrd_buffer_size; > + int64_t hrd_initial_buffer_fullness; > + int fr_num, fr_den; > + const D3D12VAEncodeRCMode *rc_mode; > + > + // Rate control mode selection: > + // * If the user has set a mode explicitly with the rc_mode option, > + // use it and fail if it is not available. > + // * If an explicit QP option has been set, use CQP. > + // * If the codec is CQ-only, use CQP. > + // * If the QSCALE avcodec option is set, use CQP. > + // * If bitrate and quality are both set, try QVBR. > + // * If quality is set, try CQP. > + // * If bitrate and maxrate are set and have the same value, try CBR. > + // * If a bitrate is set, try VBR, then CBR. > + // * If no bitrate is set, try CQP. > + > +#define TRY_RC_MODE(mode, fail) do { \ > + rc_mode = &d3d12va_encode_rc_modes[mode]; \ > + if (!(rc_mode->d3d12_mode && check_rate_control_support(avctx, > rc_mode))) { \ > + if (fail) { \ > + av_log(avctx, AV_LOG_ERROR, "Driver does not support %s " \ > + "RC mode.\n", rc_mode->name); \ > + return AVERROR(EINVAL); \ > + } \ > + av_log(avctx, AV_LOG_DEBUG, "Driver does not support %s " \ > + "RC mode.\n", rc_mode->name); \ > + rc_mode = NULL; \ > + } else { \ > + goto rc_mode_found; \ > + } \ > + } while (0) > + > + if (base_ctx->explicit_rc_mode) > + TRY_RC_MODE(base_ctx->explicit_rc_mode, 1); > + > + if (base_ctx->explicit_qp) > + TRY_RC_MODE(RC_MODE_CQP, 1); > + > + if (ctx->codec->flags & FLAG_CONSTANT_QUALITY_ONLY) > + TRY_RC_MODE(RC_MODE_CQP, 1); > + > + if (avctx->flags & AV_CODEC_FLAG_QSCALE) > + TRY_RC_MODE(RC_MODE_CQP, 1); > + > + if (avctx->bit_rate > 0 && avctx->global_quality > 0) > + TRY_RC_MODE(RC_MODE_QVBR, 0); > + > + if (avctx->global_quality > 0) { > + TRY_RC_MODE(RC_MODE_CQP, 0); > + } > + > + if (avctx->bit_rate > 0 && avctx->rc_max_rate == avctx->bit_rate) > + TRY_RC_MODE(RC_MODE_CBR, 0); > + > + if (avctx->bit_rate > 0) { > + TRY_RC_MODE(RC_MODE_VBR, 0); > + TRY_RC_MODE(RC_MODE_CBR, 0); > + } else { > + TRY_RC_MODE(RC_MODE_CQP, 0); > + } > + > + av_log(avctx, AV_LOG_ERROR, "Driver does not support any " > + "RC mode compatible with selected options.\n"); > + return AVERROR(EINVAL); > + > +rc_mode_found: > + if (rc_mode->bitrate) { > + if (avctx->bit_rate <= 0) { > + av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s " > + "RC mode.\n", rc_mode->name); > + return AVERROR(EINVAL); > + } > + > + if (rc_mode->maxrate) { > + if (avctx->rc_max_rate > 0) { > + if (avctx->rc_max_rate < avctx->bit_rate) { > + av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: " > + "bitrate (%"PRId64") must not be greater than " > + "maxrate (%"PRId64").\n", avctx->bit_rate, > + avctx->rc_max_rate); > + return AVERROR(EINVAL); > + } > + rc_target_bitrate = avctx->bit_rate; > + rc_peak_bitrate = avctx->rc_max_rate; > + } else { > + // We only have a target bitrate, but this mode requires > + // that a maximum rate be supplied as well. Since the > + // user does not want this to be a constraint, arbitrarily > + // pick a maximum rate of double the target rate. > + rc_target_bitrate = avctx->bit_rate; > + rc_peak_bitrate = 2 * avctx->bit_rate; > + } > + } else { > + if (avctx->rc_max_rate > avctx->bit_rate) { > + av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored " > + "in %s RC mode.\n", rc_mode->name); > + } > + rc_target_bitrate = avctx->bit_rate; > + rc_peak_bitrate = 0; > + } > + } else { > + rc_target_bitrate = 0; > + rc_peak_bitrate = 0; > + } > + > + if (rc_mode->quality) { > + if (base_ctx->explicit_qp) { > + rc_quality = base_ctx->explicit_qp; > + } else if (avctx->global_quality > 0) { > + rc_quality = avctx->global_quality; When AV_CODEC_FLAG_QSCALE is set, avctx->global_quality is lambda, you'd convert lambda to qp. > + } else { > + rc_quality = ctx->codec->default_quality; > + av_log(avctx, AV_LOG_WARNING, "No quality level set; " > + "using default (%d).\n", rc_quality); > + } > + } else { > + rc_quality = 0; > + } > + > + if (rc_mode->hrd) { > + if (avctx->rc_buffer_size) > + hrd_buffer_size = avctx->rc_buffer_size; > + else if (avctx->rc_max_rate > 0) > + hrd_buffer_size = avctx->rc_max_rate; > + else > + hrd_buffer_size = avctx->bit_rate; > + if (avctx->rc_initial_buffer_occupancy) { > + if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) { > + av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: " > + "must have initial buffer size (%d) <= " > + "buffer size (%"PRId64").\n", > + avctx->rc_initial_buffer_occupancy, hrd_buffer_size); > + return AVERROR(EINVAL); > + } > + hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; > + } else { > + hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; > + } > + } else { > + if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) { > + av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored " > + "in %s RC mode.\n", rc_mode->name); > + } > + > + hrd_buffer_size = 0; > + hrd_initial_buffer_fullness = 0; > + } > + > + if (rc_target_bitrate > UINT32_MAX || > + hrd_buffer_size > UINT32_MAX || > + hrd_initial_buffer_fullness > UINT32_MAX) { > + av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or " > + "greater are not supported by D3D12.\n"); > + return AVERROR(EINVAL); > + } > + > + base_ctx->rc_quality = rc_quality; > + > + av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name); > + > + if (rc_mode->quality) > + av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_quality); > + > + if (rc_mode->hrd) { > + av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, " > + "initial fullness %"PRId64" bits.\n", > + hrd_buffer_size, hrd_initial_buffer_fullness); > + } > + > + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) > + av_reduce(&fr_num, &fr_den, > + avctx->framerate.num, avctx->framerate.den, 65535); > + else > + av_reduce(&fr_num, &fr_den, > + avctx->time_base.den, avctx->time_base.num, 65535); > + > + av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n", > + fr_num, fr_den, (double)fr_num / fr_den); > + > + ctx->rc.Flags = > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE; > + ctx->rc.TargetFrameRate.Numerator = fr_num; > + ctx->rc.TargetFrameRate.Denominator = fr_den; > + ctx->rc.Mode = rc_mode->d3d12_mode; > + > + switch (rc_mode->mode) { > + case RC_MODE_CQP: > + // cqp ConfigParams will be updated in ctx->codec->configure. > + break; > + > + case RC_MODE_CBR: > + D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR *cbr_ctl; > + > + ctx->rc.ConfigParams.DataSize = > sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR); > + cbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); > + if (!cbr_ctl) > + return AVERROR(ENOMEM); > + > + cbr_ctl->TargetBitRate = rc_target_bitrate; > + cbr_ctl->VBVCapacity = hrd_buffer_size; > + cbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; > + ctx->rc.Flags |= > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; > + > + if (avctx->qmin > 0 || avctx->qmax > 0) { > + cbr_ctl->MinQP = avctx->qmin; > + cbr_ctl->MaxQP = avctx->qmax; > + ctx->rc.Flags |= > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; > + } > + > + ctx->rc.ConfigParams.pConfiguration_CBR = cbr_ctl; > + break; > + > + case RC_MODE_VBR: > + D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR *vbr_ctl; > + > + ctx->rc.ConfigParams.DataSize = > sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR); > + vbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); > + if (!vbr_ctl) > + return AVERROR(ENOMEM); > + > + vbr_ctl->TargetAvgBitRate = rc_target_bitrate; > + vbr_ctl->PeakBitRate = rc_peak_bitrate; > + vbr_ctl->VBVCapacity = hrd_buffer_size; > + vbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; > + ctx->rc.Flags |= > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; > + > + if (avctx->qmin > 0 || avctx->qmax > 0) { > + vbr_ctl->MinQP = avctx->qmin; > + vbr_ctl->MaxQP = avctx->qmax; > + ctx->rc.Flags |= > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; > + } > + > + ctx->rc.ConfigParams.pConfiguration_VBR = vbr_ctl; > + break; > + > + case RC_MODE_QVBR: > + D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR *qvbr_ctl; > + > + ctx->rc.ConfigParams.DataSize = > sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR); > + qvbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); > + if (!qvbr_ctl) > + return AVERROR(ENOMEM); > + > + qvbr_ctl->TargetAvgBitRate = rc_target_bitrate; > + qvbr_ctl->PeakBitRate = rc_peak_bitrate; > + qvbr_ctl->ConstantQualityTarget = rc_quality; > + > + if (avctx->qmin > 0 || avctx->qmax > 0) { > + qvbr_ctl->MinQP = avctx->qmin; > + qvbr_ctl->MaxQP = avctx->qmax; > + ctx->rc.Flags |= > D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; > + } > + > + ctx->rc.ConfigParams.pConfiguration_QVBR = qvbr_ctl; > + break; > + > + default: > + break; > + } > + return 0; > +} > + > +static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + uint32_t ref_l0, ref_l1; > + int err; > + HRESULT hr; > + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT support; > + union { > + D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 h264; > + D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_HEVC hevc; > + } codec_support; > + > + support.NodeIndex = 0; > + support.Codec = ctx->codec->d3d12_codec; > + support.Profile = ctx->profile->d3d12_profile; > + > + switch (ctx->codec->d3d12_codec) { > + case D3D12_VIDEO_ENCODER_CODEC_H264: > + support.PictureSupport.DataSize = sizeof(codec_support.h264); > + support.PictureSupport.pH264Support = &codec_support.h264; > + break; > + > + case D3D12_VIDEO_ENCODER_CODEC_HEVC: > + support.PictureSupport.DataSize = sizeof(codec_support.hevc); > + support.PictureSupport.pHEVCSupport = &codec_support.hevc; > + break; > + } > + > + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, > D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT, > + &support, sizeof(support)); > + if (FAILED(hr)) > + return AVERROR(EINVAL); > + > + if (support.IsSupported) { > + switch (ctx->codec->d3d12_codec) { > + case D3D12_VIDEO_ENCODER_CODEC_H264: > + ref_l0 = FFMIN(support.PictureSupport.pH264Support- > >MaxL0ReferencesForP, > + support.PictureSupport.pH264Support- > >MaxL1ReferencesForB); > + ref_l1 = support.PictureSupport.pH264Support- > >MaxL1ReferencesForB; > + break; > + > + case D3D12_VIDEO_ENCODER_CODEC_HEVC: > + ref_l0 = FFMIN(support.PictureSupport.pHEVCSupport- > >MaxL0ReferencesForP, > + support.PictureSupport.pHEVCSupport- > >MaxL1ReferencesForB); > + ref_l1 = support.PictureSupport.pHEVCSupport- > >MaxL1ReferencesForB; > + break; > + } > + } else { > + ref_l0 = ref_l1 = 0; > + } > + > + if (ref_l0 > 0 && ref_l1 > 0 && ctx->bi_not_empty) { > + base_ctx->p_to_gpb = 1; > + av_log(avctx, AV_LOG_VERBOSE, "Driver does not support P-frames, " > + "replacing them with B-frames.\n"); > + } > + > + err = ff_hw_base_init_gop_structure(avctx, ref_l0, ref_l1, ctx->codec- > >flags, 0); > + if (err < 0) > + return err; > + > + return 0; > +} > + > +static int d3d12va_create_encoder(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; > + HRESULT hr; > + > + D3D12_VIDEO_ENCODER_DESC desc = { > + .NodeMask = 0, > + .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, > + .EncodeCodec = ctx->codec->d3d12_codec, > + .EncodeProfile = ctx->profile->d3d12_profile, > + .InputFormat = frames_hwctx->format, > + .CodecConfiguration = ctx->codec_conf, > + .MaxMotionEstimationPrecision = > D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM, > + }; > + > + hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, > &IID_ID3D12VideoEncoder, > + (void **)&ctx->encoder); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create encoder.\n"); > + return AVERROR(EINVAL); > + } > + > + return 0; > +} > + > +static int d3d12va_create_encoder_heap(AVCodecContext* avctx) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + HRESULT hr; > + > + D3D12_VIDEO_ENCODER_HEAP_DESC desc = { > + .NodeMask = 0, > + .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, > + .EncodeCodec = ctx->codec->d3d12_codec, > + .EncodeProfile = ctx->profile->d3d12_profile, > + .EncodeLevel = ctx->level, > + .ResolutionsListCount = 1, > + .pResolutionList = &ctx->resolution, > + }; > + > + hr = ID3D12VideoDevice3_CreateVideoEncoderHeap(ctx->video_device3, &desc, > + > &IID_ID3D12VideoEncoderHeap, (void **)&ctx->encoder_heap); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create encoder heap.\n"); > + return AVERROR(EINVAL); > + } > + > + return 0; > +} > + > +static void d3d12va_encode_free_buffer(void *opaque, uint8_t *data) > +{ > + ID3D12Resource *pResource; > + > + pResource = (ID3D12Resource *)data; > + D3D12_OBJECT_RELEASE(pResource); > +} > + > +static AVBufferRef *d3d12va_encode_alloc_output_buffer(void *opaque, size_t > size) > +{ > + AVCodecContext *avctx = opaque; > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + ID3D12Resource *pResource = NULL; > + HRESULT hr; > + AVBufferRef *ref; > + D3D12_HEAP_PROPERTIES heap_props; > + D3D12_HEAP_TYPE heap_type = D3D12_HEAP_TYPE_READBACK; > + > + D3D12_RESOURCE_DESC desc = { > + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, > + .Alignment = 0, > + .Width = FFALIGN(3 * base_ctx->surface_width * base_ctx- > >surface_height + (1 << 16), > + D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT), > + .Height = 1, > + .DepthOrArraySize = 1, > + .MipLevels = 1, > + .Format = DXGI_FORMAT_UNKNOWN, > + .SampleDesc = { .Count = 1, .Quality = 0 }, > + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, > + .Flags = D3D12_RESOURCE_FLAG_NONE, > + }; > + > + ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, > &heap_props, 0, heap_type); > + > + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, > &heap_props, D3D12_HEAP_FLAG_NONE, > + &desc, > D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, > + (void **)&pResource); > + > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create d3d12 buffer.\n"); > + return NULL; > + } > + > + ref = av_buffer_create((uint8_t *)(uintptr_t)pResource, > + sizeof(pResource), > + &d3d12va_encode_free_buffer, > + avctx, AV_BUFFER_FLAG_READONLY); > + if (!ref) { > + D3D12_OBJECT_RELEASE(pResource); > + return NULL; > + } > + > + return ref; > +} > + > +static int d3d12va_encode_prepare_output_buffers(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + AVD3D12VAFramesContext *frames_ctx = base_ctx->input_frames->hwctx; > + HRESULT hr; > + > + ctx->req.NodeIndex = 0; > + ctx->req.Codec = ctx->codec->d3d12_codec; > + ctx->req.Profile = ctx->profile->d3d12_profile; > + ctx->req.InputFormat = frames_ctx->format; > + ctx->req.PictureTargetResolution = ctx->resolution; > + > + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, > + > D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS, > + &ctx->req, sizeof(ctx->req)); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to check encoder resource > requirements support.\n"); > + return AVERROR(EINVAL); > + } > + > + if (!ctx->req.IsSupported) { > + av_log(avctx, AV_LOG_ERROR, "Encoder resource requirements > unsupported.\n"); > + return AVERROR(EINVAL); > + } > + > + ctx->output_buffer_pool = av_buffer_pool_init2(sizeof(ID3D12Resource *), > avctx, > + > &d3d12va_encode_alloc_output_buffer, NULL); > + if (!ctx->output_buffer_pool) > + return AVERROR(ENOMEM); > + > + return 0; > +} > + > +static int d3d12va_encode_create_command_objects(AVCodecContext *avctx) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + ID3D12CommandAllocator *command_allocator = NULL; > + int err; > + HRESULT hr; > + > + D3D12_COMMAND_QUEUE_DESC queue_desc = { > + .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, > + .Priority = 0, > + .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, > + .NodeMask = 0, > + }; > + > + ctx->allocator_queue = av_fifo_alloc2(D3D12VA_VIDEO_ENC_ASYNC_DEPTH, > + sizeof(CommandAllocator), > AV_FIFO_FLAG_AUTO_GROW); > + if (!ctx->allocator_queue) > + return AVERROR(ENOMEM); > + > + hr = ID3D12Device_CreateFence(ctx->hwctx->device, 0, > D3D12_FENCE_FLAG_NONE, > + &IID_ID3D12Fence, (void **)&ctx- > >sync_ctx.fence); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create fence(%lx)\n", > (long)hr); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); > + if (!ctx->sync_ctx.event) > + goto fail; > + > + err = d3d12va_get_valid_command_allocator(avctx, &command_allocator); > + if (err < 0) > + goto fail; > + > + hr = ID3D12Device_CreateCommandQueue(ctx->hwctx->device, &queue_desc, > + &IID_ID3D12CommandQueue, (void > **)&ctx->command_queue); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create command queue(%lx)\n", > (long)hr); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12Device_CreateCommandList(ctx->hwctx->device, 0, > queue_desc.Type, > + command_allocator, NULL, > &IID_ID3D12CommandList, > + (void **)&ctx->command_list); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to create command list(%lx)\n", > (long)hr); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12VideoEncodeCommandList2_Close(ctx->command_list); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "Failed to close the command > list(%lx)\n", (long)hr); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, > (ID3D12CommandList **)&ctx->command_list); > + > + err = d3d12va_sync_with_gpu(avctx); > + if (err < 0) > + goto fail; > + > + err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx- > >sync_ctx.fence_value); > + if (err < 0) > + goto fail; > + > + return 0; > + > +fail: > + D3D12_OBJECT_RELEASE(command_allocator); > + return err; > +} > + > +static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + AVD3D12VAFramesContext *hwctx; > + enum AVPixelFormat recon_format; > + int err; > + > + err = ff_hw_base_get_recon_format(avctx, NULL, &recon_format); > + if (err < 0) > + return err; > + > + base_ctx->recon_frames_ref = av_hwframe_ctx_alloc(base_ctx->device_ref); > + if (!base_ctx->recon_frames_ref) > + return AVERROR(ENOMEM); > + > + base_ctx->recon_frames = (AVHWFramesContext *)base_ctx->recon_frames_ref- > >data; > + hwctx = (AVD3D12VAFramesContext *)base_ctx->recon_frames->hwctx; > + > + base_ctx->recon_frames->format = AV_PIX_FMT_D3D12; > + base_ctx->recon_frames->sw_format = recon_format; > + base_ctx->recon_frames->width = base_ctx->surface_width; > + base_ctx->recon_frames->height = base_ctx->surface_height; > + > + hwctx->flags = D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | > + D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; > + > + err = av_hwframe_ctx_init(base_ctx->recon_frames_ref); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Failed to initialise reconstructed " > + "frame context: %d.\n", err); > + return err; > + } > + > + return 0; > +} > + > +static const HWEncodePictureOperation d3d12va_type = { > + .alloc = &d3d12va_encode_alloc, > + > + .issue = &d3d12va_encode_issue, > + > + .output = &d3d12va_encode_output, > + > + .free = &d3d12va_encode_free, > +}; > + > +int ff_d3d12va_encode_init(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT support = { 0 }; > + int err; > + HRESULT hr; > + > + err = ff_hw_base_encode_init(avctx); > + if (err < 0) > + goto fail; > + > + base_ctx->op = &d3d12va_type; > + > + ctx->hwctx = base_ctx->device->hwctx; > + > + ctx->resolution.Width = base_ctx->input_frames->width; > + ctx->resolution.Height = base_ctx->input_frames->height; > + > + hr = ID3D12Device_QueryInterface(ctx->hwctx->device, &IID_ID3D12Device3, > (void **)&ctx->device3); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "ID3D12Device3 interface is not > supported.\n"); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + hr = ID3D12Device3_QueryInterface(ctx->device3, &IID_ID3D12VideoDevice3, > (void **)&ctx->video_device3); > + if (FAILED(hr)) { > + av_log(avctx, AV_LOG_ERROR, "ID3D12VideoDevice3 interface is not > supported.\n"); > + err = AVERROR_UNKNOWN; > + goto fail; > + } > + > + if (FAILED(ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, > D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT, > + &support, > sizeof(support))) && !support.VideoEncodeSupport) { > + av_log(avctx, AV_LOG_ERROR, "D3D12 video device has no video encoder > support.\n"); > + err = AVERROR(EINVAL); > + goto fail; > + } > + > + err = d3d12va_encode_set_profile(avctx); > + if (err < 0) > + goto fail; > + > + if (ctx->codec->get_encoder_caps) { > + err = ctx->codec->get_encoder_caps(avctx); > + if (err < 0) > + goto fail; > + } > + > + err = d3d12va_encode_init_rate_control(avctx); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_init_gop_structure(avctx); > + if (err < 0) > + goto fail; > + > + if (!(ctx->codec->flags & FLAG_SLICE_CONTROL) && avctx->slices > 0) { > + av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested " > + "but this codec does not support controlling slices.\n"); > + } > + > + err = d3d12va_encode_create_command_objects(avctx); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_create_recon_frames(avctx); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_prepare_output_buffers(avctx); > + if (err < 0) > + goto fail; > + > + if (ctx->codec->configure) { > + err = ctx->codec->configure(avctx); > + if (err < 0) > + goto fail; > + } > + > + if (ctx->codec->init_sequence_params) { > + err = ctx->codec->init_sequence_params(avctx); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Codec sequence initialisation " > + "failed: %d.\n", err); > + goto fail; > + } > + } > + > + if (ctx->codec->set_level) { > + err = ctx->codec->set_level(avctx); > + if (err < 0) > + goto fail; > + } > + > + base_ctx->output_delay = base_ctx->b_per_p; > + base_ctx->decode_delay = base_ctx->max_b_depth; > + > + err = d3d12va_create_encoder(avctx); > + if (err < 0) > + goto fail; > + > + err = d3d12va_create_encoder_heap(avctx); > + if (err < 0) > + goto fail; > + > + base_ctx->async_encode = 1; > + base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth, > + sizeof(D3D12VAEncodePicture *), > 0); > + if (!base_ctx->encode_fifo) > + return AVERROR(ENOMEM); > + > + return 0; > + > +fail: > + return err; > +} > + > +int ff_d3d12va_encode_close(AVCodecContext *avctx) > +{ > + int num_allocator = 0; > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + HWBaseEncodePicture *pic, *next; > + CommandAllocator allocator; > + > + if (!base_ctx->frame) > + return 0; > + > + for (pic = base_ctx->pic_start; pic; pic = next) { > + next = pic->next; > + d3d12va_encode_free(avctx, pic); > + } > + > + d3d12va_encode_free_rc_params(avctx); > + > + av_buffer_pool_uninit(&ctx->output_buffer_pool); > + > + D3D12_OBJECT_RELEASE(ctx->command_list); > + D3D12_OBJECT_RELEASE(ctx->command_queue); > + > + if (ctx->allocator_queue) { > + while (av_fifo_read(ctx->allocator_queue, &allocator, 1) >= 0) { > + num_allocator++; > + D3D12_OBJECT_RELEASE(allocator.command_allocator); > + } > + > + av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators > reused: %d\n", num_allocator); > + } > + > + av_fifo_freep2(&ctx->allocator_queue); > + > + D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence); > + if (ctx->sync_ctx.event) > + CloseHandle(ctx->sync_ctx.event); > + > + D3D12_OBJECT_RELEASE(ctx->encoder_heap); > + D3D12_OBJECT_RELEASE(ctx->encoder); > + D3D12_OBJECT_RELEASE(ctx->video_device3); > + D3D12_OBJECT_RELEASE(ctx->device3); > + > + ff_hw_base_encode_close(avctx); > + > + return 0; > +} > diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h > new file mode 100644 > index 0000000000..10e2d87035 > --- /dev/null > +++ b/libavcodec/d3d12va_encode.h > @@ -0,0 +1,321 @@ > +/* > + * 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 > + */ > + > +#ifndef AVCODEC_D3D12VA_ENCODE_H > +#define AVCODEC_D3D12VA_ENCODE_H > + > +#include "libavutil/fifo.h" > +#include "libavutil/hwcontext.h" > +#include "libavutil/hwcontext_d3d12va_internal.h" > +#include "libavutil/hwcontext_d3d12va.h" > +#include "avcodec.h" > +#include "internal.h" > +#include "hwconfig.h" > +#include "hw_base_encode.h" > + > +struct D3D12VAEncodeType; > + > +extern const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[]; > + > +#define MAX_PARAM_BUFFER_SIZE 4096 > +#define D3D12VA_VIDEO_ENC_ASYNC_DEPTH 8 > + > +typedef struct D3D12VAEncodePicture { > + HWBaseEncodePicture base; > + > + int header_size; > + > + AVD3D12VAFrame *input_surface; > + AVD3D12VAFrame *recon_surface; > + > + AVBufferRef *output_buffer_ref; > + ID3D12Resource *output_buffer; > + > + ID3D12Resource *encoded_metadata; > + ID3D12Resource *resolved_metadata; > + > + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA pic_ctl; > + > + int fence_value; > +} D3D12VAEncodePicture; > + > +typedef struct D3D12VAEncodeProfile { > + /** > + * lavc profile value (AV_PROFILE_*). > + */ > + int av_profile; > + > + /** > + * Supported bit depth. > + */ > + int depth; > + > + /** > + * Number of components. > + */ > + int nb_components; > + > + /** > + * Chroma subsampling in width dimension. > + */ > + int log2_chroma_w; > + > + /** > + * Chroma subsampling in height dimension. > + */ > + int log2_chroma_h; > + > + /** > + * D3D12 profile value. > + */ > + D3D12_VIDEO_ENCODER_PROFILE_DESC d3d12_profile; > +} D3D12VAEncodeProfile; > + > +enum { > + RC_MODE_AUTO, > + RC_MODE_CQP, > + RC_MODE_CBR, > + RC_MODE_VBR, > + RC_MODE_QVBR, > + RC_MODE_MAX = RC_MODE_QVBR, > +}; > + > + > +typedef struct D3D12VAEncodeRCMode { > + /** > + * Mode from above enum (RC_MODE_*). > + */ > + int mode; > + > + /** > + * Name. > + * > + */ > + const char *name; > + > + /** > + * Uses bitrate parameters. > + * > + */ > + int bitrate; > + > + /** > + * Supports maxrate distinct from bitrate. > + * > + */ > + int maxrate; > + > + /** > + * Uses quality value. > + * > + */ > + int quality; > + > + /** > + * Supports HRD/VBV parameters. > + * > + */ > + int hrd; > + > + /** > + * Supported by D3D12 HW. > + */ > + int supported; > + > + /** > + * D3D12 mode value. > + */ > + D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_mode; > +} D3D12VAEncodeRCMode; > + > +typedef struct D3D12VAEncodeContext { > + HWBaseEncodeContext base; > + > + /** > + * Codec-specific hooks. > + */ > + const struct D3D12VAEncodeType *codec; > + > + /** > + * Chosen encoding profile details. > + */ > + const D3D12VAEncodeProfile *profile; > + > + AVD3D12VADeviceContext *hwctx; > + > + /** > + * ID3D12Device3 interface. > + */ > + ID3D12Device3 *device3; > + > + /** > + * ID3D12VideoDevice3 interface. > + */ > + ID3D12VideoDevice3 *video_device3; > + > + /** > + * Pool of (reusable) bitstream output buffers. > + */ > + AVBufferPool *output_buffer_pool; > + > + /** > + * D3D12 video encoder. > + */ > + AVBufferRef *encoder_ref; > + > + ID3D12VideoEncoder *encoder; > + > + /** > + * D3D12 video encoder heap. > + */ > + ID3D12VideoEncoderHeap *encoder_heap; > + > + /** > + * A cached queue for reusing the D3D12 command allocators. > + * > + * @see > https://learn.microsoft.com/en-us/windows/win32/direct3d12/recording-command-lists-and-bundles#id3d12commandallocator > + */ > + AVFifo *allocator_queue; > + > + /** > + * D3D12 command queue. > + */ > + ID3D12CommandQueue *command_queue; > + > + /** > + * D3D12 video encode command list. > + */ > + ID3D12VideoEncodeCommandList2 *command_list; > + > + /** > + * The sync context used to sync command queue. > + */ > + AVD3D12VASyncContext sync_ctx; > + > + /** > + * The bi_not_empty feature. > + */ > + int bi_not_empty; > + > + /** > + * D3D12_FEATURE structures. > + */ > + D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req; > + > + D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS res_limits; > + > + /** > + * D3D12_VIDEO_ENCODER structures. > + */ > + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolution; > + > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codec_conf; > + > + D3D12_VIDEO_ENCODER_RATE_CONTROL rc; > + > + D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop; > + > + D3D12_VIDEO_ENCODER_LEVEL_SETTING level; > +} D3D12VAEncodeContext; > + > +typedef struct D3D12VAEncodeType { > + /** > + * List of supported profiles. > + */ > + const D3D12VAEncodeProfile *profiles; > + > + /** > + * D3D12 codec name. > + */ > + D3D12_VIDEO_ENCODER_CODEC d3d12_codec; > + > + /** > + * Codec feature flags. > + */ > + int flags; > + > + /** > + * Default quality for this codec - used as quantiser or RC quality > + * factor depending on RC mode. > + */ > + int default_quality; > + > + /** > + * Query codec configuration and determine encode parameters like > + * block sizes for surface alignment and slices. If not set, assume > + * that all blocks are 16x16 and that surfaces should be aligned to match > + * this. > + */ > + int (*get_encoder_caps)(AVCodecContext *avctx); > + > + /** > + * Perform any extra codec-specific configuration. > + */ > + int (*configure)(AVCodecContext *avctx); > + > + /** > + * Set codec-specific level setting. > + */ > + int (*set_level)(AVCodecContext *avctx); > + > + /** > + * The size of any private data structure associated with each > + * picture (can be zero if not required). > + */ > + size_t picture_priv_data_size; > + > + /** > + * Fill the corresponding parameters. > + */ > + int (*init_sequence_params)(AVCodecContext *avctx); > + > + int (*init_picture_params)(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic); > + > + void (*free_picture_params)(D3D12VAEncodePicture *pic); > + > + /** > + * Write the packed header data to the provided buffer. > + */ > + int (*write_sequence_header)(AVCodecContext *avctx, > + char *data, size_t *data_len); > +} D3D12VAEncodeType; > + > +int ff_d3d12va_encode_init(AVCodecContext *avctx); > +int ff_d3d12va_encode_close(AVCodecContext *avctx); > + > +#define D3D12VA_ENCODE_RC_MODE(name, desc) \ > + { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \ > + 0, 0, FLAGS, .unit = "rc_mode" } > +#define D3D12VA_ENCODE_RC_OPTIONS \ > + { "rc_mode",\ > + "Set rate control mode", \ > + OFFSET(common.base.explicit_rc_mode), AV_OPT_TYPE_INT, \ > + { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, FLAGS, .unit = > "rc_mode" }, \ > + { "auto", "Choose mode automatically based on other parameters", \ > + 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, .unit = > "rc_mode" }, \ > + D3D12VA_ENCODE_RC_MODE(CQP, "Constant-quality"), \ > + D3D12VA_ENCODE_RC_MODE(CBR, "Constant-bitrate"), \ > + D3D12VA_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \ > + D3D12VA_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate") > + > +#endif /* AVCODEC_D3D12VA_ENCODE_H */ > diff --git a/libavcodec/d3d12va_encode_hevc.c > b/libavcodec/d3d12va_encode_hevc.c > new file mode 100644 > index 0000000000..aec0d9dcec > --- /dev/null > +++ b/libavcodec/d3d12va_encode_hevc.c > @@ -0,0 +1,957 @@ > +/* > + * 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/pixdesc.h" > +#include "libavutil/hwcontext_d3d12va_internal.h" > + > +#include "avcodec.h" > +#include "cbs.h" > +#include "cbs_h265.h" > +#include "h2645data.h" > +#include "h265_profile_level.h" > +#include "codec_internal.h" > +#include "d3d12va_encode.h" > + > +typedef struct D3D12VAEncodeHEVCPicture { > + int pic_order_cnt; > + int64_t last_idr_frame; > +} D3D12VAEncodeHEVCPicture; > + > +typedef struct D3D12VAEncodeHEVCContext { > + D3D12VAEncodeContext common; > + > + // User options. > + int qp; > + int profile; > + int tier; > + int level; > + > + // Writer structures. > + H265RawVPS raw_vps; > + H265RawSPS raw_sps; > + H265RawPPS raw_pps; > + > + CodedBitstreamContext *cbc; > + CodedBitstreamFragment current_access_unit; > +} D3D12VAEncodeHEVCContext; > + > +typedef struct D3D12VAEncodeHEVCLevel { > + int level; > + D3D12_VIDEO_ENCODER_LEVELS_HEVC d3d12_level; > +} D3D12VAEncodeHEVCLevel; > + > +static const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC > hevc_config_support_sets[] = > +{ > + { > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, > + 3, > + 3, > + }, > + { > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, > + 0, > + 0, > + }, > + { > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, > + 2, > + 2, > + }, > + { > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, > + 2, > + 2, > + }, > + { > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, > + 4, > + 4, > + }, > +}; > + > +static const D3D12VAEncodeHEVCLevel hevc_levels[] = { > + { 30, D3D12_VIDEO_ENCODER_LEVELS_HEVC_1 }, > + { 60, D3D12_VIDEO_ENCODER_LEVELS_HEVC_2 }, > + { 63, D3D12_VIDEO_ENCODER_LEVELS_HEVC_21 }, > + { 90, D3D12_VIDEO_ENCODER_LEVELS_HEVC_3 }, > + { 93, D3D12_VIDEO_ENCODER_LEVELS_HEVC_31 }, > + { 120, D3D12_VIDEO_ENCODER_LEVELS_HEVC_4 }, > + { 123, D3D12_VIDEO_ENCODER_LEVELS_HEVC_41 }, > + { 150, D3D12_VIDEO_ENCODER_LEVELS_HEVC_5 }, > + { 153, D3D12_VIDEO_ENCODER_LEVELS_HEVC_51 }, > + { 156, D3D12_VIDEO_ENCODER_LEVELS_HEVC_52 }, > + { 180, D3D12_VIDEO_ENCODER_LEVELS_HEVC_6 }, > + { 183, D3D12_VIDEO_ENCODER_LEVELS_HEVC_61 }, > + { 186, D3D12_VIDEO_ENCODER_LEVELS_HEVC_62 }, > +}; > + > +static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main = > D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; > +static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main10 = > D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN10; > + > +#define D3D_PROFILE_DESC(name) \ > + { sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), { .pHEVCProfile = > (D3D12_VIDEO_ENCODER_PROFILE_HEVC *)&profile_ ## name } } > +static const D3D12VAEncodeProfile d3d12va_encode_hevc_profiles[] = { > + { AV_PROFILE_HEVC_MAIN, 8, 3, 1, 1, D3D_PROFILE_DESC(main) }, > + { AV_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, D3D_PROFILE_DESC(main10) }, > + { AV_PROFILE_UNKNOWN }, > +}; > + > +static uint8_t > d3d12va_encode_hevc_map_cusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CU > SIZE cusize) > +{ > + switch (cusize) { > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8: > return 8; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_16x16: > return 16; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32: > return 32; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64: > return 64; > + default: av_assert0(0); > + } > + return 0; > +} > + > +static uint8_t > d3d12va_encode_hevc_map_tusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TU > SIZE tusize) > +{ > + switch (tusize) { > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4: > return 4; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_8x8: > return 8; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_16x16: > return 16; > + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32: > return 32; > + default: av_assert0(0); > + } > + return 0; > +} > + > +static int d3d12va_encode_hevc_write_access_unit(AVCodecContext *avctx, > + char *data, size_t > *data_len, > + CodedBitstreamFragment *au) > +{ > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + int err; > + > + err = ff_cbs_write_fragment_data(priv->cbc, au); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); > + return err; > + } > + > + if (*data_len < 8 * au->data_size - au->data_bit_padding) { > + av_log(avctx, AV_LOG_ERROR, "Access unit too large: " > + "%zu < %zu.\n", *data_len, > + 8 * au->data_size - au->data_bit_padding); > + return AVERROR(ENOSPC); > + } > + > + memcpy(data, au->data, au->data_size); > + *data_len = 8 * au->data_size - au->data_bit_padding; > + > + return 0; > +} > + > +static int d3d12va_encode_hevc_add_nal(AVCodecContext *avctx, > + CodedBitstreamFragment *au, > + void *nal_unit) > +{ > + H265RawNALUnitHeader *header = nal_unit; > + int err; > + > + err = ff_cbs_insert_unit_content(au, -1, > + header->nal_unit_type, nal_unit, NULL); > + if (err < 0) { > + av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: " > + "type = %d.\n", header->nal_unit_type); > + return err; > + } > + > + return 0; > +} > + > +static int d3d12va_encode_hevc_write_sequence_header(AVCodecContext *avctx, > + char *data, size_t > *data_len) > +{ > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + CodedBitstreamFragment *au = &priv->current_access_unit; > + int err; > + > + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_vps); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_sps); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_pps); > + if (err < 0) > + goto fail; > + > + err = d3d12va_encode_hevc_write_access_unit(avctx, data, data_len, au); > +fail: > + ff_cbs_fragment_reset(au); > + return err; > + > +} > + > +static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx; > + H265RawVPS *vps = &priv->raw_vps; > + H265RawSPS *sps = &priv->raw_sps; > + H265RawPPS *pps = &priv->raw_pps; > + H265RawProfileTierLevel *ptl = &vps->profile_tier_level; > + H265RawVUI *vui = &sps->vui; > + D3D12_VIDEO_ENCODER_PROFILE_HEVC profile = > D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; > + D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC level = { 0 }; > + const AVPixFmtDescriptor *desc; > + uint8_t min_cu_size, max_cu_size, min_tu_size, max_tu_size; > + int chroma_format, bit_depth; > + HRESULT hr; > + int i; > + > + D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = { > + .NodeIndex = 0, > + .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, > + .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 = MAX_DPB_SIZE - 1, > + .CodecConfiguration = ctx->codec_conf, > + .SuggestedProfile.DataSize = > sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), > + .SuggestedProfile.pHEVCProfile = &profile, > + .SuggestedLevel.DataSize = > sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC), > + .SuggestedLevel.pHEVCLevelSetting = &level, > + .pResolutionDependentSupport = &ctx->res_limits, > + }; > + > + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, > D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, > + &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 > features. %#x\n", > + support.ValidationFlags); > + return AVERROR(EINVAL); > + } > + > + if (support.SupportFlags & > D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) > { > + av_log(avctx, AV_LOG_ERROR, "D3D12 video encode on this device > requires texture array support, " > + "but it's not implemented.\n"); > + return AVERROR_PATCHWELCOME; > + } > + > + memset(vps, 0, sizeof(*vps)); > + memset(sps, 0, sizeof(*sps)); > + memset(pps, 0, sizeof(*pps)); > + > + desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); > + av_assert0(desc); > + if (desc->nb_components == 1) { > + chroma_format = 0; > + } else { > + if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) { > + chroma_format = 1; > + } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) { > + chroma_format = 2; > + } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) { > + chroma_format = 3; > + } else { > + av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format > " > + "%s is not supported.\n", desc->name); > + return AVERROR(EINVAL); > + } > + } > + bit_depth = desc->comp[0].depth; > + > + min_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig- > >MinLumaCodingUnitSize); > + max_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig- > >MaxLumaCodingUnitSize); > + min_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig- > >MinLumaTransformUnitSize); > + max_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig- > >MaxLumaTransformUnitSize); > + > + // VPS > + > + vps->nal_unit_header = (H265RawNALUnitHeader) { > + .nal_unit_type = HEVC_NAL_VPS, > + .nuh_layer_id = 0, > + .nuh_temporal_id_plus1 = 1, > + }; > + > + vps->vps_video_parameter_set_id = 0; > + > + vps->vps_base_layer_internal_flag = 1; > + vps->vps_base_layer_available_flag = 1; > + vps->vps_max_layers_minus1 = 0; > + vps->vps_max_sub_layers_minus1 = 0; > + vps->vps_temporal_id_nesting_flag = 1; > + > + ptl->general_profile_space = 0; > + ptl->general_profile_idc = avctx->profile; > + ptl->general_tier_flag = priv->tier; > + > + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; > + > + ptl->general_progressive_source_flag = 1; > + ptl->general_interlaced_source_flag = 0; > + ptl->general_non_packed_constraint_flag = 1; > + ptl->general_frame_only_constraint_flag = 1; > + > + ptl->general_max_14bit_constraint_flag = bit_depth <= 14; > + ptl->general_max_12bit_constraint_flag = bit_depth <= 12; > + ptl->general_max_10bit_constraint_flag = bit_depth <= 10; > + ptl->general_max_8bit_constraint_flag = bit_depth == 8; > + > + ptl->general_max_422chroma_constraint_flag = chroma_format <= 2; > + ptl->general_max_420chroma_constraint_flag = chroma_format <= 1; > + ptl->general_max_monochrome_constraint_flag = chroma_format == 0; > + > + ptl->general_intra_constraint_flag = base_ctx->gop_size == 1; > + ptl->general_one_picture_only_constraint_flag = 0; > + > + ptl->general_lower_bit_rate_constraint_flag = 1; > + > + if (avctx->level != FF_LEVEL_UNKNOWN) { > + ptl->general_level_idc = avctx->level; > + } else { > + const H265LevelDescriptor *level; > + > + level = ff_h265_guess_level(ptl, avctx->bit_rate, > + base_ctx->surface_width, base_ctx- > >surface_height, > + 1, 1, 1, (base_ctx->b_per_p > 0) + 1); > + if (level) { > + av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name); > + ptl->general_level_idc = level->level_idc; > + } else { > + av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to " > + "any normal level; using level 8.5.\n"); > + ptl->general_level_idc = 255; > + // The tier flag must be set in level 8.5. > + ptl->general_tier_flag = 1; > + } > + avctx->level = ptl->general_level_idc; > + } > + > + vps->vps_sub_layer_ordering_info_present_flag = 0; > + vps->vps_max_dec_pic_buffering_minus1[0] = base_ctx->max_b_depth + > 1; > + vps->vps_max_num_reorder_pics[0] = base_ctx->max_b_depth; > + vps->vps_max_latency_increase_plus1[0] = 0; > + > + vps->vps_max_layer_id = 0; > + vps->vps_num_layer_sets_minus1 = 0; > + vps->layer_id_included_flag[0][0] = 1; > + > + vps->vps_timing_info_present_flag = 0; > + > + // SPS > + > + sps->nal_unit_header = (H265RawNALUnitHeader) { > + .nal_unit_type = HEVC_NAL_SPS, > + .nuh_layer_id = 0, > + .nuh_temporal_id_plus1 = 1, > + }; > + > + sps->sps_video_parameter_set_id = vps->vps_video_parameter_set_id; > + > + sps->sps_max_sub_layers_minus1 = vps->vps_max_sub_layers_minus1; > + sps->sps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag; > + > + sps->profile_tier_level = vps->profile_tier_level; > + > + sps->sps_seq_parameter_set_id = 0; > + > + sps->chroma_format_idc = chroma_format; > + sps->separate_colour_plane_flag = 0; > + > + av_assert0(ctx->res_limits.SubregionBlockPixelsSize % min_cu_size == 0); > + > + sps->pic_width_in_luma_samples = FFALIGN(base_ctx->surface_width, > + ctx- > >res_limits.SubregionBlockPixelsSize); > + sps->pic_height_in_luma_samples = FFALIGN(base_ctx->surface_height, > + ctx- > >res_limits.SubregionBlockPixelsSize); > + > + if (avctx->width != sps->pic_width_in_luma_samples || > + avctx->height != sps->pic_height_in_luma_samples) { > + sps->conformance_window_flag = 1; > + sps->conf_win_left_offset = 0; > + sps->conf_win_right_offset = > + (sps->pic_width_in_luma_samples - avctx->width) >> desc- > >log2_chroma_w; > + sps->conf_win_top_offset = 0; > + sps->conf_win_bottom_offset = > + (sps->pic_height_in_luma_samples - avctx->height) >> desc- > >log2_chroma_h; > + } else { > + sps->conformance_window_flag = 0; > + } > + > + sps->bit_depth_luma_minus8 = bit_depth - 8; > + sps->bit_depth_chroma_minus8 = bit_depth - 8; > + > + sps->log2_max_pic_order_cnt_lsb_minus4 = ctx->gop.pHEVCGroupOfPictures- > >log2_max_pic_order_cnt_lsb_minus4; > + > + sps->sps_sub_layer_ordering_info_present_flag = > + vps->vps_sub_layer_ordering_info_present_flag; > + for (i = 0; i <= sps->sps_max_sub_layers_minus1; i++) { > + sps->sps_max_dec_pic_buffering_minus1[i] = > + vps->vps_max_dec_pic_buffering_minus1[i]; > + sps->sps_max_num_reorder_pics[i] = > + vps->vps_max_num_reorder_pics[i]; > + sps->sps_max_latency_increase_plus1[i] = > + vps->vps_max_latency_increase_plus1[i]; > + } > + > + sps->log2_min_luma_coding_block_size_minus3 = > (uint8_t)(av_log2(min_cu_size) - 3); > + sps->log2_diff_max_min_luma_coding_block_size = > (uint8_t)(av_log2(max_cu_size) - av_log2(min_cu_size)); > + sps->log2_min_luma_transform_block_size_minus2 = > (uint8_t)(av_log2(min_tu_size) - 2); > + sps->log2_diff_max_min_luma_transform_block_size = > (uint8_t)(av_log2(max_tu_size) - av_log2(min_tu_size)); > + > + sps->max_transform_hierarchy_depth_inter = ctx->codec_conf.pHEVCConfig- > >max_transform_hierarchy_depth_inter; > + sps->max_transform_hierarchy_depth_intra = ctx->codec_conf.pHEVCConfig- > >max_transform_hierarchy_depth_intra; > + > + sps->amp_enabled_flag = !!(ctx->codec_conf.pHEVCConfig- > >ConfigurationFlags & > + > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITI > ON); > + sps->sample_adaptive_offset_enabled_flag = !!(ctx- > >codec_conf.pHEVCConfig->ConfigurationFlags & > + > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER); > + sps->sps_temporal_mvp_enabled_flag = 0; > + sps->pcm_enabled_flag = 0; > + > + sps->vui_parameters_present_flag = 1; > + > + // vui default parameters > + vui->aspect_ratio_idc = 0; > + vui->video_format = 5; > + vui->video_full_range_flag = 0; > + vui->colour_primaries = 2; > + vui->transfer_characteristics = 2; > + vui->matrix_coefficients = 2; > + vui->chroma_sample_loc_type_top_field = 0; > + vui->chroma_sample_loc_type_bottom_field = 0; > + vui->tiles_fixed_structure_flag = 0; > + vui->motion_vectors_over_pic_boundaries_flag = 1; > + vui->min_spatial_segmentation_idc = 0; > + vui->max_bytes_per_pic_denom = 2; > + vui->max_bits_per_min_cu_denom = 1; > + vui->log2_max_mv_length_horizontal = 15; > + vui->log2_max_mv_length_vertical = 15; The input image might have different values. Thanks Haihao > + > + // PPS > + > + pps->nal_unit_header = (H265RawNALUnitHeader) { > + .nal_unit_type = HEVC_NAL_PPS, > + .nuh_layer_id = 0, > + .nuh_temporal_id_plus1 = 1, > + }; > + > + pps->pps_pic_parameter_set_id = 0; > + pps->pps_seq_parameter_set_id = sps->sps_seq_parameter_set_id; > + > + pps->cabac_init_present_flag = 1; > + > + pps->num_ref_idx_l0_default_active_minus1 = 0; > + pps->num_ref_idx_l1_default_active_minus1 = 0; > + > + pps->init_qp_minus26 = 0; > + > + pps->transform_skip_enabled_flag = !!(ctx->codec_conf.pHEVCConfig- > >ConfigurationFlags & > + > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING); > + > + // cu_qp_delta always required to be 1 in > https://github.com/microsoft/DirectX-Specs/blob/master/d3d/D3D12VideoEncoding.md > + pps->cu_qp_delta_enabled_flag = 1; > + > + pps->diff_cu_qp_delta_depth = 0; > + > + pps->pps_slice_chroma_qp_offsets_present_flag = 1; > + > + pps->tiles_enabled_flag = 0; // no tiling in D3D12 > + > + pps->pps_loop_filter_across_slices_enabled_flag = !(ctx- > >codec_conf.pHEVCConfig->ConfigurationFlags & > + > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_S > LICES); > + pps->deblocking_filter_control_present_flag = 1; > + > + return 0; > +} > + > +static int d3d12va_encode_hevc_get_encoder_caps(AVCodecContext *avctx) > +{ > + int i; > + HRESULT hr; > + uint8_t min_cu_size, max_cu_size; > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC *config; > + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC hevc_caps; > + > + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codec_caps = > { > + .NodeIndex = 0, > + .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, > + .Profile = ctx->profile->d3d12_profile, > + .CodecSupportLimits.DataSize = > sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC), > + }; > + > + for (i = 0; i < FF_ARRAY_ELEMS(hevc_config_support_sets); i++) { > + hevc_caps = hevc_config_support_sets[i]; > + codec_caps.CodecSupportLimits.pHEVCSupport = &hevc_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) > + break; > + } > + > + if (i == FF_ARRAY_ELEMS(hevc_config_support_sets)) { > + av_log(avctx, AV_LOG_ERROR, "Unsupported codec configuration\n"); > + return AVERROR(EINVAL); > + } > + > + ctx->codec_conf.DataSize = > sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC); > + ctx->codec_conf.pHEVCConfig = av_mallocz(ctx->codec_conf.DataSize); > + if (!ctx->codec_conf.pHEVCConfig) > + return AVERROR(ENOMEM); > + > + config = ctx->codec_conf.pHEVCConfig; > + > + config->ConfigurationFlags = > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_NONE; > + config->MinLumaCodingUnitSize = > hevc_caps.MinLumaCodingUnitSize; > + config->MaxLumaCodingUnitSize = > hevc_caps.MaxLumaCodingUnitSize; > + config->MinLumaTransformUnitSize = > hevc_caps.MinLumaTransformUnitSize; > + config->MaxLumaTransformUnitSize = > hevc_caps.MaxLumaTransformUnitSize; > + config->max_transform_hierarchy_depth_inter = > hevc_caps.max_transform_hierarchy_depth_inter; > + config->max_transform_hierarchy_depth_intra = > hevc_caps.max_transform_hierarchy_depth_intra; > + > + if (hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PAR > TITION_SUPPORT || > + hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PAR > TITION_REQUIRED) > + config->ConfigurationFlags |= > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITI > ON; > + > + if (hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_SAO_FILTER_SUPPORT) > + config->ConfigurationFlags |= > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER; > + > + if (hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_DISABLING_LOOP_FILTE > R_ACROSS_SLICES_SUPPORT) > + config->ConfigurationFlags |= > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_S > LICES; > + > + if (hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_SUPPO > RT) > + config->ConfigurationFlags |= > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING; > + > + if (hevc_caps.SupportFlags & > D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_P_FRAMES_IMPLEMENTED > _AS_LOW_DELAY_B_FRAMES) > + ctx->bi_not_empty = 1; > + > + // block sizes > + min_cu_size = > d3d12va_encode_hevc_map_cusize(hevc_caps.MinLumaCodingUnitSize); > + max_cu_size = > d3d12va_encode_hevc_map_cusize(hevc_caps.MaxLumaCodingUnitSize); > + > + av_log(avctx, AV_LOG_VERBOSE, "Using CTU size %dx%d, " > + "min CB size %dx%d.\n", max_cu_size, max_cu_size, > + min_cu_size, min_cu_size); > + > + base_ctx->surface_width = FFALIGN(avctx->width, min_cu_size); > + base_ctx->surface_height = FFALIGN(avctx->height, min_cu_size); > + > + return 0; > +} > + > +static int d3d12va_encode_hevc_configure(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + int fixed_qp_idr, fixed_qp_p, fixed_qp_b; > + int err; > + > + err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx); > + if (err < 0) > + return err; > + > + // Rate control > + if (ctx->rc.Mode == D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP) { > + D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP *cqp_ctl; > + fixed_qp_p = av_clip(base_ctx->rc_quality, 1, 51); > + if (avctx->i_quant_factor > 0.0) > + fixed_qp_idr = av_clip((avctx->i_quant_factor * fixed_qp_p + > + avctx->i_quant_offset) + 0.5, 1, 51); > + else > + fixed_qp_idr = fixed_qp_p; > + if (avctx->b_quant_factor > 0.0) > + fixed_qp_b = av_clip((avctx->b_quant_factor * fixed_qp_p + > + avctx->b_quant_offset) + 0.5, 1, 51); > + else > + fixed_qp_b = fixed_qp_p; > + > + av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " > + "%d / %d / %d for IDR- / P- / B-frames.\n", > + fixed_qp_idr, fixed_qp_p, fixed_qp_b); > + > + 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_idr; > + cqp_ctl->ConstantQP_InterPredictedFrame_PrevRefOnly = > fixed_qp_p; > + cqp_ctl->ConstantQP_InterPredictedFrame_BiDirectionalRef = > fixed_qp_b; > + > + ctx->rc.ConfigParams.pConfiguration_CQP = cqp_ctl; > + } > + > + // GOP > + ctx->gop.DataSize = > sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC); > + ctx->gop.pHEVCGroupOfPictures = av_mallocz(ctx->gop.DataSize); > + if (!ctx->gop.pHEVCGroupOfPictures) > + return AVERROR(ENOMEM); > + > + ctx->gop.pHEVCGroupOfPictures->GOPLength = base_ctx->gop_size; > + ctx->gop.pHEVCGroupOfPictures->PPicturePeriod = base_ctx->b_per_p + 1; > + // Power of 2 > + if (base_ctx->gop_size & base_ctx->gop_size - 1 == 0) > + ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 = > + FFMAX(av_log2(base_ctx->gop_size) - 4, 0); > + else > + ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 = > + FFMAX(av_log2(base_ctx->gop_size) - 3, 0); > + > + return 0; > +} > + > +static int d3d12va_encode_hevc_set_level(AVCodecContext *avctx) > +{ > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + int i; > + > + ctx->level.DataSize = > sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC); > + ctx->level.pHEVCLevelSetting = av_mallocz(ctx->level.DataSize); > + if (!ctx->level.pHEVCLevelSetting) > + return AVERROR(ENOMEM); > + > + for (i = 0; i < FF_ARRAY_ELEMS(hevc_levels); i++) { > + if (avctx->level == hevc_levels[i].level) { > + ctx->level.pHEVCLevelSetting->Level = hevc_levels[i].d3d12_level; > + break; > + } > + } > + > + if (i == FF_ARRAY_ELEMS(hevc_levels)) { > + av_log(avctx, AV_LOG_ERROR, "Invalid level %d.\n", avctx->level); > + return AVERROR(EINVAL); > + } > + > + ctx->level.pHEVCLevelSetting->Tier = priv- > >raw_vps.profile_tier_level.general_tier_flag == 0 ? > + D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN : > + D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH; > + > + return 0; > +} > + > +static void d3d12va_encode_hevc_free_picture_params(D3D12VAEncodePicture > *pic) > +{ > + if (!pic->pic_ctl.pHEVCPicData) > + return; > + > + av_freep(&pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames); > + av_freep(&pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames); > + av_freep(&pic->pic_ctl.pHEVCPicData- > >pReferenceFramesReconPictureDescriptors); > + av_freep(&pic->pic_ctl.pHEVCPicData); > +} > + > +static int d3d12va_encode_hevc_init_picture_params(AVCodecContext *avctx, > + D3D12VAEncodePicture *pic) > +{ > + HWBaseEncodePicture *base_pic = > (HWBaseEncodePicture *)pic; > + D3D12VAEncodeHEVCPicture *hpic = base_pic- > >priv_data; > + HWBaseEncodePicture *prev = base_pic- > >prev; > + D3D12VAEncodeHEVCPicture *hprev = prev ? prev- > >priv_data : NULL; > + D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_HEVC *pd = NULL; > + UINT *ref_list0 = NULL, > *ref_list1 = NULL; > + int i, idx = 0; > + > + pic->pic_ctl.DataSize = > sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC); > + pic->pic_ctl.pHEVCPicData = av_mallocz(pic->pic_ctl.DataSize); > + if (!pic->pic_ctl.pHEVCPicData) > + return AVERROR(ENOMEM); > + > + if (base_pic->type == PICTURE_TYPE_IDR) { > + av_assert0(base_pic->display_order == base_pic->encode_order); > + hpic->last_idr_frame = base_pic->display_order; > + } else { > + av_assert0(prev); > + hpic->last_idr_frame = hprev->last_idr_frame; > + } > + hpic->pic_order_cnt = base_pic->display_order - hpic->last_idr_frame; > + > + switch(base_pic->type) { > + case PICTURE_TYPE_IDR: > + pic->pic_ctl.pHEVCPicData->FrameType = > D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_IDR_FRAME; > + break; > + case PICTURE_TYPE_I: > + pic->pic_ctl.pHEVCPicData->FrameType = > D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_I_FRAME; > + break; > + case PICTURE_TYPE_P: > + pic->pic_ctl.pHEVCPicData->FrameType = > D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_P_FRAME; > + break; > + case PICTURE_TYPE_B: > + pic->pic_ctl.pHEVCPicData->FrameType = > D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_B_FRAME; > + break; > + default: > + av_assert0(0 && "invalid picture type"); > + } > + > + pic->pic_ctl.pHEVCPicData->slice_pic_parameter_set_id = 0; > + pic->pic_ctl.pHEVCPicData->PictureOrderCountNumber = hpic- > >pic_order_cnt; > + > + if (base_pic->type == PICTURE_TYPE_P || base_pic->type == PICTURE_TYPE_B) > { > + pd = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*pd)); > + if (!pd) > + return AVERROR(ENOMEM); > + > + ref_list0 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list0)); > + if (!ref_list0) > + return AVERROR(ENOMEM); > + > + pic->pic_ctl.pHEVCPicData->List0ReferenceFramesCount = base_pic- > >nb_refs[0]; > + for (i = 0; i < base_pic->nb_refs[0]; i++) { > + HWBaseEncodePicture *ref = base_pic->refs[0][i]; > + D3D12VAEncodeHEVCPicture *href; > + > + av_assert0(ref && ref->encode_order < base_pic->encode_order); > + href = ref->priv_data; > + > + ref_list0[i] = idx; > + pd[idx].ReconstructedPictureResourceIndex = idx; > + pd[idx].IsRefUsedByCurrentPic = TRUE; > + pd[idx].PictureOrderCountNumber = href->pic_order_cnt; > + idx++; > + } > + } > + > + if (base_pic->type == PICTURE_TYPE_B) { > + ref_list1 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list1)); > + if (!ref_list1) > + return AVERROR(ENOMEM); > + > + pic->pic_ctl.pHEVCPicData->List1ReferenceFramesCount = base_pic- > >nb_refs[1]; > + for (i = 0; i < base_pic->nb_refs[1]; i++) { > + HWBaseEncodePicture *ref = base_pic->refs[1][i]; > + D3D12VAEncodeHEVCPicture *href; > + > + av_assert0(ref && ref->encode_order < base_pic->encode_order); > + href = ref->priv_data; > + > + ref_list1[i] = idx; > + pd[idx].ReconstructedPictureResourceIndex = idx; > + pd[idx].IsRefUsedByCurrentPic = TRUE; > + pd[idx].PictureOrderCountNumber = href->pic_order_cnt; > + idx++; > + } > + } > + > + pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames = ref_list0; > + pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames = ref_list1; > + pic->pic_ctl.pHEVCPicData->ReferenceFramesReconPictureDescriptorsCount = > idx; > + pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors = pd; > + > + return 0; > +} > + > +static const D3D12VAEncodeType d3d12va_encode_type_hevc = { > + .profiles = d3d12va_encode_hevc_profiles, > + > + .d3d12_codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, > + > + .flags = FLAG_B_PICTURES | > + FLAG_B_PICTURE_REFERENCES | > + FLAG_NON_IDR_KEY_PICTURES, > + > + .default_quality = 25, > + > + .get_encoder_caps = &d3d12va_encode_hevc_get_encoder_caps, > + > + .configure = &d3d12va_encode_hevc_configure, > + > + .set_level = &d3d12va_encode_hevc_set_level, > + > + .picture_priv_data_size = sizeof(D3D12VAEncodeHEVCPicture), > + > + .init_sequence_params = &d3d12va_encode_hevc_init_sequence_params, > + > + .init_picture_params = &d3d12va_encode_hevc_init_picture_params, > + > + .free_picture_params = &d3d12va_encode_hevc_free_picture_params, > + > + .write_sequence_header = &d3d12va_encode_hevc_write_sequence_header, > +}; > + > +static int d3d12va_encode_hevc_init(AVCodecContext *avctx) > +{ > + HWBaseEncodeContext *base_ctx = avctx->priv_data; > + D3D12VAEncodeContext *ctx = avctx->priv_data; > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + > + ctx->codec = &d3d12va_encode_type_hevc; > + > + if (avctx->profile == AV_PROFILE_UNKNOWN) > + avctx->profile = priv->profile; > + if (avctx->level == FF_LEVEL_UNKNOWN) > + avctx->level = priv->level; > + > + if (avctx->level != FF_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) > + base_ctx->explicit_qp = priv->qp; > + > + return ff_d3d12va_encode_init(avctx); > +} > + > +static int d3d12va_encode_hevc_close(AVCodecContext *avctx) > +{ > + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; > + > + ff_cbs_fragment_free(&priv->current_access_unit); > + ff_cbs_close(&priv->cbc); > + > + av_freep(&priv->common.codec_conf.pHEVCConfig); > + av_freep(&priv->common.gop.pHEVCGroupOfPictures); > + av_freep(&priv->common.level.pHEVCLevelSetting); > + > + return ff_d3d12va_encode_close(avctx); > +} > + > +#define OFFSET(x) offsetof(D3D12VAEncodeHEVCContext, x) > +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) > +static const AVOption d3d12va_encode_hevc_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_HEVC_MAIN) }, > + { PROFILE("main10", AV_PROFILE_HEVC_MAIN_10) }, > + { PROFILE("rext", AV_PROFILE_HEVC_REXT) }, > +#undef PROFILE > + > + { "tier", "Set tier (general_tier_flag)", > + OFFSET(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 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, > + > +#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ > + { .i64 = value }, 0, 0, FLAGS, "level" > + { LEVEL("1", 30) }, > + { LEVEL("2", 60) }, > + { LEVEL("2.1", 63) }, > + { LEVEL("3", 90) }, > + { LEVEL("3.1", 93) }, > + { LEVEL("4", 120) }, > + { LEVEL("4.1", 123) }, > + { LEVEL("5", 150) }, > + { LEVEL("5.1", 153) }, > + { LEVEL("5.2", 156) }, > + { LEVEL("6", 180) }, > + { LEVEL("6.1", 183) }, > + { LEVEL("6.2", 186) }, > +#undef LEVEL > + > + { NULL }, > +}; > + > +static const FFCodecDefault d3d12va_encode_hevc_defaults[] = { > + { "b", "0" }, > + { "bf", "2" }, > + { "g", "120" }, > + { "i_qfactor", "1" }, > + { "i_qoffset", "0" }, > + { "b_qfactor", "1" }, > + { "b_qoffset", "0" }, > + { "qmin", "-1" }, > + { "qmax", "-1" }, > + { NULL }, > +}; > + > +static const AVClass d3d12va_encode_hevc_class = { > + .class_name = "hevc_d3d12va", > + .item_name = av_default_item_name, > + .option = d3d12va_encode_hevc_options, > + .version = LIBAVUTIL_VERSION_INT, > +}; > + > +const FFCodec ff_hevc_d3d12va_encoder = { > + .p.name = "hevc_d3d12va", > + CODEC_LONG_NAME("D3D12VA hevc encoder"), > + .p.type = AVMEDIA_TYPE_VIDEO, > + .p.id = AV_CODEC_ID_HEVC, > + .priv_data_size = sizeof(D3D12VAEncodeHEVCContext), > + .init = &d3d12va_encode_hevc_init, > + FF_CODEC_RECEIVE_PACKET_CB(&ff_hw_base_encode_receive_packet), > + .close = &d3d12va_encode_hevc_close, > + .p.priv_class = &d3d12va_encode_hevc_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_hevc_defaults, > + .p.pix_fmts = (const enum AVPixelFormat[]) { > + AV_PIX_FMT_D3D12, > + AV_PIX_FMT_NONE, > + }, > + .hw_configs = ff_d3d12va_encode_hw_configs, > + .p.wrapper_name = "d3d12va", > +};
diff --git a/configure b/configure index c34bdd13f5..53076fbf22 100755 --- a/configure +++ b/configure @@ -2570,6 +2570,7 @@ CONFIG_EXTRA=" tpeldsp vaapi_1 vaapi_encode + d3d12va_encode vc1dsp videodsp vp3dsp @@ -3214,6 +3215,7 @@ wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" # hardware-accelerated codecs +d3d12va_encode_deps="d3d12va ID3D12VideoEncoder d3d12_encoder_feature" mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" omx_deps="libdl pthreads" omx_rpi_select="omx" @@ -3280,6 +3282,7 @@ h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m" hevc_amf_encoder_deps="amf" hevc_cuvid_decoder_deps="cuvid" hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" +hevc_d3d12va_encoder_select="cbs_h265 d3d12va_encode" hevc_mediacodec_decoder_deps="mediacodec" hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" hevc_mediacodec_encoder_deps="mediacodec" @@ -6620,6 +6623,9 @@ check_type "windows.h d3d11.h" "ID3D11VideoDecoder" check_type "windows.h d3d11.h" "ID3D11VideoContext" check_type "windows.h d3d12.h" "ID3D12Device" 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 check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat diff --git a/libavcodec/Makefile b/libavcodec/Makefile index cbfae5f182..cdda3f0d0a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -84,6 +84,7 @@ OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o +OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o @@ -435,6 +436,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \ h274.o OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o @@ -1263,7 +1265,7 @@ SKIPHEADERS += %_tablegen.h \ SKIPHEADERS-$(CONFIG_AMF) += amfenc.h SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h -SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h +SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h d3d12va_encode.h SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h SKIPHEADERS-$(CONFIG_JNI) += ffjni.h SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 2386b450a6..7b5093233c 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -855,6 +855,7 @@ extern const FFCodec ff_h264_vaapi_encoder; extern const FFCodec ff_h264_videotoolbox_encoder; extern const FFCodec ff_hevc_amf_encoder; extern const FFCodec ff_hevc_cuvid_decoder; +extern const FFCodec ff_hevc_d3d12va_encoder; extern const FFCodec ff_hevc_mediacodec_decoder; extern const FFCodec ff_hevc_mediacodec_encoder; extern const FFCodec ff_hevc_mf_encoder; diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c new file mode 100644 index 0000000000..88a08efa76 --- /dev/null +++ b/libavcodec/d3d12va_encode.c @@ -0,0 +1,1550 @@ +/* + * 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/avassert.h" +#include "libavutil/common.h" +#include "libavutil/internal.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "libavutil/hwcontext_d3d12va.h" + +#include "avcodec.h" +#include "d3d12va_encode.h" +#include "encode.h" + +const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(D3D12, D3D12VA), + NULL, +}; + +static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx) +{ + uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence); + if (completion < psync_ctx->fence_value) { + if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event))) + return AVERROR(EINVAL); + + WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE); + } + + return 0; +} + +static int d3d12va_sync_with_gpu(AVCodecContext *avctx) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + + DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value)); + return d3d12va_fence_completion(&ctx->sync_ctx); + +fail: + return AVERROR(EINVAL); +} + +typedef struct CommandAllocator { + ID3D12CommandAllocator *command_allocator; + uint64_t fence_value; +} CommandAllocator; + +static int d3d12va_get_valid_command_allocator(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator) +{ + HRESULT hr; + D3D12VAEncodeContext *ctx = avctx->priv_data; + CommandAllocator allocator; + + if (av_fifo_peek(ctx->allocator_queue, &allocator, 1, 0) >= 0) { + uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence); + if (completion >= allocator.fence_value) { + *ppAllocator = allocator.command_allocator; + av_fifo_read(ctx->allocator_queue, &allocator, 1); + return 0; + } + } + + hr = ID3D12Device_CreateCommandAllocator(ctx->hwctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, + &IID_ID3D12CommandAllocator, (void **)ppAllocator); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int d3d12va_discard_command_allocator(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator, uint64_t fence_value) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + + CommandAllocator allocator = { + .command_allocator = pAllocator, + .fence_value = fence_value, + }; + + av_fifo_write(ctx->allocator_queue, &allocator, 1); + + return 0; +} + +static int d3d12va_encode_wait(AVCodecContext *avctx, + D3D12VAEncodePicture *pic) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; + uint64_t completion; + + av_assert0(base_pic->encode_issued); + + if (base_pic->encode_complete) { + // Already waited for this picture. + return 0; + } + + completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence); + if (completion < pic->fence_value) { + if (FAILED(ID3D12Fence_SetEventOnCompletion(ctx->sync_ctx.fence, pic->fence_value, + ctx->sync_ctx.event))) + return AVERROR(EINVAL); + + WaitForSingleObjectEx(ctx->sync_ctx.event, INFINITE, FALSE); + } + + av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" " + "(input surface %p).\n", base_pic->display_order, + base_pic->encode_order, pic->input_surface->texture); + + av_frame_free(&base_pic->input_image); + + base_pic->encode_complete = 1; + return 0; +} + +static int d3d12va_encode_create_metadata_buffers(AVCodecContext *avctx, + D3D12VAEncodePicture *pic) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + int width = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA); + 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; + + D3D12_RESOURCE_DESC meta_desc = { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = 0, + .Width = ctx->req.MaxEncoderOutputMetadataBufferSize, + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = { .Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE, + }; + + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &encoded_meta_props, D3D12_HEAP_FLAG_NONE, + &meta_desc, D3D12_RESOURCE_STATE_COMMON, NULL, + &IID_ID3D12Resource, (void **)&pic->encoded_metadata); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create metadata buffer.\n"); + return AVERROR_UNKNOWN; + } + + ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, &resolved_meta_props, 0, resolved_heap_type); + + meta_desc.Width = width; + + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &resolved_meta_props, D3D12_HEAP_FLAG_NONE, + &meta_desc, D3D12_RESOURCE_STATE_COMMON, NULL, + &IID_ID3D12Resource, (void **)&pic->resolved_metadata); + + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create output metadata buffer.\n"); + return AVERROR_UNKNOWN; + } + + return 0; +} + +static int d3d12va_encode_issue(AVCodecContext *avctx, + const HWBaseEncodePicture *base_pic) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; + int err, i, j; + HRESULT hr; + char data[MAX_PARAM_BUFFER_SIZE]; + void *ptr; + size_t bit_len; + ID3D12CommandAllocator *command_allocator = NULL; + ID3D12VideoEncodeCommandList2 *cmd_list = ctx->command_list; + D3D12_RESOURCE_BARRIER barriers[32] = { 0 }; + D3D12_VIDEO_ENCODE_REFERENCE_FRAMES d3d12_refs = { 0 }; + + D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = { + .SequenceControlDesc = { + .Flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE, + .IntraRefreshConfig = { 0 }, + .RateControl = ctx->rc, + .PictureTargetResolution = ctx->resolution, + .SelectedLayoutMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, + .FrameSubregionsLayoutData = { 0 }, + .CodecGopSequence = ctx->gop, + }, + .pInputFrame = pic->input_surface->texture, + .InputFrameSubresource = 0, + }; + + D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS output_args = { 0 }; + + D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS input_metadata = { + .EncoderCodec = ctx->codec->d3d12_codec, + .EncoderProfile = ctx->profile->d3d12_profile, + .EncoderInputFormat = frames_hwctx->format, + .EncodedPictureEffectiveResolution = ctx->resolution, + }; + + D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS output_metadata = { 0 }; + + memset(data, 0, sizeof(data)); + + av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" " + "as type %s.\n", base_pic->display_order, base_pic->encode_order, + ff_hw_base_encode_get_pictype_name(base_pic->type)); + if (base_pic->nb_refs[0] == 0 && base_pic->nb_refs[1] == 0) { + av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n"); + } else { + av_log(avctx, AV_LOG_DEBUG, "L0 refers to"); + for (i = 0; i < base_pic->nb_refs[0]; i++) { + av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, + base_pic->refs[0][i]->display_order, base_pic->refs[0][i]->encode_order); + } + av_log(avctx, AV_LOG_DEBUG, ".\n"); + + if (base_pic->nb_refs[1]) { + av_log(avctx, AV_LOG_DEBUG, "L1 refers to"); + for (i = 0; i < base_pic->nb_refs[1]; i++) { + av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, + base_pic->refs[1][i]->display_order, base_pic->refs[1][i]->encode_order); + } + av_log(avctx, AV_LOG_DEBUG, ".\n"); + } + } + + av_assert0(!base_pic->encode_issued); + for (i = 0; i < base_pic->nb_refs[0]; i++) { + av_assert0(base_pic->refs[0][i]); + av_assert0(base_pic->refs[0][i]->encode_issued); + } + for (i = 0; i < base_pic->nb_refs[1]; i++) { + av_assert0(base_pic->refs[1][i]); + av_assert0(base_pic->refs[1][i]->encode_issued); + } + + av_log(avctx, AV_LOG_DEBUG, "Input surface is %p.\n", pic->input_surface->texture); + + err = av_hwframe_get_buffer(base_ctx->recon_frames_ref, base_pic->recon_image, 0); + if (err < 0) { + err = AVERROR(ENOMEM); + goto fail; + } + + pic->recon_surface = (AVD3D12VAFrame *)base_pic->recon_image->data[0]; + av_log(avctx, AV_LOG_DEBUG, "Recon surface is %p.\n", + pic->recon_surface->texture); + + pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool); + if (!pic->output_buffer_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + pic->output_buffer = (ID3D12Resource *)pic->output_buffer_ref->data; + av_log(avctx, AV_LOG_DEBUG, "Output buffer is %p.\n", + pic->output_buffer); + + err = d3d12va_encode_create_metadata_buffers(avctx, pic); + if (err < 0) + goto fail; + + if (ctx->codec->init_picture_params) { + err = ctx->codec->init_picture_params(avctx, pic); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialise picture " + "parameters: %d.\n", err); + goto fail; + } + } + + if (base_pic->type == PICTURE_TYPE_IDR) { + if (ctx->codec->write_sequence_header) { + bit_len = 8 * sizeof(data); + err = ctx->codec->write_sequence_header(avctx, data, &bit_len); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write per-sequence " + "header: %d.\n", err); + goto fail; + } + } + + pic->header_size = (int)bit_len / 8; + pic->header_size = pic->header_size % ctx->req.CompressedBitstreamBufferAccessAlignment ? + FFALIGN(pic->header_size, ctx->req.CompressedBitstreamBufferAccessAlignment) : + pic->header_size; + + hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&ptr); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + memcpy(ptr, data, pic->header_size); + ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); + } + + d3d12_refs.NumTexture2Ds = base_pic->nb_refs[0] + base_pic->nb_refs[1]; + if (d3d12_refs.NumTexture2Ds) { + d3d12_refs.ppTexture2Ds = av_calloc(d3d12_refs.NumTexture2Ds, + sizeof(*d3d12_refs.ppTexture2Ds)); + if (!d3d12_refs.ppTexture2Ds) { + err = AVERROR(ENOMEM); + goto fail; + } + + i = 0; + for (j = 0; j < base_pic->nb_refs[0]; j++) + d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[0][j])->recon_surface->texture; + for (j = 0; j < base_pic->nb_refs[1]; j++) + d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[1][j])->recon_surface->texture; + } + + input_args.PictureControlDesc.IntraRefreshFrameIndex = 0; + if (base_pic->is_reference) + input_args.PictureControlDesc.Flags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE; + + input_args.PictureControlDesc.PictureControlCodecData = pic->pic_ctl; + input_args.PictureControlDesc.ReferenceFrames = d3d12_refs; + input_args.CurrentFrameBitstreamMetadataSize = pic->header_size; + + output_args.Bitstream.pBuffer = pic->output_buffer; + output_args.Bitstream.FrameStartOffset = pic->header_size; + output_args.ReconstructedPicture.pReconstructedPicture = pic->recon_surface->texture; + output_args.ReconstructedPicture.ReconstructedPictureSubresource = 0; + output_args.EncoderOutputMetadata.pBuffer = pic->encoded_metadata; + output_args.EncoderOutputMetadata.Offset = 0; + + input_metadata.HWLayoutMetadata.pBuffer = pic->encoded_metadata; + input_metadata.HWLayoutMetadata.Offset = 0; + + output_metadata.ResolvedLayoutMetadata.pBuffer = pic->resolved_metadata; + output_metadata.ResolvedLayoutMetadata.Offset = 0; + + err = d3d12va_get_valid_command_allocator(avctx, &command_allocator); + if (err < 0) + goto fail; + + hr = ID3D12CommandAllocator_Reset(command_allocator); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12VideoEncodeCommandList2_Reset(cmd_list, command_allocator); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + +#define TRANSITION_BARRIER(res, before, after) \ + (D3D12_RESOURCE_BARRIER) { \ + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, \ + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, \ + .Transition = { \ + .pResource = res, \ + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, \ + .StateBefore = before, \ + .StateAfter = after, \ + }, \ + } + + barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + barriers[1] = TRANSITION_BARRIER(pic->output_buffer, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE); + + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); + + if (d3d12_refs.NumTexture2Ds) { + D3D12_RESOURCE_BARRIER refs_barriers[3]; + + for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) + refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], + D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds, + refs_barriers); + } + + ID3D12VideoEncodeCommandList2_EncodeFrame(cmd_list, ctx->encoder, ctx->encoder_heap, + &input_args, &output_args); + + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ); + + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 1, &barriers[3]); + + ID3D12VideoEncodeCommandList2_ResolveEncoderOutputMetadata(cmd_list, &input_metadata, &output_metadata); + + if (d3d12_refs.NumTexture2Ds) { + D3D12_RESOURCE_BARRIER refs_barriers[3]; + + for (i = 0; i < d3d12_refs.NumTexture2Ds; i++) + refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i], + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, + D3D12_RESOURCE_STATE_COMMON); + + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds, + refs_barriers); + } + + barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, + D3D12_RESOURCE_STATE_COMMON); + barriers[1] = TRANSITION_BARRIER(pic->output_buffer, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, + D3D12_RESOURCE_STATE_COMMON); + barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, + D3D12_RESOURCE_STATE_COMMON); + barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ, + D3D12_RESOURCE_STATE_COMMON); + barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata, + D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE, + D3D12_RESOURCE_STATE_COMMON); + + ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers); + + hr = ID3D12VideoEncodeCommandList2_Close(cmd_list); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12CommandQueue_Wait(ctx->command_queue, pic->input_surface->sync_ctx.fence, + pic->input_surface->sync_ctx.fence_value); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list); + + hr = ID3D12CommandQueue_Signal(ctx->command_queue, pic->input_surface->sync_ctx.fence, + ++pic->input_surface->sync_ctx.fence_value); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + goto fail; + } + + err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value); + if (err < 0) + goto fail; + + pic->fence_value = ctx->sync_ctx.fence_value; + + if (d3d12_refs.ppTexture2Ds) + av_freep(&d3d12_refs.ppTexture2Ds); + + return 0; + +fail: + if (command_allocator) + d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value); + + if (d3d12_refs.ppTexture2Ds) + av_freep(&d3d12_refs.ppTexture2Ds); + + if (ctx->codec->free_picture_params) + ctx->codec->free_picture_params(pic); + + av_buffer_unref(&pic->output_buffer_ref); + pic->output_buffer = NULL; + D3D12_OBJECT_RELEASE(pic->encoded_metadata); + D3D12_OBJECT_RELEASE(pic->resolved_metadata); + return err; +} + +static int d3d12va_encode_discard(AVCodecContext *avctx, + D3D12VAEncodePicture *pic) +{ + HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; + d3d12va_encode_wait(avctx, pic); + + if (pic->output_buffer_ref) { + av_log(avctx, AV_LOG_DEBUG, "Discard output for pic " + "%"PRId64"/%"PRId64".\n", + base_pic->display_order, base_pic->encode_order); + + av_buffer_unref(&pic->output_buffer_ref); + pic->output_buffer = NULL; + } + + D3D12_OBJECT_RELEASE(pic->encoded_metadata); + D3D12_OBJECT_RELEASE(pic->resolved_metadata); + + return 0; +} + +static int d3d12va_encode_free_rc_params(AVCodecContext *avctx) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + + switch (ctx->rc.Mode) + { + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP: + av_freep(&ctx->rc.ConfigParams.pConfiguration_CQP); + break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR: + av_freep(&ctx->rc.ConfigParams.pConfiguration_CBR); + break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR: + av_freep(&ctx->rc.ConfigParams.pConfiguration_VBR); + break; + case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR: + av_freep(&ctx->rc.ConfigParams.pConfiguration_QVBR); + break; + default: + break; + } + + return 0; +} + +static HWBaseEncodePicture *d3d12va_encode_alloc(AVCodecContext *avctx, + const AVFrame *frame) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodePicture *pic; + + pic = av_mallocz(sizeof(*pic)); + if (!pic) + return NULL; + + if (ctx->codec->picture_priv_data_size > 0) { + pic->base.priv_data = av_mallocz(ctx->codec->picture_priv_data_size); + if (!pic->base.priv_data) { + av_freep(&pic); + return NULL; + } + } + + pic->input_surface = (AVD3D12VAFrame *)frame->data[0]; + + return (HWBaseEncodePicture *)pic; +} + +static int d3d12va_encode_free(AVCodecContext *avctx, + HWBaseEncodePicture *base_pic) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; + + if (base_pic->encode_issued) + d3d12va_encode_discard(avctx, pic); + + if (ctx->codec->free_picture_params) + ctx->codec->free_picture_params(pic); + + ff_hw_base_encode_free(avctx, base_pic); + + av_free(pic); + + return 0; +} + +static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx, + D3D12VAEncodePicture *pic, size_t *size) +{ + D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL; + uint8_t *data; + HRESULT hr; + int err; + + hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void **)&data); + if (FAILED(hr)) { + err = AVERROR_UNKNOWN; + return err; + } + + 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; + + ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL); + + return 0; +} + +static int d3d12va_encode_get_coded_data(AVCodecContext *avctx, + D3D12VAEncodePicture *pic, AVPacket *pkt) +{ + int err; + uint8_t *ptr, *mapped_data; + size_t total_size = 0; + HRESULT hr; + + err = d3d12va_encode_get_buffer_size(avctx, pic, &total_size); + if (err < 0) + goto end; + + total_size += pic->header_size; + av_log(avctx, AV_LOG_DEBUG, "Output buffer size %"PRId64"\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, total_size); + + ID3D12Resource_Unmap(pic->output_buffer, 0, NULL); + +end: + av_buffer_unref(&pic->output_buffer_ref); + pic->output_buffer = NULL; + return err; +} + +static int d3d12va_encode_output(AVCodecContext *avctx, + const HWBaseEncodePicture *base_pic, AVPacket *pkt) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic; + AVPacket *pkt_ptr = pkt; + int err; + + err = d3d12va_encode_wait(avctx, pic); + if (err < 0) + return err; + + err = d3d12va_encode_get_coded_data(avctx, pic, pkt); + if (err < 0) + return err; + + av_log(avctx, AV_LOG_DEBUG, "Output read for pic %"PRId64"/%"PRId64".\n", + base_pic->display_order, base_pic->encode_order); + + ff_hw_base_encode_set_output_property(avctx, (HWBaseEncodePicture *)base_pic, pkt_ptr, 0); + + return 0; +} + +static int d3d12va_encode_set_profile(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + const D3D12VAEncodeProfile *profile; + const AVPixFmtDescriptor *desc; + int i, depth; + + desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); + if (!desc) { + av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n", + base_ctx->input_frames->sw_format); + return AVERROR(EINVAL); + } + + depth = desc->comp[0].depth; + for (i = 1; i < desc->nb_components; i++) { + if (desc->comp[i].depth != depth) { + av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n", + desc->name); + return AVERROR(EINVAL); + } + } + av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n", + desc->name); + + av_assert0(ctx->codec->profiles); + for (i = 0; (ctx->codec->profiles[i].av_profile != + AV_PROFILE_UNKNOWN); i++) { + profile = &ctx->codec->profiles[i]; + if (depth != profile->depth || + desc->nb_components != profile->nb_components) + continue; + if (desc->nb_components > 1 && + (desc->log2_chroma_w != profile->log2_chroma_w || + desc->log2_chroma_h != profile->log2_chroma_h)) + continue; + if (avctx->profile != profile->av_profile && + avctx->profile != AV_PROFILE_UNKNOWN) + continue; + + ctx->profile = profile; + break; + } + if (!ctx->profile) { + av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n"); + return AVERROR(ENOSYS); + } + + avctx->profile = profile->av_profile; + return 0; +} + +static const D3D12VAEncodeRCMode d3d12va_encode_rc_modes[] = { + // Bitrate Quality + // | Maxrate | HRD/VBV + { 0 }, // | | | | + { RC_MODE_CQP, "CQP", 0, 0, 1, 0, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP }, + { RC_MODE_CBR, "CBR", 1, 0, 0, 1, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR }, + { RC_MODE_VBR, "VBR", 1, 1, 0, 1, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR }, + { RC_MODE_QVBR, "QVBR", 1, 1, 1, 1, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR }, +}; + +static int check_rate_control_support(AVCodecContext *avctx, const D3D12VAEncodeRCMode *rc_mode) +{ + HRESULT hr; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_rc_mode = { + .Codec = ctx->codec->d3d12_codec, + }; + + if (!rc_mode->d3d12_mode) + return 0; + + d3d12_rc_mode.IsSupported = 0; + d3d12_rc_mode.RateControlMode = rc_mode->d3d12_mode; + + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, + D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE, + &d3d12_rc_mode, sizeof(d3d12_rc_mode)); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to check rate control support.\n"); + return 0; + } + + return d3d12_rc_mode.IsSupported; +} + +static int d3d12va_encode_init_rate_control(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + int64_t rc_target_bitrate; + int64_t rc_peak_bitrate; + int rc_quality; + int64_t hrd_buffer_size; + int64_t hrd_initial_buffer_fullness; + int fr_num, fr_den; + const D3D12VAEncodeRCMode *rc_mode; + + // Rate control mode selection: + // * If the user has set a mode explicitly with the rc_mode option, + // use it and fail if it is not available. + // * If an explicit QP option has been set, use CQP. + // * If the codec is CQ-only, use CQP. + // * If the QSCALE avcodec option is set, use CQP. + // * If bitrate and quality are both set, try QVBR. + // * If quality is set, try CQP. + // * If bitrate and maxrate are set and have the same value, try CBR. + // * If a bitrate is set, try VBR, then CBR. + // * If no bitrate is set, try CQP. + +#define TRY_RC_MODE(mode, fail) do { \ + rc_mode = &d3d12va_encode_rc_modes[mode]; \ + if (!(rc_mode->d3d12_mode && check_rate_control_support(avctx, rc_mode))) { \ + if (fail) { \ + av_log(avctx, AV_LOG_ERROR, "Driver does not support %s " \ + "RC mode.\n", rc_mode->name); \ + return AVERROR(EINVAL); \ + } \ + av_log(avctx, AV_LOG_DEBUG, "Driver does not support %s " \ + "RC mode.\n", rc_mode->name); \ + rc_mode = NULL; \ + } else { \ + goto rc_mode_found; \ + } \ + } while (0) + + if (base_ctx->explicit_rc_mode) + TRY_RC_MODE(base_ctx->explicit_rc_mode, 1); + + if (base_ctx->explicit_qp) + TRY_RC_MODE(RC_MODE_CQP, 1); + + if (ctx->codec->flags & FLAG_CONSTANT_QUALITY_ONLY) + TRY_RC_MODE(RC_MODE_CQP, 1); + + if (avctx->flags & AV_CODEC_FLAG_QSCALE) + TRY_RC_MODE(RC_MODE_CQP, 1); + + if (avctx->bit_rate > 0 && avctx->global_quality > 0) + TRY_RC_MODE(RC_MODE_QVBR, 0); + + if (avctx->global_quality > 0) { + TRY_RC_MODE(RC_MODE_CQP, 0); + } + + if (avctx->bit_rate > 0 && avctx->rc_max_rate == avctx->bit_rate) + TRY_RC_MODE(RC_MODE_CBR, 0); + + if (avctx->bit_rate > 0) { + TRY_RC_MODE(RC_MODE_VBR, 0); + TRY_RC_MODE(RC_MODE_CBR, 0); + } else { + TRY_RC_MODE(RC_MODE_CQP, 0); + } + + av_log(avctx, AV_LOG_ERROR, "Driver does not support any " + "RC mode compatible with selected options.\n"); + return AVERROR(EINVAL); + +rc_mode_found: + if (rc_mode->bitrate) { + if (avctx->bit_rate <= 0) { + av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s " + "RC mode.\n", rc_mode->name); + return AVERROR(EINVAL); + } + + if (rc_mode->maxrate) { + if (avctx->rc_max_rate > 0) { + if (avctx->rc_max_rate < avctx->bit_rate) { + av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: " + "bitrate (%"PRId64") must not be greater than " + "maxrate (%"PRId64").\n", avctx->bit_rate, + avctx->rc_max_rate); + return AVERROR(EINVAL); + } + rc_target_bitrate = avctx->bit_rate; + rc_peak_bitrate = avctx->rc_max_rate; + } else { + // We only have a target bitrate, but this mode requires + // that a maximum rate be supplied as well. Since the + // user does not want this to be a constraint, arbitrarily + // pick a maximum rate of double the target rate. + rc_target_bitrate = avctx->bit_rate; + rc_peak_bitrate = 2 * avctx->bit_rate; + } + } else { + if (avctx->rc_max_rate > avctx->bit_rate) { + av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored " + "in %s RC mode.\n", rc_mode->name); + } + rc_target_bitrate = avctx->bit_rate; + rc_peak_bitrate = 0; + } + } else { + rc_target_bitrate = 0; + rc_peak_bitrate = 0; + } + + if (rc_mode->quality) { + if (base_ctx->explicit_qp) { + rc_quality = base_ctx->explicit_qp; + } else if (avctx->global_quality > 0) { + rc_quality = avctx->global_quality; + } else { + rc_quality = ctx->codec->default_quality; + av_log(avctx, AV_LOG_WARNING, "No quality level set; " + "using default (%d).\n", rc_quality); + } + } else { + rc_quality = 0; + } + + if (rc_mode->hrd) { + if (avctx->rc_buffer_size) + hrd_buffer_size = avctx->rc_buffer_size; + else if (avctx->rc_max_rate > 0) + hrd_buffer_size = avctx->rc_max_rate; + else + hrd_buffer_size = avctx->bit_rate; + if (avctx->rc_initial_buffer_occupancy) { + if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) { + av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: " + "must have initial buffer size (%d) <= " + "buffer size (%"PRId64").\n", + avctx->rc_initial_buffer_occupancy, hrd_buffer_size); + return AVERROR(EINVAL); + } + hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy; + } else { + hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4; + } + } else { + if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) { + av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored " + "in %s RC mode.\n", rc_mode->name); + } + + hrd_buffer_size = 0; + hrd_initial_buffer_fullness = 0; + } + + if (rc_target_bitrate > UINT32_MAX || + hrd_buffer_size > UINT32_MAX || + hrd_initial_buffer_fullness > UINT32_MAX) { + av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or " + "greater are not supported by D3D12.\n"); + return AVERROR(EINVAL); + } + + base_ctx->rc_quality = rc_quality; + + av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name); + + if (rc_mode->quality) + av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_quality); + + if (rc_mode->hrd) { + av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, " + "initial fullness %"PRId64" bits.\n", + hrd_buffer_size, hrd_initial_buffer_fullness); + } + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + av_reduce(&fr_num, &fr_den, + avctx->framerate.num, avctx->framerate.den, 65535); + else + av_reduce(&fr_num, &fr_den, + avctx->time_base.den, avctx->time_base.num, 65535); + + av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n", + fr_num, fr_den, (double)fr_num / fr_den); + + ctx->rc.Flags = D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE; + ctx->rc.TargetFrameRate.Numerator = fr_num; + ctx->rc.TargetFrameRate.Denominator = fr_den; + ctx->rc.Mode = rc_mode->d3d12_mode; + + switch (rc_mode->mode) { + case RC_MODE_CQP: + // cqp ConfigParams will be updated in ctx->codec->configure. + break; + + case RC_MODE_CBR: + D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR *cbr_ctl; + + ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR); + cbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); + if (!cbr_ctl) + return AVERROR(ENOMEM); + + cbr_ctl->TargetBitRate = rc_target_bitrate; + cbr_ctl->VBVCapacity = hrd_buffer_size; + cbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; + ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; + + if (avctx->qmin > 0 || avctx->qmax > 0) { + cbr_ctl->MinQP = avctx->qmin; + cbr_ctl->MaxQP = avctx->qmax; + ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; + } + + ctx->rc.ConfigParams.pConfiguration_CBR = cbr_ctl; + break; + + case RC_MODE_VBR: + D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR *vbr_ctl; + + ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR); + vbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); + if (!vbr_ctl) + return AVERROR(ENOMEM); + + vbr_ctl->TargetAvgBitRate = rc_target_bitrate; + vbr_ctl->PeakBitRate = rc_peak_bitrate; + vbr_ctl->VBVCapacity = hrd_buffer_size; + vbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness; + ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES; + + if (avctx->qmin > 0 || avctx->qmax > 0) { + vbr_ctl->MinQP = avctx->qmin; + vbr_ctl->MaxQP = avctx->qmax; + ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; + } + + ctx->rc.ConfigParams.pConfiguration_VBR = vbr_ctl; + break; + + case RC_MODE_QVBR: + D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR *qvbr_ctl; + + ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR); + qvbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize); + if (!qvbr_ctl) + return AVERROR(ENOMEM); + + qvbr_ctl->TargetAvgBitRate = rc_target_bitrate; + qvbr_ctl->PeakBitRate = rc_peak_bitrate; + qvbr_ctl->ConstantQualityTarget = rc_quality; + + if (avctx->qmin > 0 || avctx->qmax > 0) { + qvbr_ctl->MinQP = avctx->qmin; + qvbr_ctl->MaxQP = avctx->qmax; + ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE; + } + + ctx->rc.ConfigParams.pConfiguration_QVBR = qvbr_ctl; + break; + + default: + break; + } + return 0; +} + +static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + uint32_t ref_l0, ref_l1; + int err; + HRESULT hr; + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT support; + union { + D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 h264; + D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_HEVC hevc; + } codec_support; + + support.NodeIndex = 0; + support.Codec = ctx->codec->d3d12_codec; + support.Profile = ctx->profile->d3d12_profile; + + switch (ctx->codec->d3d12_codec) { + case D3D12_VIDEO_ENCODER_CODEC_H264: + support.PictureSupport.DataSize = sizeof(codec_support.h264); + support.PictureSupport.pH264Support = &codec_support.h264; + break; + + case D3D12_VIDEO_ENCODER_CODEC_HEVC: + support.PictureSupport.DataSize = sizeof(codec_support.hevc); + support.PictureSupport.pHEVCSupport = &codec_support.hevc; + break; + } + + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT, + &support, sizeof(support)); + if (FAILED(hr)) + return AVERROR(EINVAL); + + if (support.IsSupported) { + switch (ctx->codec->d3d12_codec) { + case D3D12_VIDEO_ENCODER_CODEC_H264: + ref_l0 = FFMIN(support.PictureSupport.pH264Support->MaxL0ReferencesForP, + support.PictureSupport.pH264Support->MaxL1ReferencesForB); + ref_l1 = support.PictureSupport.pH264Support->MaxL1ReferencesForB; + break; + + case D3D12_VIDEO_ENCODER_CODEC_HEVC: + ref_l0 = FFMIN(support.PictureSupport.pHEVCSupport->MaxL0ReferencesForP, + support.PictureSupport.pHEVCSupport->MaxL1ReferencesForB); + ref_l1 = support.PictureSupport.pHEVCSupport->MaxL1ReferencesForB; + break; + } + } else { + ref_l0 = ref_l1 = 0; + } + + if (ref_l0 > 0 && ref_l1 > 0 && ctx->bi_not_empty) { + base_ctx->p_to_gpb = 1; + av_log(avctx, AV_LOG_VERBOSE, "Driver does not support P-frames, " + "replacing them with B-frames.\n"); + } + + err = ff_hw_base_init_gop_structure(avctx, ref_l0, ref_l1, ctx->codec->flags, 0); + if (err < 0) + return err; + + return 0; +} + +static int d3d12va_create_encoder(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx; + HRESULT hr; + + D3D12_VIDEO_ENCODER_DESC desc = { + .NodeMask = 0, + .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, + .EncodeCodec = ctx->codec->d3d12_codec, + .EncodeProfile = ctx->profile->d3d12_profile, + .InputFormat = frames_hwctx->format, + .CodecConfiguration = ctx->codec_conf, + .MaxMotionEstimationPrecision = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM, + }; + + hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, &IID_ID3D12VideoEncoder, + (void **)&ctx->encoder); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create encoder.\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int d3d12va_create_encoder_heap(AVCodecContext* avctx) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + HRESULT hr; + + D3D12_VIDEO_ENCODER_HEAP_DESC desc = { + .NodeMask = 0, + .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE, + .EncodeCodec = ctx->codec->d3d12_codec, + .EncodeProfile = ctx->profile->d3d12_profile, + .EncodeLevel = ctx->level, + .ResolutionsListCount = 1, + .pResolutionList = &ctx->resolution, + }; + + hr = ID3D12VideoDevice3_CreateVideoEncoderHeap(ctx->video_device3, &desc, + &IID_ID3D12VideoEncoderHeap, (void **)&ctx->encoder_heap); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create encoder heap.\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static void d3d12va_encode_free_buffer(void *opaque, uint8_t *data) +{ + ID3D12Resource *pResource; + + pResource = (ID3D12Resource *)data; + D3D12_OBJECT_RELEASE(pResource); +} + +static AVBufferRef *d3d12va_encode_alloc_output_buffer(void *opaque, size_t size) +{ + AVCodecContext *avctx = opaque; + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + ID3D12Resource *pResource = NULL; + HRESULT hr; + AVBufferRef *ref; + D3D12_HEAP_PROPERTIES heap_props; + D3D12_HEAP_TYPE heap_type = D3D12_HEAP_TYPE_READBACK; + + D3D12_RESOURCE_DESC desc = { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = 0, + .Width = FFALIGN(3 * base_ctx->surface_width * base_ctx->surface_height + (1 << 16), + D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT), + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = { .Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE, + }; + + ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, &heap_props, 0, heap_type); + + hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &heap_props, D3D12_HEAP_FLAG_NONE, + &desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, + (void **)&pResource); + + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create d3d12 buffer.\n"); + return NULL; + } + + ref = av_buffer_create((uint8_t *)(uintptr_t)pResource, + sizeof(pResource), + &d3d12va_encode_free_buffer, + avctx, AV_BUFFER_FLAG_READONLY); + if (!ref) { + D3D12_OBJECT_RELEASE(pResource); + return NULL; + } + + return ref; +} + +static int d3d12va_encode_prepare_output_buffers(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + AVD3D12VAFramesContext *frames_ctx = base_ctx->input_frames->hwctx; + HRESULT hr; + + ctx->req.NodeIndex = 0; + ctx->req.Codec = ctx->codec->d3d12_codec; + ctx->req.Profile = ctx->profile->d3d12_profile; + ctx->req.InputFormat = frames_ctx->format; + ctx->req.PictureTargetResolution = ctx->resolution; + + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, + D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS, + &ctx->req, sizeof(ctx->req)); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to check encoder resource requirements support.\n"); + return AVERROR(EINVAL); + } + + if (!ctx->req.IsSupported) { + av_log(avctx, AV_LOG_ERROR, "Encoder resource requirements unsupported.\n"); + return AVERROR(EINVAL); + } + + ctx->output_buffer_pool = av_buffer_pool_init2(sizeof(ID3D12Resource *), avctx, + &d3d12va_encode_alloc_output_buffer, NULL); + if (!ctx->output_buffer_pool) + return AVERROR(ENOMEM); + + return 0; +} + +static int d3d12va_encode_create_command_objects(AVCodecContext *avctx) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + ID3D12CommandAllocator *command_allocator = NULL; + int err; + HRESULT hr; + + D3D12_COMMAND_QUEUE_DESC queue_desc = { + .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE, + .Priority = 0, + .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, + .NodeMask = 0, + }; + + ctx->allocator_queue = av_fifo_alloc2(D3D12VA_VIDEO_ENC_ASYNC_DEPTH, + sizeof(CommandAllocator), AV_FIFO_FLAG_AUTO_GROW); + if (!ctx->allocator_queue) + return AVERROR(ENOMEM); + + hr = ID3D12Device_CreateFence(ctx->hwctx->device, 0, D3D12_FENCE_FLAG_NONE, + &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create fence(%lx)\n", (long)hr); + err = AVERROR_UNKNOWN; + goto fail; + } + + ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!ctx->sync_ctx.event) + goto fail; + + err = d3d12va_get_valid_command_allocator(avctx, &command_allocator); + if (err < 0) + goto fail; + + hr = ID3D12Device_CreateCommandQueue(ctx->hwctx->device, &queue_desc, + &IID_ID3D12CommandQueue, (void **)&ctx->command_queue); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create command queue(%lx)\n", (long)hr); + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12Device_CreateCommandList(ctx->hwctx->device, 0, queue_desc.Type, + command_allocator, NULL, &IID_ID3D12CommandList, + (void **)&ctx->command_list); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create command list(%lx)\n", (long)hr); + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12VideoEncodeCommandList2_Close(ctx->command_list); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to close the command list(%lx)\n", (long)hr); + err = AVERROR_UNKNOWN; + goto fail; + } + + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list); + + err = d3d12va_sync_with_gpu(avctx); + if (err < 0) + goto fail; + + err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value); + if (err < 0) + goto fail; + + return 0; + +fail: + D3D12_OBJECT_RELEASE(command_allocator); + return err; +} + +static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + AVD3D12VAFramesContext *hwctx; + enum AVPixelFormat recon_format; + int err; + + err = ff_hw_base_get_recon_format(avctx, NULL, &recon_format); + if (err < 0) + return err; + + base_ctx->recon_frames_ref = av_hwframe_ctx_alloc(base_ctx->device_ref); + if (!base_ctx->recon_frames_ref) + return AVERROR(ENOMEM); + + base_ctx->recon_frames = (AVHWFramesContext *)base_ctx->recon_frames_ref->data; + hwctx = (AVD3D12VAFramesContext *)base_ctx->recon_frames->hwctx; + + base_ctx->recon_frames->format = AV_PIX_FMT_D3D12; + base_ctx->recon_frames->sw_format = recon_format; + base_ctx->recon_frames->width = base_ctx->surface_width; + base_ctx->recon_frames->height = base_ctx->surface_height; + + hwctx->flags = D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY | + D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + + err = av_hwframe_ctx_init(base_ctx->recon_frames_ref); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialise reconstructed " + "frame context: %d.\n", err); + return err; + } + + return 0; +} + +static const HWEncodePictureOperation d3d12va_type = { + .alloc = &d3d12va_encode_alloc, + + .issue = &d3d12va_encode_issue, + + .output = &d3d12va_encode_output, + + .free = &d3d12va_encode_free, +}; + +int ff_d3d12va_encode_init(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT support = { 0 }; + int err; + HRESULT hr; + + err = ff_hw_base_encode_init(avctx); + if (err < 0) + goto fail; + + base_ctx->op = &d3d12va_type; + + ctx->hwctx = base_ctx->device->hwctx; + + ctx->resolution.Width = base_ctx->input_frames->width; + ctx->resolution.Height = base_ctx->input_frames->height; + + hr = ID3D12Device_QueryInterface(ctx->hwctx->device, &IID_ID3D12Device3, (void **)&ctx->device3); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "ID3D12Device3 interface is not supported.\n"); + err = AVERROR_UNKNOWN; + goto fail; + } + + hr = ID3D12Device3_QueryInterface(ctx->device3, &IID_ID3D12VideoDevice3, (void **)&ctx->video_device3); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "ID3D12VideoDevice3 interface is not supported.\n"); + err = AVERROR_UNKNOWN; + goto fail; + } + + if (FAILED(ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT, + &support, sizeof(support))) && !support.VideoEncodeSupport) { + av_log(avctx, AV_LOG_ERROR, "D3D12 video device has no video encoder support.\n"); + err = AVERROR(EINVAL); + goto fail; + } + + err = d3d12va_encode_set_profile(avctx); + if (err < 0) + goto fail; + + if (ctx->codec->get_encoder_caps) { + err = ctx->codec->get_encoder_caps(avctx); + if (err < 0) + goto fail; + } + + err = d3d12va_encode_init_rate_control(avctx); + if (err < 0) + goto fail; + + err = d3d12va_encode_init_gop_structure(avctx); + if (err < 0) + goto fail; + + if (!(ctx->codec->flags & FLAG_SLICE_CONTROL) && avctx->slices > 0) { + av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested " + "but this codec does not support controlling slices.\n"); + } + + err = d3d12va_encode_create_command_objects(avctx); + if (err < 0) + goto fail; + + err = d3d12va_encode_create_recon_frames(avctx); + if (err < 0) + goto fail; + + err = d3d12va_encode_prepare_output_buffers(avctx); + if (err < 0) + goto fail; + + if (ctx->codec->configure) { + err = ctx->codec->configure(avctx); + if (err < 0) + goto fail; + } + + if (ctx->codec->init_sequence_params) { + err = ctx->codec->init_sequence_params(avctx); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Codec sequence initialisation " + "failed: %d.\n", err); + goto fail; + } + } + + if (ctx->codec->set_level) { + err = ctx->codec->set_level(avctx); + if (err < 0) + goto fail; + } + + base_ctx->output_delay = base_ctx->b_per_p; + base_ctx->decode_delay = base_ctx->max_b_depth; + + err = d3d12va_create_encoder(avctx); + if (err < 0) + goto fail; + + err = d3d12va_create_encoder_heap(avctx); + if (err < 0) + goto fail; + + base_ctx->async_encode = 1; + base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth, + sizeof(D3D12VAEncodePicture *), 0); + if (!base_ctx->encode_fifo) + return AVERROR(ENOMEM); + + return 0; + +fail: + return err; +} + +int ff_d3d12va_encode_close(AVCodecContext *avctx) +{ + int num_allocator = 0; + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + HWBaseEncodePicture *pic, *next; + CommandAllocator allocator; + + if (!base_ctx->frame) + return 0; + + for (pic = base_ctx->pic_start; pic; pic = next) { + next = pic->next; + d3d12va_encode_free(avctx, pic); + } + + d3d12va_encode_free_rc_params(avctx); + + av_buffer_pool_uninit(&ctx->output_buffer_pool); + + D3D12_OBJECT_RELEASE(ctx->command_list); + D3D12_OBJECT_RELEASE(ctx->command_queue); + + if (ctx->allocator_queue) { + while (av_fifo_read(ctx->allocator_queue, &allocator, 1) >= 0) { + num_allocator++; + D3D12_OBJECT_RELEASE(allocator.command_allocator); + } + + av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator); + } + + av_fifo_freep2(&ctx->allocator_queue); + + D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence); + if (ctx->sync_ctx.event) + CloseHandle(ctx->sync_ctx.event); + + D3D12_OBJECT_RELEASE(ctx->encoder_heap); + D3D12_OBJECT_RELEASE(ctx->encoder); + D3D12_OBJECT_RELEASE(ctx->video_device3); + D3D12_OBJECT_RELEASE(ctx->device3); + + ff_hw_base_encode_close(avctx); + + return 0; +} diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h new file mode 100644 index 0000000000..10e2d87035 --- /dev/null +++ b/libavcodec/d3d12va_encode.h @@ -0,0 +1,321 @@ +/* + * 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 + */ + +#ifndef AVCODEC_D3D12VA_ENCODE_H +#define AVCODEC_D3D12VA_ENCODE_H + +#include "libavutil/fifo.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "libavutil/hwcontext_d3d12va.h" +#include "avcodec.h" +#include "internal.h" +#include "hwconfig.h" +#include "hw_base_encode.h" + +struct D3D12VAEncodeType; + +extern const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[]; + +#define MAX_PARAM_BUFFER_SIZE 4096 +#define D3D12VA_VIDEO_ENC_ASYNC_DEPTH 8 + +typedef struct D3D12VAEncodePicture { + HWBaseEncodePicture base; + + int header_size; + + AVD3D12VAFrame *input_surface; + AVD3D12VAFrame *recon_surface; + + AVBufferRef *output_buffer_ref; + ID3D12Resource *output_buffer; + + ID3D12Resource *encoded_metadata; + ID3D12Resource *resolved_metadata; + + D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA pic_ctl; + + int fence_value; +} D3D12VAEncodePicture; + +typedef struct D3D12VAEncodeProfile { + /** + * lavc profile value (AV_PROFILE_*). + */ + int av_profile; + + /** + * Supported bit depth. + */ + int depth; + + /** + * Number of components. + */ + int nb_components; + + /** + * Chroma subsampling in width dimension. + */ + int log2_chroma_w; + + /** + * Chroma subsampling in height dimension. + */ + int log2_chroma_h; + + /** + * D3D12 profile value. + */ + D3D12_VIDEO_ENCODER_PROFILE_DESC d3d12_profile; +} D3D12VAEncodeProfile; + +enum { + RC_MODE_AUTO, + RC_MODE_CQP, + RC_MODE_CBR, + RC_MODE_VBR, + RC_MODE_QVBR, + RC_MODE_MAX = RC_MODE_QVBR, +}; + + +typedef struct D3D12VAEncodeRCMode { + /** + * Mode from above enum (RC_MODE_*). + */ + int mode; + + /** + * Name. + * + */ + const char *name; + + /** + * Uses bitrate parameters. + * + */ + int bitrate; + + /** + * Supports maxrate distinct from bitrate. + * + */ + int maxrate; + + /** + * Uses quality value. + * + */ + int quality; + + /** + * Supports HRD/VBV parameters. + * + */ + int hrd; + + /** + * Supported by D3D12 HW. + */ + int supported; + + /** + * D3D12 mode value. + */ + D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_mode; +} D3D12VAEncodeRCMode; + +typedef struct D3D12VAEncodeContext { + HWBaseEncodeContext base; + + /** + * Codec-specific hooks. + */ + const struct D3D12VAEncodeType *codec; + + /** + * Chosen encoding profile details. + */ + const D3D12VAEncodeProfile *profile; + + AVD3D12VADeviceContext *hwctx; + + /** + * ID3D12Device3 interface. + */ + ID3D12Device3 *device3; + + /** + * ID3D12VideoDevice3 interface. + */ + ID3D12VideoDevice3 *video_device3; + + /** + * Pool of (reusable) bitstream output buffers. + */ + AVBufferPool *output_buffer_pool; + + /** + * D3D12 video encoder. + */ + AVBufferRef *encoder_ref; + + ID3D12VideoEncoder *encoder; + + /** + * D3D12 video encoder heap. + */ + ID3D12VideoEncoderHeap *encoder_heap; + + /** + * A cached queue for reusing the D3D12 command allocators. + * + * @see https://learn.microsoft.com/en-us/windows/win32/direct3d12/recording-command-lists-and-bundles#id3d12commandallocator + */ + AVFifo *allocator_queue; + + /** + * D3D12 command queue. + */ + ID3D12CommandQueue *command_queue; + + /** + * D3D12 video encode command list. + */ + ID3D12VideoEncodeCommandList2 *command_list; + + /** + * The sync context used to sync command queue. + */ + AVD3D12VASyncContext sync_ctx; + + /** + * The bi_not_empty feature. + */ + int bi_not_empty; + + /** + * D3D12_FEATURE structures. + */ + D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req; + + D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS res_limits; + + /** + * D3D12_VIDEO_ENCODER structures. + */ + D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolution; + + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codec_conf; + + D3D12_VIDEO_ENCODER_RATE_CONTROL rc; + + D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop; + + D3D12_VIDEO_ENCODER_LEVEL_SETTING level; +} D3D12VAEncodeContext; + +typedef struct D3D12VAEncodeType { + /** + * List of supported profiles. + */ + const D3D12VAEncodeProfile *profiles; + + /** + * D3D12 codec name. + */ + D3D12_VIDEO_ENCODER_CODEC d3d12_codec; + + /** + * Codec feature flags. + */ + int flags; + + /** + * Default quality for this codec - used as quantiser or RC quality + * factor depending on RC mode. + */ + int default_quality; + + /** + * Query codec configuration and determine encode parameters like + * block sizes for surface alignment and slices. If not set, assume + * that all blocks are 16x16 and that surfaces should be aligned to match + * this. + */ + int (*get_encoder_caps)(AVCodecContext *avctx); + + /** + * Perform any extra codec-specific configuration. + */ + int (*configure)(AVCodecContext *avctx); + + /** + * Set codec-specific level setting. + */ + int (*set_level)(AVCodecContext *avctx); + + /** + * The size of any private data structure associated with each + * picture (can be zero if not required). + */ + size_t picture_priv_data_size; + + /** + * Fill the corresponding parameters. + */ + int (*init_sequence_params)(AVCodecContext *avctx); + + int (*init_picture_params)(AVCodecContext *avctx, + D3D12VAEncodePicture *pic); + + void (*free_picture_params)(D3D12VAEncodePicture *pic); + + /** + * Write the packed header data to the provided buffer. + */ + int (*write_sequence_header)(AVCodecContext *avctx, + char *data, size_t *data_len); +} D3D12VAEncodeType; + +int ff_d3d12va_encode_init(AVCodecContext *avctx); +int ff_d3d12va_encode_close(AVCodecContext *avctx); + +#define D3D12VA_ENCODE_RC_MODE(name, desc) \ + { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \ + 0, 0, FLAGS, .unit = "rc_mode" } +#define D3D12VA_ENCODE_RC_OPTIONS \ + { "rc_mode",\ + "Set rate control mode", \ + OFFSET(common.base.explicit_rc_mode), AV_OPT_TYPE_INT, \ + { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, FLAGS, .unit = "rc_mode" }, \ + { "auto", "Choose mode automatically based on other parameters", \ + 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, .unit = "rc_mode" }, \ + D3D12VA_ENCODE_RC_MODE(CQP, "Constant-quality"), \ + D3D12VA_ENCODE_RC_MODE(CBR, "Constant-bitrate"), \ + D3D12VA_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \ + D3D12VA_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate") + +#endif /* AVCODEC_D3D12VA_ENCODE_H */ diff --git a/libavcodec/d3d12va_encode_hevc.c b/libavcodec/d3d12va_encode_hevc.c new file mode 100644 index 0000000000..aec0d9dcec --- /dev/null +++ b/libavcodec/d3d12va_encode_hevc.c @@ -0,0 +1,957 @@ +/* + * 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/pixdesc.h" +#include "libavutil/hwcontext_d3d12va_internal.h" + +#include "avcodec.h" +#include "cbs.h" +#include "cbs_h265.h" +#include "h2645data.h" +#include "h265_profile_level.h" +#include "codec_internal.h" +#include "d3d12va_encode.h" + +typedef struct D3D12VAEncodeHEVCPicture { + int pic_order_cnt; + int64_t last_idr_frame; +} D3D12VAEncodeHEVCPicture; + +typedef struct D3D12VAEncodeHEVCContext { + D3D12VAEncodeContext common; + + // User options. + int qp; + int profile; + int tier; + int level; + + // Writer structures. + H265RawVPS raw_vps; + H265RawSPS raw_sps; + H265RawPPS raw_pps; + + CodedBitstreamContext *cbc; + CodedBitstreamFragment current_access_unit; +} D3D12VAEncodeHEVCContext; + +typedef struct D3D12VAEncodeHEVCLevel { + int level; + D3D12_VIDEO_ENCODER_LEVELS_HEVC d3d12_level; +} D3D12VAEncodeHEVCLevel; + +static const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC hevc_config_support_sets[] = +{ + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + 3, + 3, + }, + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + 0, + 0, + }, + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + 2, + 2, + }, + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + 2, + 2, + }, + { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + 4, + 4, + }, +}; + +static const D3D12VAEncodeHEVCLevel hevc_levels[] = { + { 30, D3D12_VIDEO_ENCODER_LEVELS_HEVC_1 }, + { 60, D3D12_VIDEO_ENCODER_LEVELS_HEVC_2 }, + { 63, D3D12_VIDEO_ENCODER_LEVELS_HEVC_21 }, + { 90, D3D12_VIDEO_ENCODER_LEVELS_HEVC_3 }, + { 93, D3D12_VIDEO_ENCODER_LEVELS_HEVC_31 }, + { 120, D3D12_VIDEO_ENCODER_LEVELS_HEVC_4 }, + { 123, D3D12_VIDEO_ENCODER_LEVELS_HEVC_41 }, + { 150, D3D12_VIDEO_ENCODER_LEVELS_HEVC_5 }, + { 153, D3D12_VIDEO_ENCODER_LEVELS_HEVC_51 }, + { 156, D3D12_VIDEO_ENCODER_LEVELS_HEVC_52 }, + { 180, D3D12_VIDEO_ENCODER_LEVELS_HEVC_6 }, + { 183, D3D12_VIDEO_ENCODER_LEVELS_HEVC_61 }, + { 186, D3D12_VIDEO_ENCODER_LEVELS_HEVC_62 }, +}; + +static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; +static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main10 = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN10; + +#define D3D_PROFILE_DESC(name) \ + { sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), { .pHEVCProfile = (D3D12_VIDEO_ENCODER_PROFILE_HEVC *)&profile_ ## name } } +static const D3D12VAEncodeProfile d3d12va_encode_hevc_profiles[] = { + { AV_PROFILE_HEVC_MAIN, 8, 3, 1, 1, D3D_PROFILE_DESC(main) }, + { AV_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, D3D_PROFILE_DESC(main10) }, + { AV_PROFILE_UNKNOWN }, +}; + +static uint8_t d3d12va_encode_hevc_map_cusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE cusize) +{ + switch (cusize) { + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8: return 8; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_16x16: return 16; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32: return 32; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64: return 64; + default: av_assert0(0); + } + return 0; +} + +static uint8_t d3d12va_encode_hevc_map_tusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE tusize) +{ + switch (tusize) { + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4: return 4; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_8x8: return 8; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_16x16: return 16; + case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32: return 32; + default: av_assert0(0); + } + return 0; +} + +static int d3d12va_encode_hevc_write_access_unit(AVCodecContext *avctx, + char *data, size_t *data_len, + CodedBitstreamFragment *au) +{ + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + int err; + + err = ff_cbs_write_fragment_data(priv->cbc, au); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); + return err; + } + + if (*data_len < 8 * au->data_size - au->data_bit_padding) { + av_log(avctx, AV_LOG_ERROR, "Access unit too large: " + "%zu < %zu.\n", *data_len, + 8 * au->data_size - au->data_bit_padding); + return AVERROR(ENOSPC); + } + + memcpy(data, au->data, au->data_size); + *data_len = 8 * au->data_size - au->data_bit_padding; + + return 0; +} + +static int d3d12va_encode_hevc_add_nal(AVCodecContext *avctx, + CodedBitstreamFragment *au, + void *nal_unit) +{ + H265RawNALUnitHeader *header = nal_unit; + int err; + + err = ff_cbs_insert_unit_content(au, -1, + header->nal_unit_type, nal_unit, NULL); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: " + "type = %d.\n", header->nal_unit_type); + return err; + } + + return 0; +} + +static int d3d12va_encode_hevc_write_sequence_header(AVCodecContext *avctx, + char *data, size_t *data_len) +{ + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + CodedBitstreamFragment *au = &priv->current_access_unit; + int err; + + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_vps); + if (err < 0) + goto fail; + + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_sps); + if (err < 0) + goto fail; + + err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_pps); + if (err < 0) + goto fail; + + err = d3d12va_encode_hevc_write_access_unit(avctx, data, data_len, au); +fail: + ff_cbs_fragment_reset(au); + return err; + +} + +static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx; + H265RawVPS *vps = &priv->raw_vps; + H265RawSPS *sps = &priv->raw_sps; + H265RawPPS *pps = &priv->raw_pps; + H265RawProfileTierLevel *ptl = &vps->profile_tier_level; + H265RawVUI *vui = &sps->vui; + D3D12_VIDEO_ENCODER_PROFILE_HEVC profile = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; + D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC level = { 0 }; + const AVPixFmtDescriptor *desc; + uint8_t min_cu_size, max_cu_size, min_tu_size, max_tu_size; + int chroma_format, bit_depth; + HRESULT hr; + int i; + + D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = { + .NodeIndex = 0, + .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, + .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 = MAX_DPB_SIZE - 1, + .CodecConfiguration = ctx->codec_conf, + .SuggestedProfile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), + .SuggestedProfile.pHEVCProfile = &profile, + .SuggestedLevel.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC), + .SuggestedLevel.pHEVCLevelSetting = &level, + .pResolutionDependentSupport = &ctx->res_limits, + }; + + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, + &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 features. %#x\n", + support.ValidationFlags); + return AVERROR(EINVAL); + } + + if (support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) { + av_log(avctx, AV_LOG_ERROR, "D3D12 video encode on this device requires texture array support, " + "but it's not implemented.\n"); + return AVERROR_PATCHWELCOME; + } + + memset(vps, 0, sizeof(*vps)); + memset(sps, 0, sizeof(*sps)); + memset(pps, 0, sizeof(*pps)); + + desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format); + av_assert0(desc); + if (desc->nb_components == 1) { + chroma_format = 0; + } else { + if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) { + chroma_format = 1; + } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) { + chroma_format = 2; + } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) { + chroma_format = 3; + } else { + av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format " + "%s is not supported.\n", desc->name); + return AVERROR(EINVAL); + } + } + bit_depth = desc->comp[0].depth; + + min_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig->MinLumaCodingUnitSize); + max_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig->MaxLumaCodingUnitSize); + min_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig->MinLumaTransformUnitSize); + max_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig->MaxLumaTransformUnitSize); + + // VPS + + vps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_VPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + vps->vps_video_parameter_set_id = 0; + + vps->vps_base_layer_internal_flag = 1; + vps->vps_base_layer_available_flag = 1; + vps->vps_max_layers_minus1 = 0; + vps->vps_max_sub_layers_minus1 = 0; + vps->vps_temporal_id_nesting_flag = 1; + + ptl->general_profile_space = 0; + ptl->general_profile_idc = avctx->profile; + ptl->general_tier_flag = priv->tier; + + ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1; + + ptl->general_progressive_source_flag = 1; + ptl->general_interlaced_source_flag = 0; + ptl->general_non_packed_constraint_flag = 1; + ptl->general_frame_only_constraint_flag = 1; + + ptl->general_max_14bit_constraint_flag = bit_depth <= 14; + ptl->general_max_12bit_constraint_flag = bit_depth <= 12; + ptl->general_max_10bit_constraint_flag = bit_depth <= 10; + ptl->general_max_8bit_constraint_flag = bit_depth == 8; + + ptl->general_max_422chroma_constraint_flag = chroma_format <= 2; + ptl->general_max_420chroma_constraint_flag = chroma_format <= 1; + ptl->general_max_monochrome_constraint_flag = chroma_format == 0; + + ptl->general_intra_constraint_flag = base_ctx->gop_size == 1; + ptl->general_one_picture_only_constraint_flag = 0; + + ptl->general_lower_bit_rate_constraint_flag = 1; + + if (avctx->level != FF_LEVEL_UNKNOWN) { + ptl->general_level_idc = avctx->level; + } else { + const H265LevelDescriptor *level; + + level = ff_h265_guess_level(ptl, avctx->bit_rate, + base_ctx->surface_width, base_ctx->surface_height, + 1, 1, 1, (base_ctx->b_per_p > 0) + 1); + if (level) { + av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name); + ptl->general_level_idc = level->level_idc; + } else { + av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to " + "any normal level; using level 8.5.\n"); + ptl->general_level_idc = 255; + // The tier flag must be set in level 8.5. + ptl->general_tier_flag = 1; + } + avctx->level = ptl->general_level_idc; + } + + vps->vps_sub_layer_ordering_info_present_flag = 0; + vps->vps_max_dec_pic_buffering_minus1[0] = base_ctx->max_b_depth + 1; + vps->vps_max_num_reorder_pics[0] = base_ctx->max_b_depth; + vps->vps_max_latency_increase_plus1[0] = 0; + + vps->vps_max_layer_id = 0; + vps->vps_num_layer_sets_minus1 = 0; + vps->layer_id_included_flag[0][0] = 1; + + vps->vps_timing_info_present_flag = 0; + + // SPS + + sps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_SPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + sps->sps_video_parameter_set_id = vps->vps_video_parameter_set_id; + + sps->sps_max_sub_layers_minus1 = vps->vps_max_sub_layers_minus1; + sps->sps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag; + + sps->profile_tier_level = vps->profile_tier_level; + + sps->sps_seq_parameter_set_id = 0; + + sps->chroma_format_idc = chroma_format; + sps->separate_colour_plane_flag = 0; + + av_assert0(ctx->res_limits.SubregionBlockPixelsSize % min_cu_size == 0); + + sps->pic_width_in_luma_samples = FFALIGN(base_ctx->surface_width, + ctx->res_limits.SubregionBlockPixelsSize); + sps->pic_height_in_luma_samples = FFALIGN(base_ctx->surface_height, + ctx->res_limits.SubregionBlockPixelsSize); + + if (avctx->width != sps->pic_width_in_luma_samples || + avctx->height != sps->pic_height_in_luma_samples) { + sps->conformance_window_flag = 1; + sps->conf_win_left_offset = 0; + sps->conf_win_right_offset = + (sps->pic_width_in_luma_samples - avctx->width) >> desc->log2_chroma_w; + sps->conf_win_top_offset = 0; + sps->conf_win_bottom_offset = + (sps->pic_height_in_luma_samples - avctx->height) >> desc->log2_chroma_h; + } else { + sps->conformance_window_flag = 0; + } + + sps->bit_depth_luma_minus8 = bit_depth - 8; + sps->bit_depth_chroma_minus8 = bit_depth - 8; + + sps->log2_max_pic_order_cnt_lsb_minus4 = ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4; + + sps->sps_sub_layer_ordering_info_present_flag = + vps->vps_sub_layer_ordering_info_present_flag; + for (i = 0; i <= sps->sps_max_sub_layers_minus1; i++) { + sps->sps_max_dec_pic_buffering_minus1[i] = + vps->vps_max_dec_pic_buffering_minus1[i]; + sps->sps_max_num_reorder_pics[i] = + vps->vps_max_num_reorder_pics[i]; + sps->sps_max_latency_increase_plus1[i] = + vps->vps_max_latency_increase_plus1[i]; + } + + sps->log2_min_luma_coding_block_size_minus3 = (uint8_t)(av_log2(min_cu_size) - 3); + sps->log2_diff_max_min_luma_coding_block_size = (uint8_t)(av_log2(max_cu_size) - av_log2(min_cu_size)); + sps->log2_min_luma_transform_block_size_minus2 = (uint8_t)(av_log2(min_tu_size) - 2); + sps->log2_diff_max_min_luma_transform_block_size = (uint8_t)(av_log2(max_tu_size) - av_log2(min_tu_size)); + + sps->max_transform_hierarchy_depth_inter = ctx->codec_conf.pHEVCConfig->max_transform_hierarchy_depth_inter; + sps->max_transform_hierarchy_depth_intra = ctx->codec_conf.pHEVCConfig->max_transform_hierarchy_depth_intra; + + sps->amp_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags & + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION); + sps->sample_adaptive_offset_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags & + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER); + sps->sps_temporal_mvp_enabled_flag = 0; + sps->pcm_enabled_flag = 0; + + sps->vui_parameters_present_flag = 1; + + // vui default parameters + vui->aspect_ratio_idc = 0; + vui->video_format = 5; + vui->video_full_range_flag = 0; + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coefficients = 2; + vui->chroma_sample_loc_type_top_field = 0; + vui->chroma_sample_loc_type_bottom_field = 0; + vui->tiles_fixed_structure_flag = 0; + vui->motion_vectors_over_pic_boundaries_flag = 1; + vui->min_spatial_segmentation_idc = 0; + vui->max_bytes_per_pic_denom = 2; + vui->max_bits_per_min_cu_denom = 1; + vui->log2_max_mv_length_horizontal = 15; + vui->log2_max_mv_length_vertical = 15; + + // PPS + + pps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_PPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + pps->pps_pic_parameter_set_id = 0; + pps->pps_seq_parameter_set_id = sps->sps_seq_parameter_set_id; + + pps->cabac_init_present_flag = 1; + + pps->num_ref_idx_l0_default_active_minus1 = 0; + pps->num_ref_idx_l1_default_active_minus1 = 0; + + pps->init_qp_minus26 = 0; + + pps->transform_skip_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags & + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING); + + // cu_qp_delta always required to be 1 in https://github.com/microsoft/DirectX-Specs/blob/master/d3d/D3D12VideoEncoding.md + pps->cu_qp_delta_enabled_flag = 1; + + pps->diff_cu_qp_delta_depth = 0; + + pps->pps_slice_chroma_qp_offsets_present_flag = 1; + + pps->tiles_enabled_flag = 0; // no tiling in D3D12 + + pps->pps_loop_filter_across_slices_enabled_flag = !(ctx->codec_conf.pHEVCConfig->ConfigurationFlags & + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_SLICES); + pps->deblocking_filter_control_present_flag = 1; + + return 0; +} + +static int d3d12va_encode_hevc_get_encoder_caps(AVCodecContext *avctx) +{ + int i; + HRESULT hr; + uint8_t min_cu_size, max_cu_size; + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC *config; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC hevc_caps; + + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codec_caps = { + .NodeIndex = 0, + .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, + .Profile = ctx->profile->d3d12_profile, + .CodecSupportLimits.DataSize = sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC), + }; + + for (i = 0; i < FF_ARRAY_ELEMS(hevc_config_support_sets); i++) { + hevc_caps = hevc_config_support_sets[i]; + codec_caps.CodecSupportLimits.pHEVCSupport = &hevc_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) + break; + } + + if (i == FF_ARRAY_ELEMS(hevc_config_support_sets)) { + av_log(avctx, AV_LOG_ERROR, "Unsupported codec configuration\n"); + return AVERROR(EINVAL); + } + + ctx->codec_conf.DataSize = sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC); + ctx->codec_conf.pHEVCConfig = av_mallocz(ctx->codec_conf.DataSize); + if (!ctx->codec_conf.pHEVCConfig) + return AVERROR(ENOMEM); + + config = ctx->codec_conf.pHEVCConfig; + + config->ConfigurationFlags = D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_NONE; + config->MinLumaCodingUnitSize = hevc_caps.MinLumaCodingUnitSize; + config->MaxLumaCodingUnitSize = hevc_caps.MaxLumaCodingUnitSize; + config->MinLumaTransformUnitSize = hevc_caps.MinLumaTransformUnitSize; + config->MaxLumaTransformUnitSize = hevc_caps.MaxLumaTransformUnitSize; + config->max_transform_hierarchy_depth_inter = hevc_caps.max_transform_hierarchy_depth_inter; + config->max_transform_hierarchy_depth_intra = hevc_caps.max_transform_hierarchy_depth_intra; + + if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_SUPPORT || + hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_REQUIRED) + config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION; + + if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_SAO_FILTER_SUPPORT) + config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER; + + if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_DISABLING_LOOP_FILTER_ACROSS_SLICES_SUPPORT) + config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_SLICES; + + if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_SUPPORT) + config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING; + + if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_P_FRAMES_IMPLEMENTED_AS_LOW_DELAY_B_FRAMES) + ctx->bi_not_empty = 1; + + // block sizes + min_cu_size = d3d12va_encode_hevc_map_cusize(hevc_caps.MinLumaCodingUnitSize); + max_cu_size = d3d12va_encode_hevc_map_cusize(hevc_caps.MaxLumaCodingUnitSize); + + av_log(avctx, AV_LOG_VERBOSE, "Using CTU size %dx%d, " + "min CB size %dx%d.\n", max_cu_size, max_cu_size, + min_cu_size, min_cu_size); + + base_ctx->surface_width = FFALIGN(avctx->width, min_cu_size); + base_ctx->surface_height = FFALIGN(avctx->height, min_cu_size); + + return 0; +} + +static int d3d12va_encode_hevc_configure(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + int fixed_qp_idr, fixed_qp_p, fixed_qp_b; + int err; + + err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx); + if (err < 0) + return err; + + // Rate control + if (ctx->rc.Mode == D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP) { + D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP *cqp_ctl; + fixed_qp_p = av_clip(base_ctx->rc_quality, 1, 51); + if (avctx->i_quant_factor > 0.0) + fixed_qp_idr = av_clip((avctx->i_quant_factor * fixed_qp_p + + avctx->i_quant_offset) + 0.5, 1, 51); + else + fixed_qp_idr = fixed_qp_p; + if (avctx->b_quant_factor > 0.0) + fixed_qp_b = av_clip((avctx->b_quant_factor * fixed_qp_p + + avctx->b_quant_offset) + 0.5, 1, 51); + else + fixed_qp_b = fixed_qp_p; + + av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " + "%d / %d / %d for IDR- / P- / B-frames.\n", + fixed_qp_idr, fixed_qp_p, fixed_qp_b); + + 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_idr; + cqp_ctl->ConstantQP_InterPredictedFrame_PrevRefOnly = fixed_qp_p; + cqp_ctl->ConstantQP_InterPredictedFrame_BiDirectionalRef = fixed_qp_b; + + ctx->rc.ConfigParams.pConfiguration_CQP = cqp_ctl; + } + + // GOP + ctx->gop.DataSize = sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC); + ctx->gop.pHEVCGroupOfPictures = av_mallocz(ctx->gop.DataSize); + if (!ctx->gop.pHEVCGroupOfPictures) + return AVERROR(ENOMEM); + + ctx->gop.pHEVCGroupOfPictures->GOPLength = base_ctx->gop_size; + ctx->gop.pHEVCGroupOfPictures->PPicturePeriod = base_ctx->b_per_p + 1; + // Power of 2 + if (base_ctx->gop_size & base_ctx->gop_size - 1 == 0) + ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 = + FFMAX(av_log2(base_ctx->gop_size) - 4, 0); + else + ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 = + FFMAX(av_log2(base_ctx->gop_size) - 3, 0); + + return 0; +} + +static int d3d12va_encode_hevc_set_level(AVCodecContext *avctx) +{ + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + int i; + + ctx->level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC); + ctx->level.pHEVCLevelSetting = av_mallocz(ctx->level.DataSize); + if (!ctx->level.pHEVCLevelSetting) + return AVERROR(ENOMEM); + + for (i = 0; i < FF_ARRAY_ELEMS(hevc_levels); i++) { + if (avctx->level == hevc_levels[i].level) { + ctx->level.pHEVCLevelSetting->Level = hevc_levels[i].d3d12_level; + break; + } + } + + if (i == FF_ARRAY_ELEMS(hevc_levels)) { + av_log(avctx, AV_LOG_ERROR, "Invalid level %d.\n", avctx->level); + return AVERROR(EINVAL); + } + + ctx->level.pHEVCLevelSetting->Tier = priv->raw_vps.profile_tier_level.general_tier_flag == 0 ? + D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN : + D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH; + + return 0; +} + +static void d3d12va_encode_hevc_free_picture_params(D3D12VAEncodePicture *pic) +{ + if (!pic->pic_ctl.pHEVCPicData) + return; + + av_freep(&pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames); + av_freep(&pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames); + av_freep(&pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors); + av_freep(&pic->pic_ctl.pHEVCPicData); +} + +static int d3d12va_encode_hevc_init_picture_params(AVCodecContext *avctx, + D3D12VAEncodePicture *pic) +{ + HWBaseEncodePicture *base_pic = (HWBaseEncodePicture *)pic; + D3D12VAEncodeHEVCPicture *hpic = base_pic->priv_data; + HWBaseEncodePicture *prev = base_pic->prev; + D3D12VAEncodeHEVCPicture *hprev = prev ? prev->priv_data : NULL; + D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_HEVC *pd = NULL; + UINT *ref_list0 = NULL, *ref_list1 = NULL; + int i, idx = 0; + + pic->pic_ctl.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC); + pic->pic_ctl.pHEVCPicData = av_mallocz(pic->pic_ctl.DataSize); + if (!pic->pic_ctl.pHEVCPicData) + return AVERROR(ENOMEM); + + if (base_pic->type == PICTURE_TYPE_IDR) { + av_assert0(base_pic->display_order == base_pic->encode_order); + hpic->last_idr_frame = base_pic->display_order; + } else { + av_assert0(prev); + hpic->last_idr_frame = hprev->last_idr_frame; + } + hpic->pic_order_cnt = base_pic->display_order - hpic->last_idr_frame; + + switch(base_pic->type) { + case PICTURE_TYPE_IDR: + pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_IDR_FRAME; + break; + case PICTURE_TYPE_I: + pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_I_FRAME; + break; + case PICTURE_TYPE_P: + pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_P_FRAME; + break; + case PICTURE_TYPE_B: + pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_B_FRAME; + break; + default: + av_assert0(0 && "invalid picture type"); + } + + pic->pic_ctl.pHEVCPicData->slice_pic_parameter_set_id = 0; + pic->pic_ctl.pHEVCPicData->PictureOrderCountNumber = hpic->pic_order_cnt; + + if (base_pic->type == PICTURE_TYPE_P || base_pic->type == PICTURE_TYPE_B) { + pd = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*pd)); + if (!pd) + return AVERROR(ENOMEM); + + ref_list0 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list0)); + if (!ref_list0) + return AVERROR(ENOMEM); + + pic->pic_ctl.pHEVCPicData->List0ReferenceFramesCount = base_pic->nb_refs[0]; + for (i = 0; i < base_pic->nb_refs[0]; i++) { + HWBaseEncodePicture *ref = base_pic->refs[0][i]; + D3D12VAEncodeHEVCPicture *href; + + av_assert0(ref && ref->encode_order < base_pic->encode_order); + href = ref->priv_data; + + ref_list0[i] = idx; + pd[idx].ReconstructedPictureResourceIndex = idx; + pd[idx].IsRefUsedByCurrentPic = TRUE; + pd[idx].PictureOrderCountNumber = href->pic_order_cnt; + idx++; + } + } + + if (base_pic->type == PICTURE_TYPE_B) { + ref_list1 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list1)); + if (!ref_list1) + return AVERROR(ENOMEM); + + pic->pic_ctl.pHEVCPicData->List1ReferenceFramesCount = base_pic->nb_refs[1]; + for (i = 0; i < base_pic->nb_refs[1]; i++) { + HWBaseEncodePicture *ref = base_pic->refs[1][i]; + D3D12VAEncodeHEVCPicture *href; + + av_assert0(ref && ref->encode_order < base_pic->encode_order); + href = ref->priv_data; + + ref_list1[i] = idx; + pd[idx].ReconstructedPictureResourceIndex = idx; + pd[idx].IsRefUsedByCurrentPic = TRUE; + pd[idx].PictureOrderCountNumber = href->pic_order_cnt; + idx++; + } + } + + pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames = ref_list0; + pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames = ref_list1; + pic->pic_ctl.pHEVCPicData->ReferenceFramesReconPictureDescriptorsCount = idx; + pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors = pd; + + return 0; +} + +static const D3D12VAEncodeType d3d12va_encode_type_hevc = { + .profiles = d3d12va_encode_hevc_profiles, + + .d3d12_codec = D3D12_VIDEO_ENCODER_CODEC_HEVC, + + .flags = FLAG_B_PICTURES | + FLAG_B_PICTURE_REFERENCES | + FLAG_NON_IDR_KEY_PICTURES, + + .default_quality = 25, + + .get_encoder_caps = &d3d12va_encode_hevc_get_encoder_caps, + + .configure = &d3d12va_encode_hevc_configure, + + .set_level = &d3d12va_encode_hevc_set_level, + + .picture_priv_data_size = sizeof(D3D12VAEncodeHEVCPicture), + + .init_sequence_params = &d3d12va_encode_hevc_init_sequence_params, + + .init_picture_params = &d3d12va_encode_hevc_init_picture_params, + + .free_picture_params = &d3d12va_encode_hevc_free_picture_params, + + .write_sequence_header = &d3d12va_encode_hevc_write_sequence_header, +}; + +static int d3d12va_encode_hevc_init(AVCodecContext *avctx) +{ + HWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + + ctx->codec = &d3d12va_encode_type_hevc; + + if (avctx->profile == AV_PROFILE_UNKNOWN) + avctx->profile = priv->profile; + if (avctx->level == FF_LEVEL_UNKNOWN) + avctx->level = priv->level; + + if (avctx->level != FF_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) + base_ctx->explicit_qp = priv->qp; + + return ff_d3d12va_encode_init(avctx); +} + +static int d3d12va_encode_hevc_close(AVCodecContext *avctx) +{ + D3D12VAEncodeHEVCContext *priv = avctx->priv_data; + + ff_cbs_fragment_free(&priv->current_access_unit); + ff_cbs_close(&priv->cbc); + + av_freep(&priv->common.codec_conf.pHEVCConfig); + av_freep(&priv->common.gop.pHEVCGroupOfPictures); + av_freep(&priv->common.level.pHEVCLevelSetting); + + return ff_d3d12va_encode_close(avctx); +} + +#define OFFSET(x) offsetof(D3D12VAEncodeHEVCContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) +static const AVOption d3d12va_encode_hevc_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_HEVC_MAIN) }, + { PROFILE("main10", AV_PROFILE_HEVC_MAIN_10) }, + { PROFILE("rext", AV_PROFILE_HEVC_REXT) }, +#undef PROFILE + + { "tier", "Set tier (general_tier_flag)", + OFFSET(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 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, + +#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = value }, 0, 0, FLAGS, "level" + { LEVEL("1", 30) }, + { LEVEL("2", 60) }, + { LEVEL("2.1", 63) }, + { LEVEL("3", 90) }, + { LEVEL("3.1", 93) }, + { LEVEL("4", 120) }, + { LEVEL("4.1", 123) }, + { LEVEL("5", 150) }, + { LEVEL("5.1", 153) }, + { LEVEL("5.2", 156) }, + { LEVEL("6", 180) }, + { LEVEL("6.1", 183) }, + { LEVEL("6.2", 186) }, +#undef LEVEL + + { NULL }, +}; + +static const FFCodecDefault d3d12va_encode_hevc_defaults[] = { + { "b", "0" }, + { "bf", "2" }, + { "g", "120" }, + { "i_qfactor", "1" }, + { "i_qoffset", "0" }, + { "b_qfactor", "1" }, + { "b_qoffset", "0" }, + { "qmin", "-1" }, + { "qmax", "-1" }, + { NULL }, +}; + +static const AVClass d3d12va_encode_hevc_class = { + .class_name = "hevc_d3d12va", + .item_name = av_default_item_name, + .option = d3d12va_encode_hevc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_hevc_d3d12va_encoder = { + .p.name = "hevc_d3d12va", + CODEC_LONG_NAME("D3D12VA hevc encoder"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .priv_data_size = sizeof(D3D12VAEncodeHEVCContext), + .init = &d3d12va_encode_hevc_init, + FF_CODEC_RECEIVE_PACKET_CB(&ff_hw_base_encode_receive_packet), + .close = &d3d12va_encode_hevc_close, + .p.priv_class = &d3d12va_encode_hevc_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_hevc_defaults, + .p.pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_D3D12, + AV_PIX_FMT_NONE, + }, + .hw_configs = ff_d3d12va_encode_hw_configs, + .p.wrapper_name = "d3d12va", +};