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