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
Unified · Split
| @@ -2547,6 +2547,7 @@ CONFIG_EXTRA=" · 2546 unmodified lines | ||
| 2547 | 2547 | cbs_mpeg2 |
| 2548 | 2548 | cbs_vp8 |
| 2549 | 2549 | cbs_vp9 |
| 2550 | + d3d12va_encode | |
| 2550 | 2551 | deflate_wrapper |
| 2551 | 2552 | dirac_parse |
| 2552 | 2553 | dnn |
| @@ -3283,6 +3284,7 @@ wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" · 730 unmodified lines | ||
| 3283 | 3284 | wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" |
| 3284 | 3285 | |
| 3285 | 3286 | # hardware-accelerated codecs |
| 3287 | +d3d12va_encode_deps="d3d12va ID3D12VideoEncoder d3d12_encoder_feature" | |
| 3286 | 3288 | mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" |
| 3287 | 3289 | omx_deps="libdl pthreads" |
| 3288 | 3290 | omx_rpi_select="omx" |
| @@ -3350,6 +3352,7 @@ h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m" · 61 unmodified lines | ||
| 3350 | 3352 | hevc_amf_encoder_deps="amf" |
| 3351 | 3353 | hevc_cuvid_decoder_deps="cuvid" |
| 3352 | 3354 | hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" |
| 3355 | +hevc_d3d12va_encoder_select="cbs_h265 d3d12va_encode" | |
| 3353 | 3356 | hevc_mediacodec_decoder_deps="mediacodec" |
| 3354 | 3357 | hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" |
| 3355 | 3358 | hevc_mediacodec_encoder_deps="mediacodec" |
| @@ -6692,6 +6695,9 @@ check_type "windows.h d3d11.h" "ID3D11VideoDecoder" · 3336 unmodified lines | ||
| 6692 | 6695 | check_type "windows.h d3d11.h" "ID3D11VideoContext" |
| 6693 | 6696 | check_type "windows.h d3d12.h" "ID3D12Device" |
| 6694 | 6697 | check_type "windows.h d3d12video.h" "ID3D12VideoDecoder" |
| 6698 | +check_type "windows.h d3d12video.h" "ID3D12VideoEncoder" | |
| 6699 | +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATURE_VIDEO_ENCODER_CODEC" && \ | |
| 6700 | +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req" && enable d3d12_encoder_feature | |
| 6695 | 6701 | check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 |
| 6696 | 6702 | check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 |
| 6697 | 6703 | check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat |
| @@ -87,6 +87,7 @@ OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o · 86 unmodified lines | ||
| 87 | 87 | OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o |
| 88 | 88 | OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o |
| 89 | 89 | OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o |
| 90 | +OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o | |
| 90 | 91 | OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o |
| 91 | 92 | OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o |
| 92 | 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 | 434 | OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o |
| 434 | 435 | OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o |
| 435 | 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 | 439 | OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o |
| 437 | 440 | OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o |
| 438 | 441 | OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o |
| @@ -1262,7 +1265,7 @@ SKIPHEADERS += %_tablegen.h \ · 823 unmodified lines | ||
| 1262 | 1265 | |
| 1263 | 1266 | SKIPHEADERS-$(CONFIG_AMF) += amfenc.h |
| 1264 | 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 | 1269 | SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h |
| 1267 | 1270 | SKIPHEADERS-$(CONFIG_JNI) += ffjni.h |
| 1268 | 1271 | SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h |
| @@ -849,6 +849,7 @@ extern const FFCodec ff_h264_vaapi_encoder; · 848 unmodified lines | ||
| 849 | 849 | extern const FFCodec ff_h264_videotoolbox_encoder; |
| 850 | 850 | extern const FFCodec ff_hevc_amf_encoder; |
| 851 | 851 | extern const FFCodec ff_hevc_cuvid_decoder; |
| 852 | +extern const FFCodec ff_hevc_d3d12va_encoder; | |
| 852 | 853 | extern const FFCodec ff_hevc_mediacodec_decoder; |
| 853 | 854 | extern const FFCodec ff_hevc_mediacodec_encoder; |
| 854 | 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 | +}; | |