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
87OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o87OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
88OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o88OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
89OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o89OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
90OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o
90OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o91OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o
91OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o92OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o
92OBJS-$(CONFIG_DOVI_RPUENC) += dovi_rpu.o dovi_rpuenc.o93OBJS-$(CONFIG_DOVI_RPUENC) += dovi_rpu.o dovi_rpuenc.o
@@ -433,6 +434,8 @@ OBJS-$(CONFIG_HDR_ENCODER) += hdrenc.o · 340 unmodified lines
433OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o434OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o
434OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o435OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o
435OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o436OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o
437OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o h265_profile_level.o \
438 h2645data.o
436OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o439OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o
437OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o440OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o
438OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o441OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o
@@ -1262,7 +1265,7 @@ SKIPHEADERS += %_tablegen.h \ · 823 unmodified lines
1262 1265
1263SKIPHEADERS-$(CONFIG_AMF) += amfenc.h1266SKIPHEADERS-$(CONFIG_AMF) += amfenc.h
1264SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h1267SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h
1265SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h1268SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h d3d12va_encode.h
1266SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h1269SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h
1267SKIPHEADERS-$(CONFIG_JNI) += ffjni.h1270SKIPHEADERS-$(CONFIG_JNI) += ffjni.h
1268SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h1271SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h
libavcodec/allcodecs.c+1−0
@@ -849,6 +849,7 @@ extern const FFCodec ff_h264_vaapi_encoder; · 848 unmodified lines
849extern const FFCodec ff_h264_videotoolbox_encoder;849extern const FFCodec ff_h264_videotoolbox_encoder;
850extern const FFCodec ff_hevc_amf_encoder;850extern const FFCodec ff_hevc_amf_encoder;
851extern const FFCodec ff_hevc_cuvid_decoder;851extern const FFCodec ff_hevc_cuvid_decoder;
852extern const FFCodec ff_hevc_d3d12va_encoder;
852extern const FFCodec ff_hevc_mediacodec_decoder;853extern const FFCodec ff_hevc_mediacodec_decoder;
853extern const FFCodec ff_hevc_mediacodec_encoder;854extern const FFCodec ff_hevc_mediacodec_encoder;
854extern const FFCodec ff_hevc_mf_encoder;855extern 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
36const AVCodecHWConfigInternal *const ff_d3d12va_encode_hw_configs[] = {
37 HW_CONFIG_ENCODER_FRAMES(D3D12, D3D12VA),
38 NULL,
39};
40
41static 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
54static 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
61fail:
62 return AVERROR(EINVAL);
63}
64
65typedef struct CommandAllocator {
66 ID3D12CommandAllocator *command_allocator;
67 uint64_t fence_value;
68} CommandAllocator;
69
70static 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
95static 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
109static 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
142static 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
188static 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
500fail:
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
517static 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
538static 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
563static 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
586static 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
605static 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
640static 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
670end:
671 av_buffer_unref(&pic->output_buffer_ref);
672 pic->output_buffer = NULL;
673 return err;
674}
675
676static 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
699static 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
752static 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
762static 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
787static 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
859rc_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
1058static 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
1129static 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
1156static 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
1181static 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
1189static 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
1237static 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
1271static 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
1342fail:
1343 D3D12_OBJECT_RELEASE(command_allocator);
1344 return err;
1345}
1346
1347static 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
1383static 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
1393int 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
1508fail:
1509 return err;
1510}
1511
1512int 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
35struct D3D12VAEncodeType;
36
37extern 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
42typedef 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
61typedef 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
93enum {
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
103typedef 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
145typedef 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
253typedef 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
316int ff_d3d12va_encode_init(AVCodecContext *avctx);
317int 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
36typedef struct D3D12VAEncodeHEVCPicture {
37 int pic_order_cnt;
38 int64_t last_idr_frame;
39} D3D12VAEncodeHEVCPicture;
40
41typedef 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
59typedef struct D3D12VAEncodeHEVCLevel {
60 int level;
61 D3D12_VIDEO_ENCODER_LEVELS_HEVC d3d12_level;
62} D3D12VAEncodeHEVCLevel;
63
64static 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
113static 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
129static const D3D12_VIDEO_ENCODER_PROFILE_HEVC profile_main = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
130static 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 } }
134static 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
140static 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
152static 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
164static 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
190static 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
208static 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);
228fail:
229 ff_cbs_fragment_reset(au);
230 return err;
231
232}
233
234static 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
571static 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
647static 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
709static 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
739static 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
750static 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
849static 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
877static 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
901static 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)
917static 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
966static 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
979static 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
986const 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