avcodec/d3d12va_encode: support motion estimation precision mode

By default, the D3D12 video encoder uses MAXIMUM, which means no restriction—it uses the highest precision supported by the driver.

Applications may want to reduce precision to improve speed or reduce power consumption. This requires the encoder to support user-defined motion estimation precision modes.

D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE defines several precision modes:

maximum: No restriction, uses the maximum precision supported by the driver.
full_pixel: Allows only full-pixel precision.
half_pixel: Allows half-pixel precision.
quarter-pixel: Allows quarter-pixel precision.
eighth-pixel: Allows eighth-pixel precision (introduced in Windows 11).

Sample Command Line:

ffmpeg -hwaccel d3d12va -hwaccel_output_format d3d12 -extra_hw_frames 20 -i input.mp4 -an -c:v h264_d3d12va -me_precision half_pixel out.mp4
This commit is contained in:
stevxiao
2025-11-19 13:14:17 -05:00
committed by Tong Wu
parent 1fcb201ab1
commit 64b9be2dc5
3 changed files with 129 additions and 2 deletions

2
configure vendored
View File

@@ -2624,6 +2624,7 @@ CONFIG_EXTRA="
celp_math
d3d12_intra_refresh
d3d12va_encode
d3d12va_me_precision_eighth_pixel
deflate_wrapper
dirac_parse
dnn
@@ -6979,6 +6980,7 @@ test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATU
test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req" && enable d3d12_encoder_feature
test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_CODEC c = D3D12_VIDEO_ENCODER_CODEC_AV1; (void)c;" && enable d3d12va_av1_headers
test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_INTRA_REFRESH_MODE check = { 0 };" && enable d3d12_intra_refresh
test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_EIGHTH_PIXEL; (void)m;" && enable d3d12va_me_precision_eighth_pixel
check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00
check_type "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo -DSECURITY_WIN32
check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602

View File

