History

avcodec: add D3D12VA hardware HEVC encoder

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

Tong Wu committed 3 years ago

Commit ba0c14e1 parent d8221466 files · +2910 −1Browse files

Showing the changes to libavcodec (5 of 6 files). Show all

Unified · Split

5 files
  1. libavcodec/Makefile +4 −1
  2. libavcodec/allcodecs.c +1 −0
  3. libavcodec/d3d12va_encode.c +1558 −0
  4. libavcodec/d3d12va_encode.h +334 −0
  5. libavcodec/d3d12va_encode_hevc.c +1007 −0
libavcodec/Makefile+4−1
@@ -87,6 +87,7 @@ OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o · 86 unmodified lines
8787 OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
8888 OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
8989 OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
90+OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o
9091 OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o
9192 OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o
9293 OBJS-$(CONFIG_DOVI_RPUENC) += dovi_rpu.o dovi_rpuenc.o
@@ -433,6 +434,8 @@ OBJS-$(CONFIG_HDR_ENCODER) += hdrenc.o · 340 unmodified lines
433434 OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o
434435 OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o
435436 OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o
437+OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o h265_profile_level.o \
438+ h2645data.o
436439 OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o
437440 OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o
438441 OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o
@@ -1262,7 +1265,7 @@ SKIPHEADERS += %_tablegen.h \ · 823 unmodified lines
12621265
12631266 SKIPHEADERS-$(CONFIG_AMF) += amfenc.h
12641267 SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h
1265−SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h
1268+SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h d3d12va_encode.h
12661269 SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h
12671270 SKIPHEADERS-$(CONFIG_JNI) += ffjni.h
12681271 SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h
libavcodec/allcodecs.c+1−0
@@ -849,6 +849,7 @@ extern const FFCodec ff_h264_vaapi_encoder; · 848 unmodified lines
849849 extern const FFCodec ff_h264_videotoolbox_encoder;
850850 extern const FFCodec ff_hevc_amf_encoder;
851851 extern const FFCodec ff_hevc_cuvid_decoder;
852+extern const FFCodec ff_hevc_d3d12va_encoder;
852853 extern const FFCodec ff_hevc_mediacodec_decoder;
853854 extern const FFCodec ff_hevc_mediacodec_encoder;
854855 extern const FFCodec ff_hevc_mf_encoder;
libavcodec/d3d12va_encode.c+1558−0
@@ -0,0 +1,1558 @@
1+/*
2+ * Direct3D 12 HW acceleration video encoder
3+ *
4+ * Copyright (c) 2024 Intel Corporation
5+ *
6+ * This file is part of FFmpeg.
7+ *
8+ * FFmpeg is free software; you can redistribute it and/or
9+ * modify it under the terms of the GNU Lesser General Public
10+ * License as published by the Free Software Foundation; either
11+ * version 2.1 of the License, or (at your option) any later version.
12+ *
13+ * FFmpeg is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+ * Lesser General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU Lesser General Public
19+ * License along with FFmpeg; if not, write to the Free Software
20+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+ */
22+
23+#include "libavutil/avassert.h"
24+#include "libavutil/common.h"
25+#include "libavutil/internal.h"
26+#include "libavutil/log.h"
27+#include "libavutil/mem.h"
28+#include "libavutil/pixdesc.h"
29+#include "libavutil/hwcontext_d3d12va_internal.h"
30+#include "libavutil/hwcontext_d3d12va.h"
31+
32+#include "avcodec.h"
33+#include "d3d12va_encode.h"
34+#include "encode.h"
35+
36+const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[] = {
37+ HW_CONFIG_ENCODER_FRAMES(D3D12, D3D12VA),
38+ NULL,
39+};
40+
41+static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx)
42+{
43+ uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence);
44+ if (completion < psync_ctx->fence_value) {
45+ if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event)))
46+ return AVERROR(EINVAL);
47+
48+ WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE);
49+ }
50+
51+ return 0;
52+}
53+
54+static int d3d12va_sync_with_gpu(AVCodecContext *avctx)
55+{
56+ D3D12VAEncodeContext *ctx = avctx->priv_data;
57+
58+ DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
59+ return d3d12va_fence_completion(&ctx->sync_ctx);
60+
61+fail:
62+ return AVERROR(EINVAL);
63+}
64+
65+typedef struct CommandAllocator {
66+ ID3D12CommandAllocator *command_allocator;
67+ uint64_t fence_value;
68+} CommandAllocator;
69+
70+static int d3d12va_get_valid_command_allocator(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator)
71+{
72+ HRESULT hr;
73+ D3D12VAEncodeContext *ctx = avctx->priv_data;
74+ CommandAllocator allocator;
75+
76+ if (av_fifo_peek(ctx->allocator_queue, &allocator, 1, 0) >= 0) {
77+ uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence);
78+ if (completion >= allocator.fence_value) {
79+ *ppAllocator = allocator.command_allocator;
80+ av_fifo_read(ctx->allocator_queue, &allocator, 1);
81+ return 0;
82+ }
83+ }
84+
85+ hr = ID3D12Device_CreateCommandAllocator(ctx->hwctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
86+ &IID_ID3D12CommandAllocator, (void **)ppAllocator);
87+ if (FAILED(hr)) {
88+ av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n");
89+ return AVERROR(EINVAL);
90+ }
91+
92+ return 0;
93+}
94+
95+static int d3d12va_discard_command_allocator(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator, uint64_t fence_value)
96+{
97+ D3D12VAEncodeContext *ctx = avctx->priv_data;
98+
99+ CommandAllocator allocator = {
100+ .command_allocator = pAllocator,
101+ .fence_value = fence_value,
102+ };
103+
104+ av_fifo_write(ctx->allocator_queue, &allocator, 1);
105+
106+ return 0;
107+}
108+
109+static int d3d12va_encode_wait(AVCodecContext *avctx,
110+ D3D12VAEncodePicture *pic)
111+{
112+ D3D12VAEncodeContext *ctx = avctx->priv_data;
113+ FFHWBaseEncodePicture *base_pic = &pic->base;
114+ uint64_t completion;
115+
116+ av_assert0(base_pic->encode_issued);
117+
118+ if (base_pic->encode_complete) {
119+ // Already waited for this picture.
120+ return 0;
121+ }
122+
123+ completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence);
124+ if (completion < pic->fence_value) {
125+ if (FAILED(ID3D12Fence_SetEventOnCompletion(ctx->sync_ctx.fence, pic->fence_value,
126+ ctx->sync_ctx.event)))
127+ return AVERROR(EINVAL);
128+
129+ WaitForSingleObjectEx(ctx->sync_ctx.event, INFINITE, FALSE);
130+ }
131+
132+ av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" "
133+ "(input surface %p).\n", base_pic->display_order,
134+ base_pic->encode_order, pic->input_surface->texture);
135+
136+ av_frame_free(&base_pic->input_image);
137+
138+ base_pic->encode_complete = 1;
139+ return 0;
140+}
141+
142+static int d3d12va_encode_create_metadata_buffers(AVCodecContext *avctx,
143+ D3D12VAEncodePicture *pic)
144+{
145+ D3D12VAEncodeContext *ctx = avctx->priv_data;
146+ int width = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA) + sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA);
147+ D3D12_HEAP_PROPERTIES encoded_meta_props = { .Type = D3D12_HEAP_TYPE_DEFAULT }, resolved_meta_props;
148+ D3D12_HEAP_TYPE resolved_heap_type = D3D12_HEAP_TYPE_READBACK;
149+ HRESULT hr;
150+
151+ D3D12_RESOURCE_DESC meta_desc = {
152+ .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
153+ .Alignment = 0,
154+ .Width = ctx->req.MaxEncoderOutputMetadataBufferSize,
155+ .Height = 1,
156+ .DepthOrArraySize = 1,
157+ .MipLevels = 1,
158+ .Format = DXGI_FORMAT_UNKNOWN,
159+ .SampleDesc = { .Count = 1, .Quality = 0 },
160+ .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
161+ .Flags = D3D12_RESOURCE_FLAG_NONE,
162+ };
163+
164+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &encoded_meta_props, D3D12_HEAP_FLAG_NONE,
165+ &meta_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
166+ &IID_ID3D12Resource, (void **)&pic->encoded_metadata);
167+ if (FAILED(hr)) {
168+ av_log(avctx, AV_LOG_ERROR, "Failed to create metadata buffer.\n");
169+ return AVERROR_UNKNOWN;
170+ }
171+
172+ ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, &resolved_meta_props, 0, resolved_heap_type);
173+
174+ meta_desc.Width = width;
175+
176+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &resolved_meta_props, D3D12_HEAP_FLAG_NONE,
177+ &meta_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
178+ &IID_ID3D12Resource, (void **)&pic->resolved_metadata);
179+
180+ if (FAILED(hr)) {
181+ av_log(avctx, AV_LOG_ERROR, "Failed to create output metadata buffer.\n");
182+ return AVERROR_UNKNOWN;
183+ }
184+
185+ return 0;
186+}
187+
188+static int d3d12va_encode_issue(AVCodecContext *avctx,
189+ const FFHWBaseEncodePicture *base_pic)
190+{
191+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
192+ D3D12VAEncodeContext *ctx = avctx->priv_data;
193+ AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx;
194+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic;
195+ int err, i, j;
196+ HRESULT hr;
197+ char data[MAX_PARAM_BUFFER_SIZE];
198+ void *ptr;
199+ size_t bit_len;
200+ ID3D12CommandAllocator *command_allocator = NULL;
201+ ID3D12VideoEncodeCommandList2 *cmd_list = ctx->command_list;
202+ D3D12_RESOURCE_BARRIER barriers[32] = { 0 };
203+ D3D12_VIDEO_ENCODE_REFERENCE_FRAMES d3d12_refs = { 0 };
204+
205+ D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = {
206+ .SequenceControlDesc = {
207+ .Flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE,
208+ .IntraRefreshConfig = { 0 },
209+ .RateControl = ctx->rc,
210+ .PictureTargetResolution = ctx->resolution,
211+ .SelectedLayoutMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
212+ .FrameSubregionsLayoutData = { 0 },
213+ .CodecGopSequence = ctx->gop,
214+ },
215+ .pInputFrame = pic->input_surface->texture,
216+ .InputFrameSubresource = 0,
217+ };
218+
219+ D3D12_VIDEO_ENCODER_ENCODEFRAME_OUTPUT_ARGUMENTS output_args = { 0 };
220+
221+ D3D12_VIDEO_ENCODER_RESOLVE_METADATA_INPUT_ARGUMENTS input_metadata = {
222+ .EncoderCodec = ctx->codec->d3d12_codec,
223+ .EncoderProfile = ctx->profile->d3d12_profile,
224+ .EncoderInputFormat = frames_hwctx->format,
225+ .EncodedPictureEffectiveResolution = ctx->resolution,
226+ };
227+
228+ D3D12_VIDEO_ENCODER_RESOLVE_METADATA_OUTPUT_ARGUMENTS output_metadata = { 0 };
229+
230+ memset(data, 0, sizeof(data));
231+
232+ av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" "
233+ "as type %s.\n", base_pic->display_order, base_pic->encode_order,
234+ ff_hw_base_encode_get_pictype_name(base_pic->type));
235+ if (base_pic->nb_refs[0] == 0 && base_pic->nb_refs[1] == 0) {
236+ av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n");
237+ } else {
238+ av_log(avctx, AV_LOG_DEBUG, "L0 refers to");
239+ for (i = 0; i < base_pic->nb_refs[0]; i++) {
240+ av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64,
241+ base_pic->refs[0][i]->display_order, base_pic->refs[0][i]->encode_order);
242+ }
243+ av_log(avctx, AV_LOG_DEBUG, ".\n");
244+
245+ if (base_pic->nb_refs[1]) {
246+ av_log(avctx, AV_LOG_DEBUG, "L1 refers to");
247+ for (i = 0; i < base_pic->nb_refs[1]; i++) {
248+ av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64,
249+ base_pic->refs[1][i]->display_order, base_pic->refs[1][i]->encode_order);
250+ }
251+ av_log(avctx, AV_LOG_DEBUG, ".\n");
252+ }
253+ }
254+
255+ av_assert0(!base_pic->encode_issued);
256+ for (i = 0; i < base_pic->nb_refs[0]; i++) {
257+ av_assert0(base_pic->refs[0][i]);
258+ av_assert0(base_pic->refs[0][i]->encode_issued);
259+ }
260+ for (i = 0; i < base_pic->nb_refs[1]; i++) {
261+ av_assert0(base_pic->refs[1][i]);
262+ av_assert0(base_pic->refs[1][i]->encode_issued);
263+ }
264+
265+ av_log(avctx, AV_LOG_DEBUG, "Input surface is %p.\n", pic->input_surface->texture);
266+
267+ err = av_hwframe_get_buffer(base_ctx->recon_frames_ref, base_pic->recon_image, 0);
268+ if (err < 0) {
269+ err = AVERROR(ENOMEM);
270+ goto fail;
271+ }
272+
273+ pic->recon_surface = (AVD3D12VAFrame *)base_pic->recon_image->data[0];
274+ av_log(avctx, AV_LOG_DEBUG, "Recon surface is %p.\n",
275+ pic->recon_surface->texture);
276+
277+ pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool);
278+ if (!pic->output_buffer_ref) {
279+ err = AVERROR(ENOMEM);
280+ goto fail;
281+ }
282+ pic->output_buffer = (ID3D12Resource *)pic->output_buffer_ref->data;
283+ av_log(avctx, AV_LOG_DEBUG, "Output buffer is %p.\n",
284+ pic->output_buffer);
285+
286+ err = d3d12va_encode_create_metadata_buffers(avctx, pic);
287+ if (err < 0)
288+ goto fail;
289+
290+ if (ctx->codec->init_picture_params) {
291+ err = ctx->codec->init_picture_params(avctx, pic);
292+ if (err < 0) {
293+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise picture "
294+ "parameters: %d.\n", err);
295+ goto fail;
296+ }
297+ }
298+
299+ if (base_pic->type == FF_HW_PICTURE_TYPE_IDR) {
300+ if (ctx->codec->write_sequence_header) {
301+ bit_len = 8 * sizeof(data);
302+ err = ctx->codec->write_sequence_header(avctx, data, &bit_len);
303+ if (err < 0) {
304+ av_log(avctx, AV_LOG_ERROR, "Failed to write per-sequence "
305+ "header: %d.\n", err);
306+ goto fail;
307+ }
308+ }
309+
310+ pic->header_size = (int)bit_len / 8;
311+ pic->header_size = pic->header_size % ctx->req.CompressedBitstreamBufferAccessAlignment ?
312+ FFALIGN(pic->header_size, ctx->req.CompressedBitstreamBufferAccessAlignment) :
313+ pic->header_size;
314+
315+ hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&ptr);
316+ if (FAILED(hr)) {
317+ err = AVERROR_UNKNOWN;
318+ goto fail;
319+ }
320+
321+ memcpy(ptr, data, pic->header_size);
322+ ID3D12Resource_Unmap(pic->output_buffer, 0, NULL);
323+ }
324+
325+ d3d12_refs.NumTexture2Ds = base_pic->nb_refs[0] + base_pic->nb_refs[1];
326+ if (d3d12_refs.NumTexture2Ds) {
327+ d3d12_refs.ppTexture2Ds = av_calloc(d3d12_refs.NumTexture2Ds,
328+ sizeof(*d3d12_refs.ppTexture2Ds));
329+ if (!d3d12_refs.ppTexture2Ds) {
330+ err = AVERROR(ENOMEM);
331+ goto fail;
332+ }
333+
334+ i = 0;
335+ for (j = 0; j < base_pic->nb_refs[0]; j++)
336+ d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[0][j])->recon_surface->texture;
337+ for (j = 0; j < base_pic->nb_refs[1]; j++)
338+ d3d12_refs.ppTexture2Ds[i++] = ((D3D12VAEncodePicture *)base_pic->refs[1][j])->recon_surface->texture;
339+ }
340+
341+ input_args.PictureControlDesc.IntraRefreshFrameIndex = 0;
342+ if (base_pic->is_reference)
343+ input_args.PictureControlDesc.Flags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
344+
345+ input_args.PictureControlDesc.PictureControlCodecData = pic->pic_ctl;
346+ input_args.PictureControlDesc.ReferenceFrames = d3d12_refs;
347+ input_args.CurrentFrameBitstreamMetadataSize = pic->header_size;
348+
349+ output_args.Bitstream.pBuffer = pic->output_buffer;
350+ output_args.Bitstream.FrameStartOffset = pic->header_size;
351+ output_args.ReconstructedPicture.pReconstructedPicture = pic->recon_surface->texture;
352+ output_args.ReconstructedPicture.ReconstructedPictureSubresource = 0;
353+ output_args.EncoderOutputMetadata.pBuffer = pic->encoded_metadata;
354+ output_args.EncoderOutputMetadata.Offset = 0;
355+
356+ input_metadata.HWLayoutMetadata.pBuffer = pic->encoded_metadata;
357+ input_metadata.HWLayoutMetadata.Offset = 0;
358+
359+ output_metadata.ResolvedLayoutMetadata.pBuffer = pic->resolved_metadata;
360+ output_metadata.ResolvedLayoutMetadata.Offset = 0;
361+
362+ err = d3d12va_get_valid_command_allocator(avctx, &command_allocator);
363+ if (err < 0)
364+ goto fail;
365+
366+ hr = ID3D12CommandAllocator_Reset(command_allocator);
367+ if (FAILED(hr)) {
368+ err = AVERROR_UNKNOWN;
369+ goto fail;
370+ }
371+
372+ hr = ID3D12VideoEncodeCommandList2_Reset(cmd_list, command_allocator);
373+ if (FAILED(hr)) {
374+ err = AVERROR_UNKNOWN;
375+ goto fail;
376+ }
377+
378+#define TRANSITION_BARRIER(res, before, after) \
379+ (D3D12_RESOURCE_BARRIER) { \
380+ .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, \
381+ .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, \
382+ .Transition = { \
383+ .pResource = res, \
384+ .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, \
385+ .StateBefore = before, \
386+ .StateAfter = after, \
387+ }, \
388+ }
389+
390+ barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture,
391+ D3D12_RESOURCE_STATE_COMMON,
392+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
393+ barriers[1] = TRANSITION_BARRIER(pic->output_buffer,
394+ D3D12_RESOURCE_STATE_COMMON,
395+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE);
396+ barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture,
397+ D3D12_RESOURCE_STATE_COMMON,
398+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE);
399+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata,
400+ D3D12_RESOURCE_STATE_COMMON,
401+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE);
402+ barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata,
403+ D3D12_RESOURCE_STATE_COMMON,
404+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE);
405+
406+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers);
407+
408+ if (d3d12_refs.NumTexture2Ds) {
409+ D3D12_RESOURCE_BARRIER refs_barriers[3];
410+
411+ for (i = 0; i < d3d12_refs.NumTexture2Ds; i++)
412+ refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i],
413+ D3D12_RESOURCE_STATE_COMMON,
414+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
415+
416+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds,
417+ refs_barriers);
418+ }
419+
420+ ID3D12VideoEncodeCommandList2_EncodeFrame(cmd_list, ctx->encoder, ctx->encoder_heap,
421+ &input_args, &output_args);
422+
423+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata,
424+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
425+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
426+
427+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 1, &barriers[3]);
428+
429+ ID3D12VideoEncodeCommandList2_ResolveEncoderOutputMetadata(cmd_list, &input_metadata, &output_metadata);
430+
431+ if (d3d12_refs.NumTexture2Ds) {
432+ D3D12_RESOURCE_BARRIER refs_barriers[3];
433+
434+ for (i = 0; i < d3d12_refs.NumTexture2Ds; i++)
435+ refs_barriers[i] = TRANSITION_BARRIER(d3d12_refs.ppTexture2Ds[i],
436+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
437+ D3D12_RESOURCE_STATE_COMMON);
438+
439+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, d3d12_refs.NumTexture2Ds,
440+ refs_barriers);
441+ }
442+
443+ barriers[0] = TRANSITION_BARRIER(pic->input_surface->texture,
444+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
445+ D3D12_RESOURCE_STATE_COMMON);
446+ barriers[1] = TRANSITION_BARRIER(pic->output_buffer,
447+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
448+ D3D12_RESOURCE_STATE_COMMON);
449+ barriers[2] = TRANSITION_BARRIER(pic->recon_surface->texture,
450+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
451+ D3D12_RESOURCE_STATE_COMMON);
452+ barriers[3] = TRANSITION_BARRIER(pic->encoded_metadata,
453+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ,
454+ D3D12_RESOURCE_STATE_COMMON);
455+ barriers[4] = TRANSITION_BARRIER(pic->resolved_metadata,
456+ D3D12_RESOURCE_STATE_VIDEO_ENCODE_WRITE,
457+ D3D12_RESOURCE_STATE_COMMON);
458+
459+ ID3D12VideoEncodeCommandList2_ResourceBarrier(cmd_list, 5, barriers);
460+
461+ hr = ID3D12VideoEncodeCommandList2_Close(cmd_list);
462+ if (FAILED(hr)) {
463+ err = AVERROR_UNKNOWN;
464+ goto fail;
465+ }
466+
467+ hr = ID3D12CommandQueue_Wait(ctx->command_queue, pic->input_surface->sync_ctx.fence,
468+ pic->input_surface->sync_ctx.fence_value);
469+ if (FAILED(hr)) {
470+ err = AVERROR_UNKNOWN;
471+ goto fail;
472+ }
473+
474+ ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
475+
476+ hr = ID3D12CommandQueue_Signal(ctx->command_queue, pic->input_surface->sync_ctx.fence,
477+ ++pic->input_surface->sync_ctx.fence_value);
478+ if (FAILED(hr)) {
479+ err = AVERROR_UNKNOWN;
480+ goto fail;
481+ }
482+
483+ hr = ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value);
484+ if (FAILED(hr)) {
485+ err = AVERROR_UNKNOWN;
486+ goto fail;
487+ }
488+
489+ err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value);
490+ if (err < 0)
491+ goto fail;
492+
493+ pic->fence_value = ctx->sync_ctx.fence_value;
494+
495+ if (d3d12_refs.ppTexture2Ds)
496+ av_freep(&d3d12_refs.ppTexture2Ds);
497+
498+ return 0;
499+
500+fail:
501+ if (command_allocator)
502+ d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value);
503+
504+ if (d3d12_refs.ppTexture2Ds)
505+ av_freep(&d3d12_refs.ppTexture2Ds);
506+
507+ if (ctx->codec->free_picture_params)
508+ ctx->codec->free_picture_params(pic);
509+
510+ av_buffer_unref(&pic->output_buffer_ref);
511+ pic->output_buffer = NULL;
512+ D3D12_OBJECT_RELEASE(pic->encoded_metadata);
513+ D3D12_OBJECT_RELEASE(pic->resolved_metadata);
514+ return err;
515+}
516+
517+static int d3d12va_encode_discard(AVCodecContext *avctx,
518+ D3D12VAEncodePicture *pic)
519+{
520+ FFHWBaseEncodePicture *base_pic = &pic->base;
521+ d3d12va_encode_wait(avctx, pic);
522+
523+ if (pic->output_buffer_ref) {
524+ av_log(avctx, AV_LOG_DEBUG, "Discard output for pic "
525+ "%"PRId64"/%"PRId64".\n",
526+ base_pic->display_order, base_pic->encode_order);
527+
528+ av_buffer_unref(&pic->output_buffer_ref);
529+ pic->output_buffer = NULL;
530+ }
531+
532+ D3D12_OBJECT_RELEASE(pic->encoded_metadata);
533+ D3D12_OBJECT_RELEASE(pic->resolved_metadata);
534+
535+ return 0;
536+}
537+
538+static int d3d12va_encode_free_rc_params(AVCodecContext *avctx)
539+{
540+ D3D12VAEncodeContext *ctx = avctx->priv_data;
541+
542+ switch (ctx->rc.Mode)
543+ {
544+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP:
545+ av_freep(&ctx->rc.ConfigParams.pConfiguration_CQP);
546+ break;
547+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR:
548+ av_freep(&ctx->rc.ConfigParams.pConfiguration_CBR);
549+ break;
550+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR:
551+ av_freep(&ctx->rc.ConfigParams.pConfiguration_VBR);
552+ break;
553+ case D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR:
554+ av_freep(&ctx->rc.ConfigParams.pConfiguration_QVBR);
555+ break;
556+ default:
557+ break;
558+ }
559+
560+ return 0;
561+}
562+
563+static FFHWBaseEncodePicture *d3d12va_encode_alloc(AVCodecContext *avctx,
564+ const AVFrame *frame)
565+{
566+ D3D12VAEncodeContext *ctx = avctx->priv_data;
567+ D3D12VAEncodePicture *pic;
568+
569+ pic = av_mallocz(sizeof(*pic));
570+ if (!pic)
571+ return NULL;
572+
573+ if (ctx->codec->picture_priv_data_size > 0) {
574+ pic->base.priv_data = av_mallocz(ctx->codec->picture_priv_data_size);
575+ if (!pic->base.priv_data) {
576+ av_freep(&pic);
577+ return NULL;
578+ }
579+ }
580+
581+ pic->input_surface = (AVD3D12VAFrame *)frame->data[0];
582+
583+ return &pic->base;
584+}
585+
586+static int d3d12va_encode_free(AVCodecContext *avctx,
587+ FFHWBaseEncodePicture *base_pic)
588+{
589+ D3D12VAEncodeContext *ctx = avctx->priv_data;
590+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic;
591+
592+ if (base_pic->encode_issued)
593+ d3d12va_encode_discard(avctx, pic);
594+
595+ if (ctx->codec->free_picture_params)
596+ ctx->codec->free_picture_params(pic);
597+
598+ ff_hw_base_encode_free(base_pic);
599+
600+ av_free(pic);
601+
602+ return 0;
603+}
604+
605+static int d3d12va_encode_get_buffer_size(AVCodecContext *avctx,
606+ D3D12VAEncodePicture *pic, size_t *size)
607+{
608+ D3D12_VIDEO_ENCODER_OUTPUT_METADATA *meta = NULL;
609+ uint8_t *data;
610+ HRESULT hr;
611+ int err;
612+
613+ hr = ID3D12Resource_Map(pic->resolved_metadata, 0, NULL, (void **)&data);
614+ if (FAILED(hr)) {
615+ err = AVERROR_UNKNOWN;
616+ return err;
617+ }
618+
619+ meta = (D3D12_VIDEO_ENCODER_OUTPUT_METADATA *)data;
620+
621+ if (meta->EncodeErrorFlags != D3D12_VIDEO_ENCODER_ENCODE_ERROR_FLAG_NO_ERROR) {
622+ av_log(avctx, AV_LOG_ERROR, "Encode failed %"PRIu64"\n", meta->EncodeErrorFlags);
623+ err = AVERROR(EINVAL);
624+ return err;
625+ }
626+
627+ if (meta->EncodedBitstreamWrittenBytesCount == 0) {
628+ av_log(avctx, AV_LOG_ERROR, "No bytes were written to encoded bitstream\n");
629+ err = AVERROR(EINVAL);
630+ return err;
631+ }
632+
633+ *size = meta->EncodedBitstreamWrittenBytesCount;
634+
635+ ID3D12Resource_Unmap(pic->resolved_metadata, 0, NULL);
636+
637+ return 0;
638+}
639+
640+static int d3d12va_encode_get_coded_data(AVCodecContext *avctx,
641+ D3D12VAEncodePicture *pic, AVPacket *pkt)
642+{
643+ int err;
644+ uint8_t *ptr, *mapped_data;
645+ size_t total_size = 0;
646+ HRESULT hr;
647+
648+ err = d3d12va_encode_get_buffer_size(avctx, pic, &total_size);
649+ if (err < 0)
650+ goto end;
651+
652+ total_size += pic->header_size;
653+ av_log(avctx, AV_LOG_DEBUG, "Output buffer size %"PRId64"\n", total_size);
654+
655+ hr = ID3D12Resource_Map(pic->output_buffer, 0, NULL, (void **)&mapped_data);
656+ if (FAILED(hr)) {
657+ err = AVERROR_UNKNOWN;
658+ goto end;
659+ }
660+
661+ err = ff_get_encode_buffer(avctx, pkt, total_size, 0);
662+ if (err < 0)
663+ goto end;
664+ ptr = pkt->data;
665+
666+ memcpy(ptr, mapped_data, total_size);
667+
668+ ID3D12Resource_Unmap(pic->output_buffer, 0, NULL);
669+
670+end:
671+ av_buffer_unref(&pic->output_buffer_ref);
672+ pic->output_buffer = NULL;
673+ return err;
674+}
675+
676+static int d3d12va_encode_output(AVCodecContext *avctx,
677+ const FFHWBaseEncodePicture *base_pic, AVPacket *pkt)
678+{
679+ D3D12VAEncodePicture *pic = (D3D12VAEncodePicture *)base_pic;
680+ AVPacket *pkt_ptr = pkt;
681+ int err;
682+
683+ err = d3d12va_encode_wait(avctx, pic);
684+ if (err < 0)
685+ return err;
686+
687+ err = d3d12va_encode_get_coded_data(avctx, pic, pkt);
688+ if (err < 0)
689+ return err;
690+
691+ av_log(avctx, AV_LOG_DEBUG, "Output read for pic %"PRId64"/%"PRId64".\n",
692+ base_pic->display_order, base_pic->encode_order);
693+
694+ ff_hw_base_encode_set_output_property(avctx, (FFHWBaseEncodePicture *)base_pic, pkt_ptr, 0);
695+
696+ return 0;
697+}
698+
699+static int d3d12va_encode_set_profile(AVCodecContext *avctx)
700+{
701+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
702+ D3D12VAEncodeContext *ctx = avctx->priv_data;
703+ const D3D12VAEncodeProfile *profile;
704+ const AVPixFmtDescriptor *desc;
705+ int i, depth;
706+
707+ desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format);
708+ if (!desc) {
709+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
710+ base_ctx->input_frames->sw_format);
711+ return AVERROR(EINVAL);
712+ }
713+
714+ depth = desc->comp[0].depth;
715+ for (i = 1; i < desc->nb_components; i++) {
716+ if (desc->comp[i].depth != depth) {
717+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
718+ desc->name);
719+ return AVERROR(EINVAL);
720+ }
721+ }
722+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
723+ desc->name);
724+
725+ av_assert0(ctx->codec->profiles);
726+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
727+ AV_PROFILE_UNKNOWN); i++) {
728+ profile = &ctx->codec->profiles[i];
729+ if (depth != profile->depth ||
730+ desc->nb_components != profile->nb_components)
731+ continue;
732+ if (desc->nb_components > 1 &&
733+ (desc->log2_chroma_w != profile->log2_chroma_w ||
734+ desc->log2_chroma_h != profile->log2_chroma_h))
735+ continue;
736+ if (avctx->profile != profile->av_profile &&
737+ avctx->profile != AV_PROFILE_UNKNOWN)
738+ continue;
739+
740+ ctx->profile = profile;
741+ break;
742+ }
743+ if (!ctx->profile) {
744+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
745+ return AVERROR(ENOSYS);
746+ }
747+
748+ avctx->profile = profile->av_profile;
749+ return 0;
750+}
751+
752+static const D3D12VAEncodeRCMode d3d12va_encode_rc_modes[] = {
753+ // Bitrate Quality
754+ // | Maxrate | HRD/VBV
755+ { 0 }, // | | | |
756+ { RC_MODE_CQP, "CQP", 0, 0, 1, 0, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP },
757+ { RC_MODE_CBR, "CBR", 1, 0, 0, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CBR },
758+ { RC_MODE_VBR, "VBR", 1, 1, 0, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_VBR },
759+ { RC_MODE_QVBR, "QVBR", 1, 1, 1, 1, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_QVBR },
760+};
761+
762+static int check_rate_control_support(AVCodecContext *avctx, const D3D12VAEncodeRCMode *rc_mode)
763+{
764+ HRESULT hr;
765+ D3D12VAEncodeContext *ctx = avctx->priv_data;
766+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_rc_mode = {
767+ .Codec = ctx->codec->d3d12_codec,
768+ };
769+
770+ if (!rc_mode->d3d12_mode)
771+ return 0;
772+
773+ d3d12_rc_mode.IsSupported = 0;
774+ d3d12_rc_mode.RateControlMode = rc_mode->d3d12_mode;
775+
776+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3,
777+ D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE,
778+ &d3d12_rc_mode, sizeof(d3d12_rc_mode));
779+ if (FAILED(hr)) {
780+ av_log(avctx, AV_LOG_ERROR, "Failed to check rate control support.\n");
781+ return 0;
782+ }
783+
784+ return d3d12_rc_mode.IsSupported;
785+}
786+
787+static int d3d12va_encode_init_rate_control(AVCodecContext *avctx)
788+{
789+ D3D12VAEncodeContext *ctx = avctx->priv_data;
790+ int64_t rc_target_bitrate;
791+ int64_t rc_peak_bitrate;
792+ int rc_quality;
793+ int64_t hrd_buffer_size;
794+ int64_t hrd_initial_buffer_fullness;
795+ int fr_num, fr_den;
796+ const D3D12VAEncodeRCMode *rc_mode;
797+
798+ // Rate control mode selection:
799+ // * If the user has set a mode explicitly with the rc_mode option,
800+ // use it and fail if it is not available.
801+ // * If an explicit QP option has been set, use CQP.
802+ // * If the codec is CQ-only, use CQP.
803+ // * If the QSCALE avcodec option is set, use CQP.
804+ // * If bitrate and quality are both set, try QVBR.
805+ // * If quality is set, try CQP.
806+ // * If bitrate and maxrate are set and have the same value, try CBR.
807+ // * If a bitrate is set, try VBR, then CBR.
808+ // * If no bitrate is set, try CQP.
809+
810+#define TRY_RC_MODE(mode, fail) do { \
811+ rc_mode = &d3d12va_encode_rc_modes[mode]; \
812+ if (!(rc_mode->d3d12_mode && check_rate_control_support(avctx, rc_mode))) { \
813+ if (fail) { \
814+ av_log(avctx, AV_LOG_ERROR, "Driver does not support %s " \
815+ "RC mode.\n", rc_mode->name); \
816+ return AVERROR(EINVAL); \
817+ } \
818+ av_log(avctx, AV_LOG_DEBUG, "Driver does not support %s " \
819+ "RC mode.\n", rc_mode->name); \
820+ rc_mode = NULL; \
821+ } else { \
822+ goto rc_mode_found; \
823+ } \
824+ } while (0)
825+
826+ if (ctx->explicit_rc_mode)
827+ TRY_RC_MODE(ctx->explicit_rc_mode, 1);
828+
829+ if (ctx->explicit_qp)
830+ TRY_RC_MODE(RC_MODE_CQP, 1);
831+
832+ if (ctx->codec->flags & FF_HW_FLAG_CONSTANT_QUALITY_ONLY)
833+ TRY_RC_MODE(RC_MODE_CQP, 1);
834+
835+ if (avctx->flags & AV_CODEC_FLAG_QSCALE)
836+ TRY_RC_MODE(RC_MODE_CQP, 1);
837+
838+ if (avctx->bit_rate > 0 && avctx->global_quality > 0)
839+ TRY_RC_MODE(RC_MODE_QVBR, 0);
840+
841+ if (avctx->global_quality > 0) {
842+ TRY_RC_MODE(RC_MODE_CQP, 0);
843+ }
844+
845+ if (avctx->bit_rate > 0 && avctx->rc_max_rate == avctx->bit_rate)
846+ TRY_RC_MODE(RC_MODE_CBR, 0);
847+
848+ if (avctx->bit_rate > 0) {
849+ TRY_RC_MODE(RC_MODE_VBR, 0);
850+ TRY_RC_MODE(RC_MODE_CBR, 0);
851+ } else {
852+ TRY_RC_MODE(RC_MODE_CQP, 0);
853+ }
854+
855+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
856+ "RC mode compatible with selected options.\n");
857+ return AVERROR(EINVAL);
858+
859+rc_mode_found:
860+ if (rc_mode->bitrate) {
861+ if (avctx->bit_rate <= 0) {
862+ av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s "
863+ "RC mode.\n", rc_mode->name);
864+ return AVERROR(EINVAL);
865+ }
866+
867+ if (rc_mode->maxrate) {
868+ if (avctx->rc_max_rate > 0) {
869+ if (avctx->rc_max_rate < avctx->bit_rate) {
870+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: "
871+ "bitrate (%"PRId64") must not be greater than "
872+ "maxrate (%"PRId64").\n", avctx->bit_rate,
873+ avctx->rc_max_rate);
874+ return AVERROR(EINVAL);
875+ }
876+ rc_target_bitrate = avctx->bit_rate;
877+ rc_peak_bitrate = avctx->rc_max_rate;
878+ } else {
879+ // We only have a target bitrate, but this mode requires
880+ // that a maximum rate be supplied as well. Since the
881+ // user does not want this to be a constraint, arbitrarily
882+ // pick a maximum rate of double the target rate.
883+ rc_target_bitrate = avctx->bit_rate;
884+ rc_peak_bitrate = 2 * avctx->bit_rate;
885+ }
886+ } else {
887+ if (avctx->rc_max_rate > avctx->bit_rate) {
888+ av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored "
889+ "in %s RC mode.\n", rc_mode->name);
890+ }
891+ rc_target_bitrate = avctx->bit_rate;
892+ rc_peak_bitrate = 0;
893+ }
894+ } else {
895+ rc_target_bitrate = 0;
896+ rc_peak_bitrate = 0;
897+ }
898+
899+ if (rc_mode->quality) {
900+ if (ctx->explicit_qp) {
901+ rc_quality = ctx->explicit_qp;
902+ } else if (avctx->global_quality > 0) {
903+ if (avctx->flags & AV_CODEC_FLAG_QSCALE)
904+ rc_quality = avctx->global_quality / FF_QP2LAMBDA;
905+ else
906+ rc_quality = avctx->global_quality;
907+ } else {
908+ rc_quality = ctx->codec->default_quality;
909+ av_log(avctx, AV_LOG_WARNING, "No quality level set; "
910+ "using default (%d).\n", rc_quality);
911+ }
912+ } else {
913+ rc_quality = 0;
914+ }
915+
916+ if (rc_mode->hrd) {
917+ if (avctx->rc_buffer_size)
918+ hrd_buffer_size = avctx->rc_buffer_size;
919+ else if (avctx->rc_max_rate > 0)
920+ hrd_buffer_size = avctx->rc_max_rate;
921+ else
922+ hrd_buffer_size = avctx->bit_rate;
923+ if (avctx->rc_initial_buffer_occupancy) {
924+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
925+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
926+ "must have initial buffer size (%d) <= "
927+ "buffer size (%"PRId64").\n",
928+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
929+ return AVERROR(EINVAL);
930+ }
931+ hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
932+ } else {
933+ hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
934+ }
935+ } else {
936+ if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) {
937+ av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored "
938+ "in %s RC mode.\n", rc_mode->name);
939+ }
940+
941+ hrd_buffer_size = 0;
942+ hrd_initial_buffer_fullness = 0;
943+ }
944+
945+ if (rc_target_bitrate > UINT32_MAX ||
946+ hrd_buffer_size > UINT32_MAX ||
947+ hrd_initial_buffer_fullness > UINT32_MAX) {
948+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
949+ "greater are not supported by D3D12.\n");
950+ return AVERROR(EINVAL);
951+ }
952+
953+ ctx->rc_quality = rc_quality;
954+
955+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name);
956+
957+ if (rc_mode->quality)
958+ av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_quality);
959+
960+ if (rc_mode->hrd) {
961+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
962+ "initial fullness %"PRId64" bits.\n",
963+ hrd_buffer_size, hrd_initial_buffer_fullness);
964+ }
965+
966+ if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
967+ av_reduce(&fr_num, &fr_den,
968+ avctx->framerate.num, avctx->framerate.den, 65535);
969+ else
970+ av_reduce(&fr_num, &fr_den,
971+ avctx->time_base.den, avctx->time_base.num, 65535);
972+
973+ av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n",
974+ fr_num, fr_den, (double)fr_num / fr_den);
975+
976+ ctx->rc.Flags = D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE;
977+ ctx->rc.TargetFrameRate.Numerator = fr_num;
978+ ctx->rc.TargetFrameRate.Denominator = fr_den;
979+ ctx->rc.Mode = rc_mode->d3d12_mode;
980+
981+ switch (rc_mode->mode) {
982+ case RC_MODE_CQP:
983+ // cqp ConfigParams will be updated in ctx->codec->configure.
984+ break;
985+
986+ case RC_MODE_CBR:
987+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR *cbr_ctl;
988+
989+ ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR);
990+ cbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize);
991+ if (!cbr_ctl)
992+ return AVERROR(ENOMEM);
993+
994+ cbr_ctl->TargetBitRate = rc_target_bitrate;
995+ cbr_ctl->VBVCapacity = hrd_buffer_size;
996+ cbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness;
997+ ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
998+
999+ if (avctx->qmin > 0 || avctx->qmax > 0) {
1000+ cbr_ctl->MinQP = avctx->qmin;
1001+ cbr_ctl->MaxQP = avctx->qmax;
1002+ ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
1003+ }
1004+
1005+ ctx->rc.ConfigParams.pConfiguration_CBR = cbr_ctl;
1006+ break;
1007+
1008+ case RC_MODE_VBR:
1009+ D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR *vbr_ctl;
1010+
1011+ ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR);
1012+ vbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize);
1013+ if (!vbr_ctl)
1014+ return AVERROR(ENOMEM);
1015+
1016+ vbr_ctl->TargetAvgBitRate = rc_target_bitrate;
1017+ vbr_ctl->PeakBitRate = rc_peak_bitrate;
1018+ vbr_ctl->VBVCapacity = hrd_buffer_size;
1019+ vbr_ctl->InitialVBVFullness = hrd_initial_buffer_fullness;
1020+ ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_VBV_SIZES;
1021+
1022+ if (avctx->qmin > 0 || avctx->qmax > 0) {
1023+ vbr_ctl->MinQP = avctx->qmin;
1024+ vbr_ctl->MaxQP = avctx->qmax;
1025+ ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
1026+ }
1027+
1028+ ctx->rc.ConfigParams.pConfiguration_VBR = vbr_ctl;
1029+ break;
1030+
1031+ case RC_MODE_QVBR:
1032+ D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR *qvbr_ctl;
1033+
1034+ ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR);
1035+ qvbr_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize);
1036+ if (!qvbr_ctl)
1037+ return AVERROR(ENOMEM);
1038+
1039+ qvbr_ctl->TargetAvgBitRate = rc_target_bitrate;
1040+ qvbr_ctl->PeakBitRate = rc_peak_bitrate;
1041+ qvbr_ctl->ConstantQualityTarget = rc_quality;
1042+
1043+ if (avctx->qmin > 0 || avctx->qmax > 0) {
1044+ qvbr_ctl->MinQP = avctx->qmin;
1045+ qvbr_ctl->MaxQP = avctx->qmax;
1046+ ctx->rc.Flags |= D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_ENABLE_QP_RANGE;
1047+ }
1048+
1049+ ctx->rc.ConfigParams.pConfiguration_QVBR = qvbr_ctl;
1050+ break;
1051+
1052+ default:
1053+ break;
1054+ }
1055+ return 0;
1056+}
1057+
1058+static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx)
1059+{
1060+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1061+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1062+ uint32_t ref_l0, ref_l1;
1063+ int err;
1064+ HRESULT hr;
1065+ D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT support;
1066+ union {
1067+ D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 h264;
1068+ D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_HEVC hevc;
1069+ } codec_support;
1070+
1071+ support.NodeIndex = 0;
1072+ support.Codec = ctx->codec->d3d12_codec;
1073+ support.Profile = ctx->profile->d3d12_profile;
1074+
1075+ switch (ctx->codec->d3d12_codec) {
1076+ case D3D12_VIDEO_ENCODER_CODEC_H264:
1077+ support.PictureSupport.DataSize = sizeof(codec_support.h264);
1078+ support.PictureSupport.pH264Support = &codec_support.h264;
1079+ break;
1080+
1081+ case D3D12_VIDEO_ENCODER_CODEC_HEVC:
1082+ support.PictureSupport.DataSize = sizeof(codec_support.hevc);
1083+ support.PictureSupport.pHEVCSupport = &codec_support.hevc;
1084+ break;
1085+
1086+ default:
1087+ av_assert0(0);
1088+ }
1089+
1090+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT,
1091+ &support, sizeof(support));
1092+ if (FAILED(hr))
1093+ return AVERROR(EINVAL);
1094+
1095+ if (support.IsSupported) {
1096+ switch (ctx->codec->d3d12_codec) {
1097+ case D3D12_VIDEO_ENCODER_CODEC_H264:
1098+ ref_l0 = FFMIN(support.PictureSupport.pH264Support->MaxL0ReferencesForP,
1099+ support.PictureSupport.pH264Support->MaxL1ReferencesForB);
1100+ ref_l1 = support.PictureSupport.pH264Support->MaxL1ReferencesForB;
1101+ break;
1102+
1103+ case D3D12_VIDEO_ENCODER_CODEC_HEVC:
1104+ ref_l0 = FFMIN(support.PictureSupport.pHEVCSupport->MaxL0ReferencesForP,
1105+ support.PictureSupport.pHEVCSupport->MaxL1ReferencesForB);
1106+ ref_l1 = support.PictureSupport.pHEVCSupport->MaxL1ReferencesForB;
1107+ break;
1108+
1109+ default:
1110+ av_assert0(0);
1111+ }
1112+ } else {
1113+ ref_l0 = ref_l1 = 0;
1114+ }
1115+
1116+ if (ref_l0 > 0 && ref_l1 > 0 && ctx->bi_not_empty) {
1117+ base_ctx->p_to_gpb = 1;
1118+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support P-frames, "
1119+ "replacing them with B-frames.\n");
1120+ }
1121+
1122+ err = ff_hw_base_init_gop_structure(avctx, ref_l0, ref_l1, ctx->codec->flags, 0);
1123+ if (err < 0)
1124+ return err;
1125+
1126+ return 0;
1127+}
1128+
1129+static int d3d12va_create_encoder(AVCodecContext *avctx)
1130+{
1131+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1132+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1133+ AVD3D12VAFramesContext *frames_hwctx = base_ctx->input_frames->hwctx;
1134+ HRESULT hr;
1135+
1136+ D3D12_VIDEO_ENCODER_DESC desc = {
1137+ .NodeMask = 0,
1138+ .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE,
1139+ .EncodeCodec = ctx->codec->d3d12_codec,
1140+ .EncodeProfile = ctx->profile->d3d12_profile,
1141+ .InputFormat = frames_hwctx->format,
1142+ .CodecConfiguration = ctx->codec_conf,
1143+ .MaxMotionEstimationPrecision = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM,
1144+ };
1145+
1146+ hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, &IID_ID3D12VideoEncoder,
1147+ (void **)&ctx->encoder);
1148+ if (FAILED(hr)) {
1149+ av_log(avctx, AV_LOG_ERROR, "Failed to create encoder.\n");
1150+ return AVERROR(EINVAL);
1151+ }
1152+
1153+ return 0;
1154+}
1155+
1156+static int d3d12va_create_encoder_heap(AVCodecContext *avctx)
1157+{
1158+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1159+ HRESULT hr;
1160+
1161+ D3D12_VIDEO_ENCODER_HEAP_DESC desc = {
1162+ .NodeMask = 0,
1163+ .Flags = D3D12_VIDEO_ENCODER_FLAG_NONE,
1164+ .EncodeCodec = ctx->codec->d3d12_codec,
1165+ .EncodeProfile = ctx->profile->d3d12_profile,
1166+ .EncodeLevel = ctx->level,
1167+ .ResolutionsListCount = 1,
1168+ .pResolutionList = &ctx->resolution,
1169+ };
1170+
1171+ hr = ID3D12VideoDevice3_CreateVideoEncoderHeap(ctx->video_device3, &desc,
1172+ &IID_ID3D12VideoEncoderHeap, (void **)&ctx->encoder_heap);
1173+ if (FAILED(hr)) {
1174+ av_log(avctx, AV_LOG_ERROR, "Failed to create encoder heap.\n");
1175+ return AVERROR(EINVAL);
1176+ }
1177+
1178+ return 0;
1179+}
1180+
1181+static void d3d12va_encode_free_buffer(void *opaque, uint8_t *data)
1182+{
1183+ ID3D12Resource *pResource;
1184+
1185+ pResource = (ID3D12Resource *)data;
1186+ D3D12_OBJECT_RELEASE(pResource);
1187+}
1188+
1189+static AVBufferRef *d3d12va_encode_alloc_output_buffer(void *opaque, size_t size)
1190+{
1191+ AVCodecContext *avctx = opaque;
1192+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1193+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1194+ ID3D12Resource *pResource = NULL;
1195+ HRESULT hr;
1196+ AVBufferRef *ref;
1197+ D3D12_HEAP_PROPERTIES heap_props;
1198+ D3D12_HEAP_TYPE heap_type = D3D12_HEAP_TYPE_READBACK;
1199+
1200+ D3D12_RESOURCE_DESC desc = {
1201+ .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
1202+ .Alignment = 0,
1203+ .Width = FFALIGN(3 * base_ctx->surface_width * base_ctx->surface_height + (1 << 16),
1204+ D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT),
1205+ .Height = 1,
1206+ .DepthOrArraySize = 1,
1207+ .MipLevels = 1,
1208+ .Format = DXGI_FORMAT_UNKNOWN,
1209+ .SampleDesc = { .Count = 1, .Quality = 0 },
1210+ .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
1211+ .Flags = D3D12_RESOURCE_FLAG_NONE,
1212+ };
1213+
1214+ ctx->hwctx->device->lpVtbl->GetCustomHeapProperties(ctx->hwctx->device, &heap_props, 0, heap_type);
1215+
1216+ hr = ID3D12Device_CreateCommittedResource(ctx->hwctx->device, &heap_props, D3D12_HEAP_FLAG_NONE,
1217+ &desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource,
1218+ (void **)&pResource);
1219+
1220+ if (FAILED(hr)) {
1221+ av_log(avctx, AV_LOG_ERROR, "Failed to create d3d12 buffer.\n");
1222+ return NULL;
1223+ }
1224+
1225+ ref = av_buffer_create((uint8_t *)(uintptr_t)pResource,
1226+ sizeof(pResource),
1227+ &d3d12va_encode_free_buffer,
1228+ avctx, AV_BUFFER_FLAG_READONLY);
1229+ if (!ref) {
1230+ D3D12_OBJECT_RELEASE(pResource);
1231+ return NULL;
1232+ }
1233+
1234+ return ref;
1235+}
1236+
1237+static int d3d12va_encode_prepare_output_buffers(AVCodecContext *avctx)
1238+{
1239+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1240+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1241+ AVD3D12VAFramesContext *frames_ctx = base_ctx->input_frames->hwctx;
1242+ HRESULT hr;
1243+
1244+ ctx->req.NodeIndex = 0;
1245+ ctx->req.Codec = ctx->codec->d3d12_codec;
1246+ ctx->req.Profile = ctx->profile->d3d12_profile;
1247+ ctx->req.InputFormat = frames_ctx->format;
1248+ ctx->req.PictureTargetResolution = ctx->resolution;
1249+
1250+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3,
1251+ D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
1252+ &ctx->req, sizeof(ctx->req));
1253+ if (FAILED(hr)) {
1254+ av_log(avctx, AV_LOG_ERROR, "Failed to check encoder resource requirements support.\n");
1255+ return AVERROR(EINVAL);
1256+ }
1257+
1258+ if (!ctx->req.IsSupported) {
1259+ av_log(avctx, AV_LOG_ERROR, "Encoder resource requirements unsupported.\n");
1260+ return AVERROR(EINVAL);
1261+ }
1262+
1263+ ctx->output_buffer_pool = av_buffer_pool_init2(sizeof(ID3D12Resource *), avctx,
1264+ &d3d12va_encode_alloc_output_buffer, NULL);
1265+ if (!ctx->output_buffer_pool)
1266+ return AVERROR(ENOMEM);
1267+
1268+ return 0;
1269+}
1270+
1271+static int d3d12va_encode_create_command_objects(AVCodecContext *avctx)
1272+{
1273+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1274+ ID3D12CommandAllocator *command_allocator = NULL;
1275+ int err;
1276+ HRESULT hr;
1277+
1278+ D3D12_COMMAND_QUEUE_DESC queue_desc = {
1279+ .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
1280+ .Priority = 0,
1281+ .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
1282+ .NodeMask = 0,
1283+ };
1284+
1285+ ctx->allocator_queue = av_fifo_alloc2(D3D12VA_VIDEO_ENC_ASYNC_DEPTH,
1286+ sizeof(CommandAllocator), AV_FIFO_FLAG_AUTO_GROW);
1287+ if (!ctx->allocator_queue)
1288+ return AVERROR(ENOMEM);
1289+
1290+ hr = ID3D12Device_CreateFence(ctx->hwctx->device, 0, D3D12_FENCE_FLAG_NONE,
1291+ &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence);
1292+ if (FAILED(hr)) {
1293+ av_log(avctx, AV_LOG_ERROR, "Failed to create fence(%lx)\n", (long)hr);
1294+ err = AVERROR_UNKNOWN;
1295+ goto fail;
1296+ }
1297+
1298+ ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
1299+ if (!ctx->sync_ctx.event)
1300+ goto fail;
1301+
1302+ err = d3d12va_get_valid_command_allocator(avctx, &command_allocator);
1303+ if (err < 0)
1304+ goto fail;
1305+
1306+ hr = ID3D12Device_CreateCommandQueue(ctx->hwctx->device, &queue_desc,
1307+ &IID_ID3D12CommandQueue, (void **)&ctx->command_queue);
1308+ if (FAILED(hr)) {
1309+ av_log(avctx, AV_LOG_ERROR, "Failed to create command queue(%lx)\n", (long)hr);
1310+ err = AVERROR_UNKNOWN;
1311+ goto fail;
1312+ }
1313+
1314+ hr = ID3D12Device_CreateCommandList(ctx->hwctx->device, 0, queue_desc.Type,
1315+ command_allocator, NULL, &IID_ID3D12CommandList,
1316+ (void **)&ctx->command_list);
1317+ if (FAILED(hr)) {
1318+ av_log(avctx, AV_LOG_ERROR, "Failed to create command list(%lx)\n", (long)hr);
1319+ err = AVERROR_UNKNOWN;
1320+ goto fail;
1321+ }
1322+
1323+ hr = ID3D12VideoEncodeCommandList2_Close(ctx->command_list);
1324+ if (FAILED(hr)) {
1325+ av_log(avctx, AV_LOG_ERROR, "Failed to close the command list(%lx)\n", (long)hr);
1326+ err = AVERROR_UNKNOWN;
1327+ goto fail;
1328+ }
1329+
1330+ ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
1331+
1332+ err = d3d12va_sync_with_gpu(avctx);
1333+ if (err < 0)
1334+ goto fail;
1335+
1336+ err = d3d12va_discard_command_allocator(avctx, command_allocator, ctx->sync_ctx.fence_value);
1337+ if (err < 0)
1338+ goto fail;
1339+
1340+ return 0;
1341+
1342+fail:
1343+ D3D12_OBJECT_RELEASE(command_allocator);
1344+ return err;
1345+}
1346+
1347+static int d3d12va_encode_create_recon_frames(AVCodecContext *avctx)
1348+{
1349+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1350+ AVD3D12VAFramesContext *hwctx;
1351+ enum AVPixelFormat recon_format;
1352+ int err;
1353+
1354+ err = ff_hw_base_get_recon_format(avctx, NULL, &recon_format);
1355+ if (err < 0)
1356+ return err;
1357+
1358+ base_ctx->recon_frames_ref = av_hwframe_ctx_alloc(base_ctx->device_ref);
1359+ if (!base_ctx->recon_frames_ref)
1360+ return AVERROR(ENOMEM);
1361+
1362+ base_ctx->recon_frames = (AVHWFramesContext *)base_ctx->recon_frames_ref->data;
1363+ hwctx = (AVD3D12VAFramesContext *)base_ctx->recon_frames->hwctx;
1364+
1365+ base_ctx->recon_frames->format = AV_PIX_FMT_D3D12;
1366+ base_ctx->recon_frames->sw_format = recon_format;
1367+ base_ctx->recon_frames->width = base_ctx->surface_width;
1368+ base_ctx->recon_frames->height = base_ctx->surface_height;
1369+
1370+ hwctx->flags = D3D12_RESOURCE_FLAG_VIDEO_ENCODE_REFERENCE_ONLY |
1371+ D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
1372+
1373+ err = av_hwframe_ctx_init(base_ctx->recon_frames_ref);
1374+ if (err < 0) {
1375+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise reconstructed "
1376+ "frame context: %d.\n", err);
1377+ return err;
1378+ }
1379+
1380+ return 0;
1381+}
1382+
1383+static const FFHWEncodePictureOperation d3d12va_type = {
1384+ .alloc = &d3d12va_encode_alloc,
1385+
1386+ .issue = &d3d12va_encode_issue,
1387+
1388+ .output = &d3d12va_encode_output,
1389+
1390+ .free = &d3d12va_encode_free,
1391+};
1392+
1393+int ff_d3d12va_encode_init(AVCodecContext *avctx)
1394+{
1395+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1396+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1397+ D3D12_FEATURE_DATA_VIDEO_FEATURE_AREA_SUPPORT support = { 0 };
1398+ int err;
1399+ HRESULT hr;
1400+
1401+ err = ff_hw_base_encode_init(avctx);
1402+ if (err < 0)
1403+ goto fail;
1404+
1405+ base_ctx->op = &d3d12va_type;
1406+
1407+ ctx->hwctx = base_ctx->device->hwctx;
1408+
1409+ ctx->resolution.Width = base_ctx->input_frames->width;
1410+ ctx->resolution.Height = base_ctx->input_frames->height;
1411+
1412+ hr = ID3D12Device_QueryInterface(ctx->hwctx->device, &IID_ID3D12Device3, (void **)&ctx->device3);
1413+ if (FAILED(hr)) {
1414+ av_log(avctx, AV_LOG_ERROR, "ID3D12Device3 interface is not supported.\n");
1415+ err = AVERROR_UNKNOWN;
1416+ goto fail;
1417+ }
1418+
1419+ hr = ID3D12Device3_QueryInterface(ctx->device3, &IID_ID3D12VideoDevice3, (void **)&ctx->video_device3);
1420+ if (FAILED(hr)) {
1421+ av_log(avctx, AV_LOG_ERROR, "ID3D12VideoDevice3 interface is not supported.\n");
1422+ err = AVERROR_UNKNOWN;
1423+ goto fail;
1424+ }
1425+
1426+ if (FAILED(ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_FEATURE_AREA_SUPPORT,
1427+ &support, sizeof(support))) && !support.VideoEncodeSupport) {
1428+ av_log(avctx, AV_LOG_ERROR, "D3D12 video device has no video encoder support.\n");
1429+ err = AVERROR(EINVAL);
1430+ goto fail;
1431+ }
1432+
1433+ err = d3d12va_encode_set_profile(avctx);
1434+ if (err < 0)
1435+ goto fail;
1436+
1437+ err = d3d12va_encode_init_rate_control(avctx);
1438+ if (err < 0)
1439+ goto fail;
1440+
1441+ if (ctx->codec->get_encoder_caps) {
1442+ err = ctx->codec->get_encoder_caps(avctx);
1443+ if (err < 0)
1444+ goto fail;
1445+ }
1446+
1447+ err = d3d12va_encode_init_gop_structure(avctx);
1448+ if (err < 0)
1449+ goto fail;
1450+
1451+ if (!(ctx->codec->flags & FF_HW_FLAG_SLICE_CONTROL) && avctx->slices > 0) {
1452+ av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested "
1453+ "but this codec does not support controlling slices.\n");
1454+ }
1455+
1456+ err = d3d12va_encode_create_command_objects(avctx);
1457+ if (err < 0)
1458+ goto fail;
1459+
1460+ err = d3d12va_encode_create_recon_frames(avctx);
1461+ if (err < 0)
1462+ goto fail;
1463+
1464+ err = d3d12va_encode_prepare_output_buffers(avctx);
1465+ if (err < 0)
1466+ goto fail;
1467+
1468+ if (ctx->codec->configure) {
1469+ err = ctx->codec->configure(avctx);
1470+ if (err < 0)
1471+ goto fail;
1472+ }
1473+
1474+ if (ctx->codec->init_sequence_params) {
1475+ err = ctx->codec->init_sequence_params(avctx);
1476+ if (err < 0) {
1477+ av_log(avctx, AV_LOG_ERROR, "Codec sequence initialisation "
1478+ "failed: %d.\n", err);
1479+ goto fail;
1480+ }
1481+ }
1482+
1483+ if (ctx->codec->set_level) {
1484+ err = ctx->codec->set_level(avctx);
1485+ if (err < 0)
1486+ goto fail;
1487+ }
1488+
1489+ base_ctx->output_delay = base_ctx->b_per_p;
1490+ base_ctx->decode_delay = base_ctx->max_b_depth;
1491+
1492+ err = d3d12va_create_encoder(avctx);
1493+ if (err < 0)
1494+ goto fail;
1495+
1496+ err = d3d12va_create_encoder_heap(avctx);
1497+ if (err < 0)
1498+ goto fail;
1499+
1500+ base_ctx->async_encode = 1;
1501+ base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth,
1502+ sizeof(D3D12VAEncodePicture *), 0);
1503+ if (!base_ctx->encode_fifo)
1504+ return AVERROR(ENOMEM);
1505+
1506+ return 0;
1507+
1508+fail:
1509+ return err;
1510+}
1511+
1512+int ff_d3d12va_encode_close(AVCodecContext *avctx)
1513+{
1514+ int num_allocator = 0;
1515+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
1516+ D3D12VAEncodeContext *ctx = avctx->priv_data;
1517+ FFHWBaseEncodePicture *pic, *next;
1518+ CommandAllocator allocator;
1519+
1520+ if (!base_ctx->frame)
1521+ return 0;
1522+
1523+ for (pic = base_ctx->pic_start; pic; pic = next) {
1524+ next = pic->next;
1525+ d3d12va_encode_free(avctx, pic);
1526+ }
1527+
1528+ d3d12va_encode_free_rc_params(avctx);
1529+
1530+ av_buffer_pool_uninit(&ctx->output_buffer_pool);
1531+
1532+ D3D12_OBJECT_RELEASE(ctx->command_list);
1533+ D3D12_OBJECT_RELEASE(ctx->command_queue);
1534+
1535+ if (ctx->allocator_queue) {
1536+ while (av_fifo_read(ctx->allocator_queue, &allocator, 1) >= 0) {
1537+ num_allocator++;
1538+ D3D12_OBJECT_RELEASE(allocator.command_allocator);
1539+ }
1540+
1541+ av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator);
1542+ }
1543+
1544+ av_fifo_freep2(&ctx->allocator_queue);
1545+
1546+ D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence);
1547+ if (ctx->sync_ctx.event)
1548+ CloseHandle(ctx->sync_ctx.event);
1549+
1550+ D3D12_OBJECT_RELEASE(ctx->encoder_heap);
1551+ D3D12_OBJECT_RELEASE(ctx->encoder);
1552+ D3D12_OBJECT_RELEASE(ctx->video_device3);
1553+ D3D12_OBJECT_RELEASE(ctx->device3);
1554+
1555+ ff_hw_base_encode_close(avctx);
1556+
1557+ return 0;
1558+}
libavcodec/d3d12va_encode.h+334−0
@@ -0,0 +1,334 @@
1+/*
2+ * Direct3D 12 HW acceleration video encoder
3+ *
4+ * Copyright (c) 2024 Intel Corporation
5+ *
6+ * This file is part of FFmpeg.
7+ *
8+ * FFmpeg is free software; you can redistribute it and/or
9+ * modify it under the terms of the GNU Lesser General Public
10+ * License as published by the Free Software Foundation; either
11+ * version 2.1 of the License, or (at your option) any later version.
12+ *
13+ * FFmpeg is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+ * Lesser General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU Lesser General Public
19+ * License along with FFmpeg; if not, write to the Free Software
20+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+ */
22+
23+#ifndef AVCODEC_D3D12VA_ENCODE_H
24+#define AVCODEC_D3D12VA_ENCODE_H
25+
26+#include "libavutil/fifo.h"
27+#include "libavutil/hwcontext.h"
28+#include "libavutil/hwcontext_d3d12va_internal.h"
29+#include "libavutil/hwcontext_d3d12va.h"
30+#include "avcodec.h"
31+#include "internal.h"
32+#include "hwconfig.h"
33+#include "hw_base_encode.h"
34+
35+struct D3D12VAEncodeType;
36+
37+extern const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[];
38+
39+#define MAX_PARAM_BUFFER_SIZE 4096
40+#define D3D12VA_VIDEO_ENC_ASYNC_DEPTH 8
41+
42+typedef struct D3D12VAEncodePicture {
43+ FFHWBaseEncodePicture base;
44+
45+ int header_size;
46+
47+ AVD3D12VAFrame *input_surface;
48+ AVD3D12VAFrame *recon_surface;
49+
50+ AVBufferRef *output_buffer_ref;
51+ ID3D12Resource *output_buffer;
52+
53+ ID3D12Resource *encoded_metadata;
54+ ID3D12Resource *resolved_metadata;
55+
56+ D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA pic_ctl;
57+
58+ int fence_value;
59+} D3D12VAEncodePicture;
60+
61+typedef struct D3D12VAEncodeProfile {
62+ /**
63+ * lavc profile value (AV_PROFILE_*).
64+ */
65+ int av_profile;
66+
67+ /**
68+ * Supported bit depth.
69+ */
70+ int depth;
71+
72+ /**
73+ * Number of components.
74+ */
75+ int nb_components;
76+
77+ /**
78+ * Chroma subsampling in width dimension.
79+ */
80+ int log2_chroma_w;
81+
82+ /**
83+ * Chroma subsampling in height dimension.
84+ */
85+ int log2_chroma_h;
86+
87+ /**
88+ * D3D12 profile value.
89+ */
90+ D3D12_VIDEO_ENCODER_PROFILE_DESC d3d12_profile;
91+} D3D12VAEncodeProfile;
92+
93+enum {
94+ RC_MODE_AUTO,
95+ RC_MODE_CQP,
96+ RC_MODE_CBR,
97+ RC_MODE_VBR,
98+ RC_MODE_QVBR,
99+ RC_MODE_MAX = RC_MODE_QVBR,
100+};
101+
102+
103+typedef struct D3D12VAEncodeRCMode {
104+ /**
105+ * Mode from above enum (RC_MODE_*).
106+ */
107+ int mode;
108+
109+ /**
110+ * Name.
111+ *
112+ */
113+ const char *name;
114+
115+ /**
116+ * Uses bitrate parameters.
117+ *
118+ */
119+ int bitrate;
120+
121+ /**
122+ * Supports maxrate distinct from bitrate.
123+ *
124+ */
125+ int maxrate;
126+
127+ /**
128+ * Uses quality value.
129+ *
130+ */
131+ int quality;
132+
133+ /**
134+ * Supports HRD/VBV parameters.
135+ *
136+ */
137+ int hrd;
138+
139+ /**
140+ * D3D12 mode value.
141+ */
142+ D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE d3d12_mode;
143+} D3D12VAEncodeRCMode;
144+
145+typedef struct D3D12VAEncodeContext {
146+ FFHWBaseEncodeContext base;
147+
148+ /**
149+ * Codec-specific hooks.
150+ */
151+ const struct D3D12VAEncodeType *codec;
152+
153+ /**
154+ * Explicitly set RC mode (otherwise attempt to pick from
155+ * available modes).
156+ */
157+ int explicit_rc_mode;
158+
159+ /**
160+ * Explicitly-set QP, for use with the "qp" options.
161+ * (Forces CQP mode when set, overriding everything else.)
162+ */
163+ int explicit_qp;
164+
165+ /**
166+ * RC quality level - meaning depends on codec and RC mode.
167+ * In CQP mode this sets the fixed quantiser value.
168+ */
169+ int rc_quality;
170+
171+ /**
172+ * Chosen encoding profile details.
173+ */
174+ const D3D12VAEncodeProfile *profile;
175+
176+ AVD3D12VADeviceContext *hwctx;
177+
178+ /**
179+ * ID3D12Device3 interface.
180+ */
181+ ID3D12Device3 *device3;
182+
183+ /**
184+ * ID3D12VideoDevice3 interface.
185+ */
186+ ID3D12VideoDevice3 *video_device3;
187+
188+ /**
189+ * Pool of (reusable) bitstream output buffers.
190+ */
191+ AVBufferPool *output_buffer_pool;
192+
193+ /**
194+ * D3D12 video encoder.
195+ */
196+ AVBufferRef *encoder_ref;
197+
198+ ID3D12VideoEncoder *encoder;
199+
200+ /**
201+ * D3D12 video encoder heap.
202+ */
203+ ID3D12VideoEncoderHeap *encoder_heap;
204+
205+ /**
206+ * A cached queue for reusing the D3D12 command allocators.
207+ *
208+ * @see https://learn.microsoft.com/en-us/windows/win32/direct3d12/recording-command-lists-and-bundles#id3d12commandallocator
209+ */
210+ AVFifo *allocator_queue;
211+
212+ /**
213+ * D3D12 command queue.
214+ */
215+ ID3D12CommandQueue *command_queue;
216+
217+ /**
218+ * D3D12 video encode command list.
219+ */
220+ ID3D12VideoEncodeCommandList2 *command_list;
221+
222+ /**
223+ * The sync context used to sync command queue.
224+ */
225+ AVD3D12VASyncContext sync_ctx;
226+
227+ /**
228+ * The bi_not_empty feature.
229+ */
230+ int bi_not_empty;
231+
232+ /**
233+ * D3D12_FEATURE structures.
234+ */
235+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req;
236+
237+ D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS res_limits;
238+
239+ /**
240+ * D3D12_VIDEO_ENCODER structures.
241+ */
242+ D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC resolution;
243+
244+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION codec_conf;
245+
246+ D3D12_VIDEO_ENCODER_RATE_CONTROL rc;
247+
248+ D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE gop;
249+
250+ D3D12_VIDEO_ENCODER_LEVEL_SETTING level;
251+} D3D12VAEncodeContext;
252+
253+typedef struct D3D12VAEncodeType {
254+ /**
255+ * List of supported profiles.
256+ */
257+ const D3D12VAEncodeProfile *profiles;
258+
259+ /**
260+ * D3D12 codec name.
261+ */
262+ D3D12_VIDEO_ENCODER_CODEC d3d12_codec;
263+
264+ /**
265+ * Codec feature flags.
266+ */
267+ int flags;
268+
269+ /**
270+ * Default quality for this codec - used as quantiser or RC quality
271+ * factor depending on RC mode.
272+ */
273+ int default_quality;
274+
275+ /**
276+ * Query codec configuration and determine encode parameters like
277+ * block sizes for surface alignment and slices. If not set, assume
278+ * that all blocks are 16x16 and that surfaces should be aligned to match
279+ * this.
280+ */
281+ int (*get_encoder_caps)(AVCodecContext *avctx);
282+
283+ /**
284+ * Perform any extra codec-specific configuration.
285+ */
286+ int (*configure)(AVCodecContext *avctx);
287+
288+ /**
289+ * Set codec-specific level setting.
290+ */
291+ int (*set_level)(AVCodecContext *avctx);
292+
293+ /**
294+ * The size of any private data structure associated with each
295+ * picture (can be zero if not required).
296+ */
297+ size_t picture_priv_data_size;
298+
299+ /**
300+ * Fill the corresponding parameters.
301+ */
302+ int (*init_sequence_params)(AVCodecContext *avctx);
303+
304+ int (*init_picture_params)(AVCodecContext *avctx,
305+ D3D12VAEncodePicture *pic);
306+
307+ void (*free_picture_params)(D3D12VAEncodePicture *pic);
308+
309+ /**
310+ * Write the packed header data to the provided buffer.
311+ */
312+ int (*write_sequence_header)(AVCodecContext *avctx,
313+ char *data, size_t *data_len);
314+} D3D12VAEncodeType;
315+
316+int ff_d3d12va_encode_init(AVCodecContext *avctx);
317+int ff_d3d12va_encode_close(AVCodecContext *avctx);
318+
319+#define D3D12VA_ENCODE_RC_MODE(name, desc) \
320+ { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \
321+ 0, 0, FLAGS, .unit = "rc_mode" }
322+#define D3D12VA_ENCODE_RC_OPTIONS \
323+ { "rc_mode",\
324+ "Set rate control mode", \
325+ OFFSET(common.explicit_rc_mode), AV_OPT_TYPE_INT, \
326+ { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, FLAGS, .unit = "rc_mode" }, \
327+ { "auto", "Choose mode automatically based on other parameters", \
328+ 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, .unit = "rc_mode" }, \
329+ D3D12VA_ENCODE_RC_MODE(CQP, "Constant-quality"), \
330+ D3D12VA_ENCODE_RC_MODE(CBR, "Constant-bitrate"), \
331+ D3D12VA_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \
332+ D3D12VA_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate")
333+
334+#endif /* AVCODEC_D3D12VA_ENCODE_H */
libavcodec/d3d12va_encode_hevc.c+1007−0
@@ -0,0 +1,1007 @@
1+/*
2+ * Direct3D 12 HW acceleration video encoder
3+ *
4+ * Copyright (c) 2024 Intel Corporation
5+ *
6+ * This file is part of FFmpeg.
7+ *
8+ * FFmpeg is free software; you can redistribute it and/or
9+ * modify it under the terms of the GNU Lesser General Public
10+ * License as published by the Free Software Foundation; either
11+ * version 2.1 of the License, or (at your option) any later version.
12+ *
13+ * FFmpeg is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+ * Lesser General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU Lesser General Public
19+ * License along with FFmpeg; if not, write to the Free Software
20+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+ */
22+#include "libavutil/opt.h"
23+#include "libavutil/common.h"
24+#include "libavutil/mem.h"
25+#include "libavutil/pixdesc.h"
26+#include "libavutil/hwcontext_d3d12va_internal.h"
27+
28+#include "avcodec.h"
29+#include "cbs.h"
30+#include "cbs_h265.h"
31+#include "h2645data.h"
32+#include "h265_profile_level.h"
33+#include "codec_internal.h"
34+#include "d3d12va_encode.h"
35+
36+typedef struct D3D12VAEncodeHEVCPicture {
37+ int pic_order_cnt;
38+ int64_t last_idr_frame;
39+} D3D12VAEncodeHEVCPicture;
40+
41+typedef struct D3D12VAEncodeHEVCContext {
42+ D3D12VAEncodeContext common;
43+
44+ // User options.
45+ int qp;
46+ int profile;
47+ int tier;
48+ int level;
49+
50+ // Writer structures.
51+ H265RawVPS raw_vps;
52+ H265RawSPS raw_sps;
53+ H265RawPPS raw_pps;
54+
55+ CodedBitstreamContext *cbc;
56+ CodedBitstreamFragment current_access_unit;
57+} D3D12VAEncodeHEVCContext;
58+
59+typedef struct D3D12VAEncodeHEVCLevel {
60+ int level;
61+ D3D12_VIDEO_ENCODER_LEVELS_HEVC d3d12_level;
62+} D3D12VAEncodeHEVCLevel;
63+
64+static const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC hevc_config_support_sets[] =
65+{
66+ {
67+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE,
68+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
69+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32,
70+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
71+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
72+ 3,
73+ 3,
74+ },
75+ {
76+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE,
77+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
78+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32,
79+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
80+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
81+ 0,
82+ 0,
83+ },
84+ {
85+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE,
86+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
87+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32,
88+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
89+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
90+ 2,
91+ 2,
92+ },
93+ {
94+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE,
95+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
96+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64,
97+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
98+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
99+ 2,
100+ 2,
101+ },
102+ {
103+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE,
104+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
105+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64,
106+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
107+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
108+ 4,
109+ 4,
110+ },
111+};
112+
113+static const D3D12VAEncodeHEVCLevel hevc_levels[] = {
114+ { 30, D3D12_VIDEO_ENCODER_LEVELS_HEVC_1 },
115+ { 60, D3D12_VIDEO_ENCODER_LEVELS_HEVC_2 },
116+ { 63, D3D12_VIDEO_ENCODER_LEVELS_HEVC_21 },
117+ { 90, D3D12_VIDEO_ENCODER_LEVELS_HEVC_3 },
118+ { 93, D3D12_VIDEO_ENCODER_LEVELS_HEVC_31 },
119+ { 120, D3D12_VIDEO_ENCODER_LEVELS_HEVC_4 },
120+ { 123, D3D12_VIDEO_ENCODER_LEVELS_HEVC_41 },
121+ { 150, D3D12_VIDEO_ENCODER_LEVELS_HEVC_5 },
122+ { 153, D3D12_VIDEO_ENCODER_LEVELS_HEVC_51 },
123+ { 156, D3D12_VIDEO_ENCODER_LEVELS_HEVC_52 },
124+ { 180, D3D12_VIDEO_ENCODER_LEVELS_HEVC_6 },
125+ { 183, D3D12_VIDEO_ENCODER_LEVELS_HEVC_61 },
126+ { 186, D3D12_VIDEO_ENCODER_LEVELS_HEVC_62 },
127+};
128+
129+static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
130+static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main10 = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN10;
131+
132+#define D3D_PROFILE_DESC(name) \
133+ { sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC), { .pHEVCProfile = (D3D12_VIDEO_ENCODER_PROFILE_HEVC *)&profile_ ## name } }
134+static const D3D12VAEncodeProfile d3d12va_encode_hevc_profiles[] = {
135+ { AV_PROFILE_HEVC_MAIN, 8, 3, 1, 1, D3D_PROFILE_DESC(main) },
136+ { AV_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, D3D_PROFILE_DESC(main10) },
137+ { AV_PROFILE_UNKNOWN },
138+};
139+
140+static uint8_t d3d12va_encode_hevc_map_cusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE cusize)
141+{
142+ switch (cusize) {
143+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8: return 8;
144+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_16x16: return 16;
145+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32: return 32;
146+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64: return 64;
147+ default: av_assert0(0);
148+ }
149+ return 0;
150+}
151+
152+static uint8_t d3d12va_encode_hevc_map_tusize(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE tusize)
153+{
154+ switch (tusize) {
155+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4: return 4;
156+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_8x8: return 8;
157+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_16x16: return 16;
158+ case D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32: return 32;
159+ default: av_assert0(0);
160+ }
161+ return 0;
162+}
163+
164+static int d3d12va_encode_hevc_write_access_unit(AVCodecContext *avctx,
165+ char *data, size_t *data_len,
166+ CodedBitstreamFragment *au)
167+{
168+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
169+ int err;
170+
171+ err = ff_cbs_write_fragment_data(priv->cbc, au);
172+ if (err < 0) {
173+ av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n");
174+ return err;
175+ }
176+
177+ if (*data_len < 8 * au->data_size - au->data_bit_padding) {
178+ av_log(avctx, AV_LOG_ERROR, "Access unit too large: "
179+ "%zu < %zu.\n", *data_len,
180+ 8 * au->data_size - au->data_bit_padding);
181+ return AVERROR(ENOSPC);
182+ }
183+
184+ memcpy(data, au->data, au->data_size);
185+ *data_len = 8 * au->data_size - au->data_bit_padding;
186+
187+ return 0;
188+}
189+
190+static int d3d12va_encode_hevc_add_nal(AVCodecContext *avctx,
191+ CodedBitstreamFragment *au,
192+ void *nal_unit)
193+{
194+ H265RawNALUnitHeader *header = nal_unit;
195+ int err;
196+
197+ err = ff_cbs_insert_unit_content(au, -1,
198+ header->nal_unit_type, nal_unit, NULL);
199+ if (err < 0) {
200+ av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: "
201+ "type = %d.\n", header->nal_unit_type);
202+ return err;
203+ }
204+
205+ return 0;
206+}
207+
208+static int d3d12va_encode_hevc_write_sequence_header(AVCodecContext *avctx,
209+ char *data, size_t *data_len)
210+{
211+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
212+ CodedBitstreamFragment *au = &priv->current_access_unit;
213+ int err;
214+
215+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_vps);
216+ if (err < 0)
217+ goto fail;
218+
219+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_sps);
220+ if (err < 0)
221+ goto fail;
222+
223+ err = d3d12va_encode_hevc_add_nal(avctx, au, &priv->raw_pps);
224+ if (err < 0)
225+ goto fail;
226+
227+ err = d3d12va_encode_hevc_write_access_unit(avctx, data, data_len, au);
228+fail:
229+ ff_cbs_fragment_reset(au);
230+ return err;
231+
232+}
233+
234+static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext *avctx)
235+{
236+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
237+ D3D12VAEncodeContext *ctx = avctx->priv_data;
238+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
239+ AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx;
240+ H265RawVPS *vps = &priv->raw_vps;
241+ H265RawSPS *sps = &priv->raw_sps;
242+ H265RawPPS *pps = &priv->raw_pps;
243+ H265RawProfileTierLevel *ptl = &vps->profile_tier_level;
244+ H265RawVUI *vui = &sps->vui;
245+ D3D12_VIDEO_ENCODER_PROFILE_HEVC profile = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
246+ D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC level = { 0 };
247+ const AVPixFmtDescriptor *desc;
248+ uint8_t min_cu_size, max_cu_size, min_tu_size, max_tu_size;
249+ int chroma_format, bit_depth;
250+ HRESULT hr;
251+ int i;
252+
253+ D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = {
254+ .NodeIndex = 0,
255+ .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC,
256+ .InputFormat = hwctx->format,
257+ .RateControl = ctx->rc,
258+ .IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
259+ .SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
260+ .ResolutionsListCount = 1,
261+ .pResolutionList = &ctx->resolution,
262+ .CodecGopSequence = ctx->gop,
263+ .MaxReferenceFramesInDPB = MAX_DPB_SIZE - 1,
264+ .CodecConfiguration = ctx->codec_conf,
265+ .SuggestedProfile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC),
266+ .SuggestedProfile.pHEVCProfile = &profile,
267+ .SuggestedLevel.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC),
268+ .SuggestedLevel.pHEVCLevelSetting = &level,
269+ .pResolutionDependentSupport = &ctx->res_limits,
270+ };
271+
272+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
273+ &support, sizeof(support));
274+
275+ if (FAILED(hr)) {
276+ av_log(avctx, AV_LOG_ERROR, "Failed to check encoder support(%lx).\n", (long)hr);
277+ return AVERROR(EINVAL);
278+ }
279+
280+ if (!(support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK)) {
281+ av_log(avctx, AV_LOG_ERROR, "Driver does not support some request features. %#x\n",
282+ support.ValidationFlags);
283+ return AVERROR(EINVAL);
284+ }
285+
286+ if (support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS) {
287+ av_log(avctx, AV_LOG_ERROR, "D3D12 video encode on this device requires texture array support, "
288+ "but it's not implemented.\n");
289+ return AVERROR_PATCHWELCOME;
290+ }
291+
292+ memset(vps, 0, sizeof(*vps));
293+ memset(sps, 0, sizeof(*sps));
294+ memset(pps, 0, sizeof(*pps));
295+
296+ desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format);
297+ av_assert0(desc);
298+ if (desc->nb_components == 1) {
299+ chroma_format = 0;
300+ } else {
301+ if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) {
302+ chroma_format = 1;
303+ } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) {
304+ chroma_format = 2;
305+ } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) {
306+ chroma_format = 3;
307+ } else {
308+ av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format "
309+ "%s is not supported.\n", desc->name);
310+ return AVERROR(EINVAL);
311+ }
312+ }
313+ bit_depth = desc->comp[0].depth;
314+
315+ min_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig->MinLumaCodingUnitSize);
316+ max_cu_size = d3d12va_encode_hevc_map_cusize(ctx->codec_conf.pHEVCConfig->MaxLumaCodingUnitSize);
317+ min_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig->MinLumaTransformUnitSize);
318+ max_tu_size = d3d12va_encode_hevc_map_tusize(ctx->codec_conf.pHEVCConfig->MaxLumaTransformUnitSize);
319+
320+ // VPS
321+
322+ vps->nal_unit_header = (H265RawNALUnitHeader) {
323+ .nal_unit_type = HEVC_NAL_VPS,
324+ .nuh_layer_id = 0,
325+ .nuh_temporal_id_plus1 = 1,
326+ };
327+
328+ vps->vps_video_parameter_set_id = 0;
329+
330+ vps->vps_base_layer_internal_flag = 1;
331+ vps->vps_base_layer_available_flag = 1;
332+ vps->vps_max_layers_minus1 = 0;
333+ vps->vps_max_sub_layers_minus1 = 0;
334+ vps->vps_temporal_id_nesting_flag = 1;
335+
336+ ptl->general_profile_space = 0;
337+ ptl->general_profile_idc = avctx->profile;
338+ ptl->general_tier_flag = priv->tier;
339+
340+ ptl->general_profile_compatibility_flag[ptl->general_profile_idc] = 1;
341+
342+ ptl->general_progressive_source_flag = 1;
343+ ptl->general_interlaced_source_flag = 0;
344+ ptl->general_non_packed_constraint_flag = 1;
345+ ptl->general_frame_only_constraint_flag = 1;
346+
347+ ptl->general_max_14bit_constraint_flag = bit_depth <= 14;
348+ ptl->general_max_12bit_constraint_flag = bit_depth <= 12;
349+ ptl->general_max_10bit_constraint_flag = bit_depth <= 10;
350+ ptl->general_max_8bit_constraint_flag = bit_depth == 8;
351+
352+ ptl->general_max_422chroma_constraint_flag = chroma_format <= 2;
353+ ptl->general_max_420chroma_constraint_flag = chroma_format <= 1;
354+ ptl->general_max_monochrome_constraint_flag = chroma_format == 0;
355+
356+ ptl->general_intra_constraint_flag = base_ctx->gop_size == 1;
357+ ptl->general_one_picture_only_constraint_flag = 0;
358+
359+ ptl->general_lower_bit_rate_constraint_flag = 1;
360+
361+ if (avctx->level != FF_LEVEL_UNKNOWN) {
362+ ptl->general_level_idc = avctx->level;
363+ } else {
364+ const H265LevelDescriptor *level;
365+
366+ level = ff_h265_guess_level(ptl, avctx->bit_rate,
367+ base_ctx->surface_width, base_ctx->surface_height,
368+ 1, 1, 1, (base_ctx->b_per_p > 0) + 1);
369+ if (level) {
370+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
371+ ptl->general_level_idc = level->level_idc;
372+ } else {
373+ av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to "
374+ "any normal level; using level 8.5.\n");
375+ ptl->general_level_idc = 255;
376+ // The tier flag must be set in level 8.5.
377+ ptl->general_tier_flag = 1;
378+ }
379+ avctx->level = ptl->general_level_idc;
380+ }
381+
382+ vps->vps_sub_layer_ordering_info_present_flag = 0;
383+ vps->vps_max_dec_pic_buffering_minus1[0] = base_ctx->max_b_depth + 1;
384+ vps->vps_max_num_reorder_pics[0] = base_ctx->max_b_depth;
385+ vps->vps_max_latency_increase_plus1[0] = 0;
386+
387+ vps->vps_max_layer_id = 0;
388+ vps->vps_num_layer_sets_minus1 = 0;
389+ vps->layer_id_included_flag[0][0] = 1;
390+
391+ vps->vps_timing_info_present_flag = 1;
392+ if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
393+ vps->vps_num_units_in_tick = avctx->framerate.den;
394+ vps->vps_time_scale = avctx->framerate.num;
395+ vps->vps_poc_proportional_to_timing_flag = 1;
396+ vps->vps_num_ticks_poc_diff_one_minus1 = 0;
397+ } else {
398+ vps->vps_num_units_in_tick = avctx->time_base.num;
399+ vps->vps_time_scale = avctx->time_base.den;
400+ vps->vps_poc_proportional_to_timing_flag = 0;
401+ }
402+ vps->vps_num_hrd_parameters = 0;
403+
404+ // SPS
405+
406+ sps->nal_unit_header = (H265RawNALUnitHeader) {
407+ .nal_unit_type = HEVC_NAL_SPS,
408+ .nuh_layer_id = 0,
409+ .nuh_temporal_id_plus1 = 1,
410+ };
411+
412+ sps->sps_video_parameter_set_id = vps->vps_video_parameter_set_id;
413+
414+ sps->sps_max_sub_layers_minus1 = vps->vps_max_sub_layers_minus1;
415+ sps->sps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag;
416+
417+ sps->profile_tier_level = vps->profile_tier_level;
418+
419+ sps->sps_seq_parameter_set_id = 0;
420+
421+ sps->chroma_format_idc = chroma_format;
422+ sps->separate_colour_plane_flag = 0;
423+
424+ av_assert0(ctx->res_limits.SubregionBlockPixelsSize % min_cu_size == 0);
425+
426+ sps->pic_width_in_luma_samples = FFALIGN(base_ctx->surface_width,
427+ ctx->res_limits.SubregionBlockPixelsSize);
428+ sps->pic_height_in_luma_samples = FFALIGN(base_ctx->surface_height,
429+ ctx->res_limits.SubregionBlockPixelsSize);
430+
431+ if (avctx->width != sps->pic_width_in_luma_samples ||
432+ avctx->height != sps->pic_height_in_luma_samples) {
433+ sps->conformance_window_flag = 1;
434+ sps->conf_win_left_offset = 0;
435+ sps->conf_win_right_offset =
436+ (sps->pic_width_in_luma_samples - avctx->width) >> desc->log2_chroma_w;
437+ sps->conf_win_top_offset = 0;
438+ sps->conf_win_bottom_offset =
439+ (sps->pic_height_in_luma_samples - avctx->height) >> desc->log2_chroma_h;
440+ } else {
441+ sps->conformance_window_flag = 0;
442+ }
443+
444+ sps->bit_depth_luma_minus8 = bit_depth - 8;
445+ sps->bit_depth_chroma_minus8 = bit_depth - 8;
446+
447+ sps->log2_max_pic_order_cnt_lsb_minus4 = ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4;
448+
449+ sps->sps_sub_layer_ordering_info_present_flag =
450+ vps->vps_sub_layer_ordering_info_present_flag;
451+ for (i = 0; i <= sps->sps_max_sub_layers_minus1; i++) {
452+ sps->sps_max_dec_pic_buffering_minus1[i] =
453+ vps->vps_max_dec_pic_buffering_minus1[i];
454+ sps->sps_max_num_reorder_pics[i] =
455+ vps->vps_max_num_reorder_pics[i];
456+ sps->sps_max_latency_increase_plus1[i] =
457+ vps->vps_max_latency_increase_plus1[i];
458+ }
459+
460+ sps->log2_min_luma_coding_block_size_minus3 = (uint8_t)(av_log2(min_cu_size) - 3);
461+ sps->log2_diff_max_min_luma_coding_block_size = (uint8_t)(av_log2(max_cu_size) - av_log2(min_cu_size));
462+ sps->log2_min_luma_transform_block_size_minus2 = (uint8_t)(av_log2(min_tu_size) - 2);
463+ sps->log2_diff_max_min_luma_transform_block_size = (uint8_t)(av_log2(max_tu_size) - av_log2(min_tu_size));
464+
465+ sps->max_transform_hierarchy_depth_inter = ctx->codec_conf.pHEVCConfig->max_transform_hierarchy_depth_inter;
466+ sps->max_transform_hierarchy_depth_intra = ctx->codec_conf.pHEVCConfig->max_transform_hierarchy_depth_intra;
467+
468+ sps->amp_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags &
469+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION);
470+ sps->sample_adaptive_offset_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags &
471+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER);
472+ sps->sps_temporal_mvp_enabled_flag = 0;
473+ sps->pcm_enabled_flag = 0;
474+
475+ sps->vui_parameters_present_flag = 1;
476+
477+ if (avctx->sample_aspect_ratio.num != 0 &&
478+ avctx->sample_aspect_ratio.den != 0) {
479+ int num, den, i;
480+ av_reduce(&num, &den, avctx->sample_aspect_ratio.num,
481+ avctx->sample_aspect_ratio.den, 65535);
482+ for (i = 0; i < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect); i++) {
483+ if (num == ff_h2645_pixel_aspect[i].num &&
484+ den == ff_h2645_pixel_aspect[i].den) {
485+ vui->aspect_ratio_idc = i;
486+ break;
487+ }
488+ }
489+ if (i >= FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) {
490+ vui->aspect_ratio_idc = 255;
491+ vui->sar_width = num;
492+ vui->sar_height = den;
493+ }
494+ vui->aspect_ratio_info_present_flag = 1;
495+ }
496+
497+ // Unspecified video format, from table E-2.
498+ vui->video_format = 5;
499+ vui->video_full_range_flag =
500+ avctx->color_range == AVCOL_RANGE_JPEG;
501+ vui->colour_primaries = avctx->color_primaries;
502+ vui->transfer_characteristics = avctx->color_trc;
503+ vui->matrix_coefficients = avctx->colorspace;
504+ if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||
505+ avctx->color_trc != AVCOL_TRC_UNSPECIFIED ||
506+ avctx->colorspace != AVCOL_SPC_UNSPECIFIED)
507+ vui->colour_description_present_flag = 1;
508+ if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED ||
509+ vui->colour_description_present_flag)
510+ vui->video_signal_type_present_flag = 1;
511+
512+ if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) {
513+ vui->chroma_loc_info_present_flag = 1;
514+ vui->chroma_sample_loc_type_top_field =
515+ vui->chroma_sample_loc_type_bottom_field =
516+ avctx->chroma_sample_location - 1;
517+ }
518+
519+ vui->vui_timing_info_present_flag = 1;
520+ vui->vui_num_units_in_tick = vps->vps_num_units_in_tick;
521+ vui->vui_time_scale = vps->vps_time_scale;
522+ vui->vui_poc_proportional_to_timing_flag = vps->vps_poc_proportional_to_timing_flag;
523+ vui->vui_num_ticks_poc_diff_one_minus1 = vps->vps_num_ticks_poc_diff_one_minus1;
524+ vui->vui_hrd_parameters_present_flag = 0;
525+
526+ vui->bitstream_restriction_flag = 1;
527+ vui->motion_vectors_over_pic_boundaries_flag = 1;
528+ vui->restricted_ref_pic_lists_flag = 1;
529+ vui->max_bytes_per_pic_denom = 0;
530+ vui->max_bits_per_min_cu_denom = 0;
531+ vui->log2_max_mv_length_horizontal = 15;
532+ vui->log2_max_mv_length_vertical = 15;
533+
534+ // PPS
535+
536+ pps->nal_unit_header = (H265RawNALUnitHeader) {
537+ .nal_unit_type = HEVC_NAL_PPS,
538+ .nuh_layer_id = 0,
539+ .nuh_temporal_id_plus1 = 1,
540+ };
541+
542+ pps->pps_pic_parameter_set_id = 0;
543+ pps->pps_seq_parameter_set_id = sps->sps_seq_parameter_set_id;
544+
545+ pps->cabac_init_present_flag = 1;
546+
547+ pps->num_ref_idx_l0_default_active_minus1 = 0;
548+ pps->num_ref_idx_l1_default_active_minus1 = 0;
549+
550+ pps->init_qp_minus26 = 0;
551+
552+ pps->transform_skip_enabled_flag = !!(ctx->codec_conf.pHEVCConfig->ConfigurationFlags &
553+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING);
554+
555+ // cu_qp_delta always required to be 1 in https://github.com/microsoft/DirectX-Specs/blob/master/d3d/D3D12VideoEncoding.md
556+ pps->cu_qp_delta_enabled_flag = 1;
557+
558+ pps->diff_cu_qp_delta_depth = 0;
559+
560+ pps->pps_slice_chroma_qp_offsets_present_flag = 1;
561+
562+ pps->tiles_enabled_flag = 0; // no tiling in D3D12
563+
564+ pps->pps_loop_filter_across_slices_enabled_flag = !(ctx->codec_conf.pHEVCConfig->ConfigurationFlags &
565+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_SLICES);
566+ pps->deblocking_filter_control_present_flag = 1;
567+
568+ return 0;
569+}
570+
571+static int d3d12va_encode_hevc_get_encoder_caps(AVCodecContext *avctx)
572+{
573+ int i;
574+ HRESULT hr;
575+ uint8_t min_cu_size, max_cu_size;
576+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
577+ D3D12VAEncodeContext *ctx = avctx->priv_data;
578+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC *config;
579+ D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC hevc_caps;
580+
581+ D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT codec_caps = {
582+ .NodeIndex = 0,
583+ .Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC,
584+ .Profile = ctx->profile->d3d12_profile,
585+ .CodecSupportLimits.DataSize = sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC),
586+ };
587+
588+ for (i = 0; i < FF_ARRAY_ELEMS(hevc_config_support_sets); i++) {
589+ hevc_caps = hevc_config_support_sets[i];
590+ codec_caps.CodecSupportLimits.pHEVCSupport = &hevc_caps;
591+ hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT,
592+ &codec_caps, sizeof(codec_caps));
593+ if (SUCCEEDED(hr) && codec_caps.IsSupported)
594+ break;
595+ }
596+
597+ if (i == FF_ARRAY_ELEMS(hevc_config_support_sets)) {
598+ av_log(avctx, AV_LOG_ERROR, "Unsupported codec configuration\n");
599+ return AVERROR(EINVAL);
600+ }
601+
602+ ctx->codec_conf.DataSize = sizeof(D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC);
603+ ctx->codec_conf.pHEVCConfig = av_mallocz(ctx->codec_conf.DataSize);
604+ if (!ctx->codec_conf.pHEVCConfig)
605+ return AVERROR(ENOMEM);
606+
607+ config = ctx->codec_conf.pHEVCConfig;
608+
609+ config->ConfigurationFlags = D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_NONE;
610+ config->MinLumaCodingUnitSize = hevc_caps.MinLumaCodingUnitSize;
611+ config->MaxLumaCodingUnitSize = hevc_caps.MaxLumaCodingUnitSize;
612+ config->MinLumaTransformUnitSize = hevc_caps.MinLumaTransformUnitSize;
613+ config->MaxLumaTransformUnitSize = hevc_caps.MaxLumaTransformUnitSize;
614+ config->max_transform_hierarchy_depth_inter = hevc_caps.max_transform_hierarchy_depth_inter;
615+ config->max_transform_hierarchy_depth_intra = hevc_caps.max_transform_hierarchy_depth_intra;
616+
617+ if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_SUPPORT ||
618+ hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_REQUIRED)
619+ config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION;
620+
621+ if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_SAO_FILTER_SUPPORT)
622+ config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_SAO_FILTER;
623+
624+ if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_DISABLING_LOOP_FILTER_ACROSS_SLICES_SUPPORT)
625+ config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_DISABLE_LOOP_FILTER_ACROSS_SLICES;
626+
627+ if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_SUPPORT)
628+ config->ConfigurationFlags |= D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_ENABLE_TRANSFORM_SKIPPING;
629+
630+ if (hevc_caps.SupportFlags & D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_P_FRAMES_IMPLEMENTED_AS_LOW_DELAY_B_FRAMES)
631+ ctx->bi_not_empty = 1;
632+
633+ // block sizes
634+ min_cu_size = d3d12va_encode_hevc_map_cusize(hevc_caps.MinLumaCodingUnitSize);
635+ max_cu_size = d3d12va_encode_hevc_map_cusize(hevc_caps.MaxLumaCodingUnitSize);
636+
637+ av_log(avctx, AV_LOG_VERBOSE, "Using CTU size %dx%d, "
638+ "min CB size %dx%d.\n", max_cu_size, max_cu_size,
639+ min_cu_size, min_cu_size);
640+
641+ base_ctx->surface_width = FFALIGN(avctx->width, min_cu_size);
642+ base_ctx->surface_height = FFALIGN(avctx->height, min_cu_size);
643+
644+ return 0;
645+}
646+
647+static int d3d12va_encode_hevc_configure(AVCodecContext *avctx)
648+{
649+ FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
650+ D3D12VAEncodeContext *ctx = avctx->priv_data;
651+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
652+ int fixed_qp_idr, fixed_qp_p, fixed_qp_b;
653+ int err;
654+
655+ err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx);
656+ if (err < 0)
657+ return err;
658+
659+ // Rate control
660+ if (ctx->rc.Mode == D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP) {
661+ D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP *cqp_ctl;
662+ fixed_qp_p = av_clip(ctx->rc_quality, 1, 51);
663+ if (avctx->i_quant_factor > 0.0)
664+ fixed_qp_idr = av_clip((avctx->i_quant_factor * fixed_qp_p +
665+ avctx->i_quant_offset) + 0.5, 1, 51);
666+ else
667+ fixed_qp_idr = fixed_qp_p;
668+ if (avctx->b_quant_factor > 0.0)
669+ fixed_qp_b = av_clip((avctx->b_quant_factor * fixed_qp_p +
670+ avctx->b_quant_offset) + 0.5, 1, 51);
671+ else
672+ fixed_qp_b = fixed_qp_p;
673+
674+ av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = "
675+ "%d / %d / %d for IDR- / P- / B-frames.\n",
676+ fixed_qp_idr, fixed_qp_p, fixed_qp_b);
677+
678+ ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP);
679+ cqp_ctl = av_mallocz(ctx->rc.ConfigParams.DataSize);
680+ if (!cqp_ctl)
681+ return AVERROR(ENOMEM);
682+
683+ cqp_ctl->ConstantQP_FullIntracodedFrame = fixed_qp_idr;
684+ cqp_ctl->ConstantQP_InterPredictedFrame_PrevRefOnly = fixed_qp_p;
685+ cqp_ctl->ConstantQP_InterPredictedFrame_BiDirectionalRef = fixed_qp_b;
686+
687+ ctx->rc.ConfigParams.pConfiguration_CQP = cqp_ctl;
688+ }
689+
690+ // GOP
691+ ctx->gop.DataSize = sizeof(D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC);
692+ ctx->gop.pHEVCGroupOfPictures = av_mallocz(ctx->gop.DataSize);
693+ if (!ctx->gop.pHEVCGroupOfPictures)
694+ return AVERROR(ENOMEM);
695+
696+ ctx->gop.pHEVCGroupOfPictures->GOPLength = base_ctx->gop_size;
697+ ctx->gop.pHEVCGroupOfPictures->PPicturePeriod = base_ctx->b_per_p + 1;
698+ // Power of 2
699+ if (base_ctx->gop_size & base_ctx->gop_size - 1 == 0)
700+ ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 =
701+ FFMAX(av_log2(base_ctx->gop_size) - 4, 0);
702+ else
703+ ctx->gop.pHEVCGroupOfPictures->log2_max_pic_order_cnt_lsb_minus4 =
704+ FFMAX(av_log2(base_ctx->gop_size) - 3, 0);
705+
706+ return 0;
707+}
708+
709+static int d3d12va_encode_hevc_set_level(AVCodecContext *avctx)
710+{
711+ D3D12VAEncodeContext *ctx = avctx->priv_data;
712+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
713+ int i;
714+
715+ ctx->level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC);
716+ ctx->level.pHEVCLevelSetting = av_mallocz(ctx->level.DataSize);
717+ if (!ctx->level.pHEVCLevelSetting)
718+ return AVERROR(ENOMEM);
719+
720+ for (i = 0; i < FF_ARRAY_ELEMS(hevc_levels); i++) {
721+ if (avctx->level == hevc_levels[i].level) {
722+ ctx->level.pHEVCLevelSetting->Level = hevc_levels[i].d3d12_level;
723+ break;
724+ }
725+ }
726+
727+ if (i == FF_ARRAY_ELEMS(hevc_levels)) {
728+ av_log(avctx, AV_LOG_ERROR, "Invalid level %d.\n", avctx->level);
729+ return AVERROR(EINVAL);
730+ }
731+
732+ ctx->level.pHEVCLevelSetting->Tier = priv->raw_vps.profile_tier_level.general_tier_flag == 0 ?
733+ D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN :
734+ D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH;
735+
736+ return 0;
737+}
738+
739+static void d3d12va_encode_hevc_free_picture_params(D3D12VAEncodePicture *pic)
740+{
741+ if (!pic->pic_ctl.pHEVCPicData)
742+ return;
743+
744+ av_freep(&pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames);
745+ av_freep(&pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames);
746+ av_freep(&pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors);
747+ av_freep(&pic->pic_ctl.pHEVCPicData);
748+}
749+
750+static int d3d12va_encode_hevc_init_picture_params(AVCodecContext *avctx,
751+ D3D12VAEncodePicture *pic)
752+{
753+ FFHWBaseEncodePicture *base_pic = &pic->base;
754+ D3D12VAEncodeHEVCPicture *hpic = base_pic->priv_data;
755+ FFHWBaseEncodePicture *prev = base_pic->prev;
756+ D3D12VAEncodeHEVCPicture *hprev = prev ? prev->priv_data : NULL;
757+ D3D12_VIDEO_ENCODER_REFERENCE_PICTURE_DESCRIPTOR_HEVC *pd = NULL;
758+ UINT *ref_list0 = NULL, *ref_list1 = NULL;
759+ int i, idx = 0;
760+
761+ pic->pic_ctl.DataSize = sizeof(D3D12_VIDEO_ENCODER_PICTURE_CONTROL_CODEC_DATA_HEVC);
762+ pic->pic_ctl.pHEVCPicData = av_mallocz(pic->pic_ctl.DataSize);
763+ if (!pic->pic_ctl.pHEVCPicData)
764+ return AVERROR(ENOMEM);
765+
766+ if (base_pic->type == FF_HW_PICTURE_TYPE_IDR) {
767+ av_assert0(base_pic->display_order == base_pic->encode_order);
768+ hpic->last_idr_frame = base_pic->display_order;
769+ } else {
770+ av_assert0(prev);
771+ hpic->last_idr_frame = hprev->last_idr_frame;
772+ }
773+ hpic->pic_order_cnt = base_pic->display_order - hpic->last_idr_frame;
774+
775+ switch(base_pic->type) {
776+ case FF_HW_PICTURE_TYPE_IDR:
777+ pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_IDR_FRAME;
778+ break;
779+ case FF_HW_PICTURE_TYPE_I:
780+ pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_I_FRAME;
781+ break;
782+ case FF_HW_PICTURE_TYPE_P:
783+ pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_P_FRAME;
784+ break;
785+ case FF_HW_PICTURE_TYPE_B:
786+ pic->pic_ctl.pHEVCPicData->FrameType = D3D12_VIDEO_ENCODER_FRAME_TYPE_HEVC_B_FRAME;
787+ break;
788+ default:
789+ av_assert0(0 && "invalid picture type");
790+ }
791+
792+ pic->pic_ctl.pHEVCPicData->slice_pic_parameter_set_id = 0;
793+ pic->pic_ctl.pHEVCPicData->PictureOrderCountNumber = hpic->pic_order_cnt;
794+
795+ if (base_pic->type == FF_HW_PICTURE_TYPE_P || base_pic->type == FF_HW_PICTURE_TYPE_B) {
796+ pd = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*pd));
797+ if (!pd)
798+ return AVERROR(ENOMEM);
799+
800+ ref_list0 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list0));
801+ if (!ref_list0)
802+ return AVERROR(ENOMEM);
803+
804+ pic->pic_ctl.pHEVCPicData->List0ReferenceFramesCount = base_pic->nb_refs[0];
805+ for (i = 0; i < base_pic->nb_refs[0]; i++) {
806+ FFHWBaseEncodePicture *ref = base_pic->refs[0][i];
807+ D3D12VAEncodeHEVCPicture *href;
808+
809+ av_assert0(ref && ref->encode_order < base_pic->encode_order);
810+ href = ref->priv_data;
811+
812+ ref_list0[i] = idx;
813+ pd[idx].ReconstructedPictureResourceIndex = idx;
814+ pd[idx].IsRefUsedByCurrentPic = TRUE;
815+ pd[idx].PictureOrderCountNumber = href->pic_order_cnt;
816+ idx++;
817+ }
818+ }
819+
820+ if (base_pic->type == FF_HW_PICTURE_TYPE_B) {
821+ ref_list1 = av_calloc(MAX_PICTURE_REFERENCES, sizeof(*ref_list1));
822+ if (!ref_list1)
823+ return AVERROR(ENOMEM);
824+
825+ pic->pic_ctl.pHEVCPicData->List1ReferenceFramesCount = base_pic->nb_refs[1];
826+ for (i = 0; i < base_pic->nb_refs[1]; i++) {
827+ FFHWBaseEncodePicture *ref = base_pic->refs[1][i];
828+ D3D12VAEncodeHEVCPicture *href;
829+
830+ av_assert0(ref && ref->encode_order < base_pic->encode_order);
831+ href = ref->priv_data;
832+
833+ ref_list1[i] = idx;
834+ pd[idx].ReconstructedPictureResourceIndex = idx;
835+ pd[idx].IsRefUsedByCurrentPic = TRUE;
836+ pd[idx].PictureOrderCountNumber = href->pic_order_cnt;
837+ idx++;
838+ }
839+ }
840+
841+ pic->pic_ctl.pHEVCPicData->pList0ReferenceFrames = ref_list0;
842+ pic->pic_ctl.pHEVCPicData->pList1ReferenceFrames = ref_list1;
843+ pic->pic_ctl.pHEVCPicData->ReferenceFramesReconPictureDescriptorsCount = idx;
844+ pic->pic_ctl.pHEVCPicData->pReferenceFramesReconPictureDescriptors = pd;
845+
846+ return 0;
847+}
848+
849+static const D3D12VAEncodeType d3d12va_encode_type_hevc = {
850+ .profiles = d3d12va_encode_hevc_profiles,
851+
852+ .d3d12_codec = D3D12_VIDEO_ENCODER_CODEC_HEVC,
853+
854+ .flags = FF_HW_FLAG_B_PICTURES |
855+ FF_HW_FLAG_B_PICTURE_REFERENCES |
856+ FF_HW_FLAG_NON_IDR_KEY_PICTURES,
857+
858+ .default_quality = 25,
859+
860+ .get_encoder_caps = &d3d12va_encode_hevc_get_encoder_caps,
861+
862+ .configure = &d3d12va_encode_hevc_configure,
863+
864+ .set_level = &d3d12va_encode_hevc_set_level,
865+
866+ .picture_priv_data_size = sizeof(D3D12VAEncodeHEVCPicture),
867+
868+ .init_sequence_params = &d3d12va_encode_hevc_init_sequence_params,
869+
870+ .init_picture_params = &d3d12va_encode_hevc_init_picture_params,
871+
872+ .free_picture_params = &d3d12va_encode_hevc_free_picture_params,
873+
874+ .write_sequence_header = &d3d12va_encode_hevc_write_sequence_header,
875+};
876+
877+static int d3d12va_encode_hevc_init(AVCodecContext *avctx)
878+{
879+ D3D12VAEncodeContext *ctx = avctx->priv_data;
880+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
881+
882+ ctx->codec = &d3d12va_encode_type_hevc;
883+
884+ if (avctx->profile == AV_PROFILE_UNKNOWN)
885+ avctx->profile = priv->profile;
886+ if (avctx->level == FF_LEVEL_UNKNOWN)
887+ avctx->level = priv->level;
888+
889+ if (avctx->level != FF_LEVEL_UNKNOWN && avctx->level & ~0xff) {
890+ av_log(avctx, AV_LOG_ERROR, "Invalid level %d: must fit "
891+ "in 8-bit unsigned integer.\n", avctx->level);
892+ return AVERROR(EINVAL);
893+ }
894+
895+ if (priv->qp > 0)
896+ ctx->explicit_qp = priv->qp;
897+
898+ return ff_d3d12va_encode_init(avctx);
899+}
900+
901+static int d3d12va_encode_hevc_close(AVCodecContext *avctx)
902+{
903+ D3D12VAEncodeHEVCContext *priv = avctx->priv_data;
904+
905+ ff_cbs_fragment_free(&priv->current_access_unit);
906+ ff_cbs_close(&priv->cbc);
907+
908+ av_freep(&priv->common.codec_conf.pHEVCConfig);
909+ av_freep(&priv->common.gop.pHEVCGroupOfPictures);
910+ av_freep(&priv->common.level.pHEVCLevelSetting);
911+
912+ return ff_d3d12va_encode_close(avctx);
913+}
914+
915+#define OFFSET(x) offsetof(D3D12VAEncodeHEVCContext, x)
916+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
917+static const AVOption d3d12va_encode_hevc_options[] = {
918+ HW_BASE_ENCODE_COMMON_OPTIONS,
919+ D3D12VA_ENCODE_RC_OPTIONS,
920+
921+ { "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
922+ OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, FLAGS },
923+
924+ { "profile", "Set profile (general_profile_idc)",
925+ OFFSET(profile), AV_OPT_TYPE_INT,
926+ { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" },
927+
928+#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
929+ { .i64 = value }, 0, 0, FLAGS, "profile"
930+ { PROFILE("main", AV_PROFILE_HEVC_MAIN) },
931+ { PROFILE("main10", AV_PROFILE_HEVC_MAIN_10) },
932+#undef PROFILE
933+
934+ { "tier", "Set tier (general_tier_flag)",
935+ OFFSET(tier), AV_OPT_TYPE_INT,
936+ { .i64 = 0 }, 0, 1, FLAGS, "tier" },
937+ { "main", NULL, 0, AV_OPT_TYPE_CONST,
938+ { .i64 = 0 }, 0, 0, FLAGS, "tier" },
939+ { "high", NULL, 0, AV_OPT_TYPE_CONST,
940+ { .i64 = 1 }, 0, 0, FLAGS, "tier" },
941+
942+ { "level", "Set level (general_level_idc)",
943+ OFFSET(level), AV_OPT_TYPE_INT,
944+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },
945+
946+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
947+ { .i64 = value }, 0, 0, FLAGS, "level"
948+ { LEVEL("1", 30) },
949+ { LEVEL("2", 60) },
950+ { LEVEL("2.1", 63) },
951+ { LEVEL("3", 90) },
952+ { LEVEL("3.1", 93) },
953+ { LEVEL("4", 120) },
954+ { LEVEL("4.1", 123) },
955+ { LEVEL("5", 150) },
956+ { LEVEL("5.1", 153) },
957+ { LEVEL("5.2", 156) },
958+ { LEVEL("6", 180) },
959+ { LEVEL("6.1", 183) },
960+ { LEVEL("6.2", 186) },
961+#undef LEVEL
962+
963+ { NULL },
964+};
965+
966+static const FFCodecDefault d3d12va_encode_hevc_defaults[] = {
967+ { "b", "0" },
968+ { "bf", "2" },
969+ { "g", "120" },
970+ { "i_qfactor", "1" },
971+ { "i_qoffset", "0" },
972+ { "b_qfactor", "1" },
973+ { "b_qoffset", "0" },
974+ { "qmin", "-1" },
975+ { "qmax", "-1" },
976+ { NULL },
977+};
978+
979+static const AVClass d3d12va_encode_hevc_class = {
980+ .class_name = "hevc_d3d12va",
981+ .item_name = av_default_item_name,
982+ .option = d3d12va_encode_hevc_options,
983+ .version = LIBAVUTIL_VERSION_INT,
984+};
985+
986+const FFCodec ff_hevc_d3d12va_encoder = {
987+ .p.name = "hevc_d3d12va",
988+ CODEC_LONG_NAME("D3D12VA hevc encoder"),
989+ .p.type = AVMEDIA_TYPE_VIDEO,
990+ .p.id = AV_CODEC_ID_HEVC,
991+ .priv_data_size = sizeof(D3D12VAEncodeHEVCContext),
992+ .init = &d3d12va_encode_hevc_init,
993+ FF_CODEC_RECEIVE_PACKET_CB(&ff_hw_base_encode_receive_packet),
994+ .close = &d3d12va_encode_hevc_close,
995+ .p.priv_class = &d3d12va_encode_hevc_class,
996+ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE |
997+ AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
998+ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE |
999+ FF_CODEC_CAP_INIT_CLEANUP,
1000+ .defaults = d3d12va_encode_hevc_defaults,
1001+ .p.pix_fmts = (const enum AVPixelFormat[]) {
1002+ AV_PIX_FMT_D3D12,
1003+ AV_PIX_FMT_NONE,
1004+ },
1005+ .hw_configs = ff_d3d12va_encode_hw_configs,
1006+ .p.wrapper_name = "d3d12va",
1007+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t