@@ -1143,6 +1143,91 @@ rc_mode_found:
return 0;
}
static int d3d12va_encode_init_motion_estimation_precision(AVCodecContext *avctx)
{
FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
D3D12VAEncodeContext *ctx = avctx->priv_data;
AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx;
HRESULT hr;
if (ctx->me_precision == D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM)
return 0;
D3D12_VIDEO_ENCODER_PROFILE_DESC profile = { 0 };
D3D12_VIDEO_ENCODER_PROFILE_H264 h264_profile = D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN;
D3D12_VIDEO_ENCODER_PROFILE_HEVC hevc_profile = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
#if CONFIG_AV1_D3D12VA_ENCODER
D3D12_VIDEO_ENCODER_AV1_PROFILE av1_profile = D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN;
#endif
D3D12_VIDEO_ENCODER_LEVEL_SETTING level = { 0 };
D3D12_VIDEO_ENCODER_LEVELS_H264 h264_level = { 0 };
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC hevc_level = { 0 };
#if CONFIG_AV1_D3D12VA_ENCODER
D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS av1_level = { 0 };
#endif
switch (ctx->codec->d3d12_codec) {
case D3D12_VIDEO_ENCODER_CODEC_H264:
profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_H264);
profile.pH264Profile = &h264_profile;
level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVELS_H264);
level.pH264LevelSetting = &h264_level;
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC);
profile.pHEVCProfile = &hevc_profile;
level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC);
level.pHEVCLevelSetting = &hevc_level;
break;
#if CONFIG_AV1_D3D12VA_ENCODER
case D3D12_VIDEO_ENCODER_CODEC_AV1:
profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PROFILE);
profile.pAV1Profile = &av1_profile;
level.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS);
level.pAV1LevelSetting = &av1_level;
break;
#endif
default:
av_assert0(0);
}
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = {
.NodeIndex = 0,
.Codec = ctx->codec->d3d12_codec,
.InputFormat = hwctx->format,
.RateControl = ctx->rc,
.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
.ResolutionsListCount = 1,
.pResolutionList = &ctx->resolution,
.CodecGopSequence = ctx->gop,
.MaxReferenceFramesInDPB = MAX_DPB_SIZE - 1,
.CodecConfiguration = ctx->codec_conf,
.SuggestedProfile = profile,
.SuggestedLevel = level,
.pResolutionDependentSupport = &ctx->res_limits,
};
hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
&support, sizeof(support));
if (FAILED(hr)) {
av_log(avctx, AV_LOG_ERROR, "Failed to check encoder support for motion estimation.\n");
return AVERROR(EINVAL);
}
if (!(support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_MOTION_ESTIMATION_PRECISION_MODE_LIMIT_AVAILABLE)) {
av_log(avctx, AV_LOG_ERROR, "Hardware does not support motion estimation "
"precision mode limits.\n");
return AVERROR(ENOTSUP);
}
av_log(avctx, AV_LOG_VERBOSE, "Hardware supports motion estimation "
"precision mode limits.\n");
return 0;
}
static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx)
{
FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
@@ -1322,7 +1407,7 @@ static int d3d12va_create_encoder(AVCodecContext *avctx)
.EncodeProfile = ctx->profile->d3d12_profile,
.InputFormat = frames_hwctx->format,
.CodecConfiguration = ctx->codec_conf,
.MaxMotionEstimationPrecision = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM,
.MaxMotionEstimationPrecision = ctx->me_precision,
};
hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, &IID_ID3D12VideoEncoder,
@@ -1681,6 +1766,10 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx)
goto fail;
}
err = d3d12va_encode_init_motion_estimation_precision(avctx);
if (err < 0)
goto fail;
if (ctx->codec->init_sequence_params) {
err = ctx->codec->init_sequence_params(avctx);
if (err < 0) {

View File

@@ -277,6 +277,11 @@ typedef struct D3D12VAEncodeContext {
*/
UINT intra_refresh_frame_index;
/**
* Motion estimation precision mode
*/
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE me_precision;
} D3D12VAEncodeContext;
typedef struct D3D12VAEncodeType {
@@ -362,6 +367,24 @@ int ff_d3d12va_encode_close(AVCodecContext *avctx);
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ ## mode }, \
0, 0, FLAGS, .unit = "intra_refresh_mode" }
#if CONFIG_D3D12VA_ME_PRECISION_EIGHTH_PIXEL
#define D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_EIGHTH_PIXEL
#else
#define D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_QUARTER_PIXEL
#endif
#define D3D12VA_ENCODE_ME_PRECISION_MODE(name, mode, desc) \
{ #name, #desc " pixel precision", 0, AV_OPT_TYPE_CONST, \
{ .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_ ## mode }, \
0, 0, FLAGS, .unit = "me_precision" }
#if CONFIG_D3D12VA_ME_PRECISION_EIGHTH_PIXEL
#define FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL \
, D3D12VA_ENCODE_ME_PRECISION_MODE(eighth_pixel, EIGHTH_PIXEL, Eighth)
#else
#define FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL
#endif
#define D3D12VA_ENCODE_COMMON_OPTIONS \
{ "max_frame_size", \
"Maximum frame size (in bytes)",\
@@ -378,7 +401,20 @@ int ff_d3d12va_encode_close(AVCodecContext *avctx);
{ "intra_refresh_duration", \
"Number of frames over which to spread intra refresh (0 = GOP size)", \
OFFSET(common.intra_refresh.IntraRefreshDuration), AV_OPT_TYPE_INT, \
{ .i64 = 0 }, 0, INT_MAX, FLAGS }
{ .i64 = 0 }, 0, INT_MAX, FLAGS }, \
{ "me_precision", "Motion estimation precision mode", \
OFFSET(common.me_precision), AV_OPT_TYPE_INT, \
{ .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM }, \
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM, \
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE, \
FLAGS, .unit = "me_precision" }, \
{ "maximum", "Maximum (best quality, slowest)", 0, AV_OPT_TYPE_CONST, \
{ .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM }, \
0, 0, FLAGS, .unit = "me_precision" }, \
D3D12VA_ENCODE_ME_PRECISION_MODE(full_pixel, FULL_PIXEL, Full), \
D3D12VA_ENCODE_ME_PRECISION_MODE(half_pixel, HALF_PIXEL, Half), \
D3D12VA_ENCODE_ME_PRECISION_MODE(quarter_pixel, QUARTER_PIXEL, Quarter) \
FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL
#define D3D12VA_ENCODE_RC_MODE(name, desc) \
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \