mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-05 14:30:00 +01:00
avcodec/d3d12va_encode: add intra refresh support for d3d12va encode
Intra refresh is a technique that gradually refreshes the video by encoding rows or regions as intra macroblocks/CTUs spread over multiple frames, rather than using periodic I-frames. This provides better error resilience for video streaming while maintaining more consistent bitrate. Disable Intra Refresh (This is the default) ffmpeg -init_hw_device d3d12va -hwaccel d3d12va -hwaccel_output_format d3d12 \ -i input.mp4 \ -c:v h264_d3d12va \ -intra_refresh_mode none \ -intra_refresh_duration 30 \ -g 60 \ output.h264 Enable Intra Refresh ffmpeg -init_hw_device d3d12va -hwaccel d3d12va -hwaccel_output_format d3d12 \ -i input.mp4 \ -c:v h264_d3d12va \ -intra_refresh_mode row_based \ -intra_refresh_duration 30 \ -g 60 \ output.h264 Parameters - `-intra_refresh_mode`: Set to `row_based` to enable row-based intra refresh, or `NONE` to disable - `-intra_refresh_duration`: Number of frames over which to spread the intra refresh (default: 0 = use GOP size) - `-g`: GOP size (should typically be larger than intra refresh duration)
This commit is contained in:
2
configure
vendored
2
configure
vendored
@@ -2615,6 +2615,7 @@ CONFIG_EXTRA="
|
|||||||
cbs_vp8
|
cbs_vp8
|
||||||
cbs_vp9
|
cbs_vp9
|
||||||
celp_math
|
celp_math
|
||||||
|
d3d12_intra_refresh
|
||||||
d3d12va_encode
|
d3d12va_encode
|
||||||
deflate_wrapper
|
deflate_wrapper
|
||||||
dirac_parse
|
dirac_parse
|
||||||
@@ -6964,6 +6965,7 @@ check_type "windows.h d3d12video.h" "ID3D12VideoEncoder"
|
|||||||
test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATURE_VIDEO_ENCODER_CODEC" && \
|
test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATURE_VIDEO_ENCODER_CODEC" && \
|
||||||
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_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_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
|
||||||
check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00
|
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 "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo -DSECURITY_WIN32
|
||||||
check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602
|
check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602
|
||||||
|
|||||||
@@ -211,10 +211,17 @@ static int d3d12va_encode_issue(AVCodecContext *avctx,
|
|||||||
int barriers_ref_index = 0;
|
int barriers_ref_index = 0;
|
||||||
D3D12_RESOURCE_BARRIER *barriers_ref = NULL;
|
D3D12_RESOURCE_BARRIER *barriers_ref = NULL;
|
||||||
|
|
||||||
|
D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAGS seq_flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE;
|
||||||
|
|
||||||
|
// Request intra refresh if enabled
|
||||||
|
if (ctx->intra_refresh.Mode != D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE) {
|
||||||
|
seq_flags |= D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_REQUEST_INTRA_REFRESH;
|
||||||
|
}
|
||||||
|
|
||||||
D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = {
|
D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS input_args = {
|
||||||
.SequenceControlDesc = {
|
.SequenceControlDesc = {
|
||||||
.Flags = D3D12_VIDEO_ENCODER_SEQUENCE_CONTROL_FLAG_NONE,
|
.Flags = seq_flags,
|
||||||
.IntraRefreshConfig = { 0 },
|
.IntraRefreshConfig = ctx->intra_refresh,
|
||||||
.RateControl = ctx->rc,
|
.RateControl = ctx->rc,
|
||||||
.PictureTargetResolution = ctx->resolution,
|
.PictureTargetResolution = ctx->resolution,
|
||||||
.SelectedLayoutMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
.SelectedLayoutMode = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
||||||
@@ -359,7 +366,7 @@ static int d3d12va_encode_issue(AVCodecContext *avctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input_args.PictureControlDesc.IntraRefreshFrameIndex = 0;
|
input_args.PictureControlDesc.IntraRefreshFrameIndex = ctx->intra_refresh_frame_index;
|
||||||
if (base_pic->is_reference)
|
if (base_pic->is_reference)
|
||||||
input_args.PictureControlDesc.Flags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
|
input_args.PictureControlDesc.Flags |= D3D12_VIDEO_ENCODER_PICTURE_CONTROL_FLAG_USED_AS_REFERENCE_PICTURE;
|
||||||
|
|
||||||
@@ -547,6 +554,12 @@ static int d3d12va_encode_issue(AVCodecContext *avctx,
|
|||||||
|
|
||||||
pic->fence_value = ctx->sync_ctx.fence_value;
|
pic->fence_value = ctx->sync_ctx.fence_value;
|
||||||
|
|
||||||
|
// Update intra refresh frame index for next frame
|
||||||
|
if (ctx->intra_refresh.Mode != D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE) {
|
||||||
|
ctx->intra_refresh_frame_index =
|
||||||
|
(ctx->intra_refresh_frame_index + 1) % ctx->intra_refresh.IntraRefreshDuration;
|
||||||
|
}
|
||||||
|
|
||||||
if (d3d12_refs.ppTexture2Ds)
|
if (d3d12_refs.ppTexture2Ds)
|
||||||
av_freep(&d3d12_refs.ppTexture2Ds);
|
av_freep(&d3d12_refs.ppTexture2Ds);
|
||||||
|
|
||||||
@@ -1220,6 +1233,81 @@ static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int d3d12va_encode_init_intra_refresh(AVCodecContext *avctx)
|
||||||
|
{
|
||||||
|
FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
|
||||||
|
D3D12VAEncodeContext *ctx = avctx->priv_data;
|
||||||
|
|
||||||
|
if (ctx->intra_refresh.Mode == D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Check for SDK API availability
|
||||||
|
#if CONFIG_D3D12_INTRA_REFRESH
|
||||||
|
HRESULT hr;
|
||||||
|
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:
|
||||||
|
level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVELS_H264);
|
||||||
|
level.pH264LevelSetting = &h264_level;
|
||||||
|
break;
|
||||||
|
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
|
||||||
|
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:
|
||||||
|
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_INTRA_REFRESH_MODE intra_refresh_support = {
|
||||||
|
.NodeIndex = 0,
|
||||||
|
.Codec = ctx->codec->d3d12_codec,
|
||||||
|
.Profile = ctx->profile->d3d12_profile,
|
||||||
|
.Level = level,
|
||||||
|
.IntraRefreshMode = ctx->intra_refresh.Mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3,
|
||||||
|
D3D12_FEATURE_VIDEO_ENCODER_INTRA_REFRESH_MODE,
|
||||||
|
&intra_refresh_support, sizeof(intra_refresh_support));
|
||||||
|
|
||||||
|
if (FAILED(hr) || !intra_refresh_support.IsSupported) {
|
||||||
|
av_log(avctx, AV_LOG_ERROR, "Requested intra refresh mode not supported by driver.\n");
|
||||||
|
return AVERROR(ENOTSUP);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Older SDK - validation will occur in init_sequence_params via D3D12_FEATURE_VIDEO_ENCODER_SUPPORT
|
||||||
|
av_log(avctx, AV_LOG_VERBOSE, "Intra refresh explicit check not available in this SDK.\n"
|
||||||
|
"Support will be validated during encoder initialization.\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set duration: use GOP size if not specified
|
||||||
|
if (ctx->intra_refresh.IntraRefreshDuration == 0) {
|
||||||
|
ctx->intra_refresh.IntraRefreshDuration = base_ctx->gop_size;
|
||||||
|
av_log(avctx, AV_LOG_VERBOSE, "Intra refresh duration set to GOP size: %d\n",
|
||||||
|
ctx->intra_refresh.IntraRefreshDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize frame index
|
||||||
|
ctx->intra_refresh_frame_index = 0;
|
||||||
|
|
||||||
|
av_log(avctx, AV_LOG_VERBOSE, "Intra refresh: mode=%d, duration=%d frames\n",
|
||||||
|
ctx->intra_refresh.Mode, ctx->intra_refresh.IntraRefreshDuration);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int d3d12va_create_encoder(AVCodecContext *avctx)
|
static int d3d12va_create_encoder(AVCodecContext *avctx)
|
||||||
{
|
{
|
||||||
FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
|
FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
|
||||||
@@ -1570,6 +1658,10 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx)
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
err = d3d12va_encode_init_intra_refresh(avctx);
|
||||||
|
if (err < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
if (!(ctx->codec->flags & FF_HW_FLAG_SLICE_CONTROL) && avctx->slices > 0) {
|
if (!(ctx->codec->flags & FF_HW_FLAG_SLICE_CONTROL) && avctx->slices > 0) {
|
||||||
av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested "
|
av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested "
|
||||||
"but this codec does not support controlling slices.\n");
|
"but this codec does not support controlling slices.\n");
|
||||||
|
|||||||
@@ -266,6 +266,17 @@ typedef struct D3D12VAEncodeContext {
|
|||||||
D3D12_VIDEO_ENCODER_LEVEL_SETTING level;
|
D3D12_VIDEO_ENCODER_LEVEL_SETTING level;
|
||||||
|
|
||||||
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregions_layout;
|
D3D12_VIDEO_ENCODER_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA subregions_layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intra refresh configuration
|
||||||
|
*/
|
||||||
|
D3D12_VIDEO_ENCODER_INTRA_REFRESH intra_refresh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current frame index within intra refresh cycle
|
||||||
|
*/
|
||||||
|
UINT intra_refresh_frame_index;
|
||||||
|
|
||||||
} D3D12VAEncodeContext;
|
} D3D12VAEncodeContext;
|
||||||
|
|
||||||
typedef struct D3D12VAEncodeType {
|
typedef struct D3D12VAEncodeType {
|
||||||
@@ -347,11 +358,27 @@ int ff_d3d12va_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt);
|
|||||||
int ff_d3d12va_encode_init(AVCodecContext *avctx);
|
int ff_d3d12va_encode_init(AVCodecContext *avctx);
|
||||||
int ff_d3d12va_encode_close(AVCodecContext *avctx);
|
int ff_d3d12va_encode_close(AVCodecContext *avctx);
|
||||||
|
|
||||||
|
#define D3D12VA_ENCODE_INTRA_REFRESH_MODE(name, mode, desc) \
|
||||||
|
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ ## mode }, \
|
||||||
|
0, 0, FLAGS, .unit = "intra_refresh_mode" }
|
||||||
|
|
||||||
#define D3D12VA_ENCODE_COMMON_OPTIONS \
|
#define D3D12VA_ENCODE_COMMON_OPTIONS \
|
||||||
{ "max_frame_size", \
|
{ "max_frame_size", \
|
||||||
"Maximum frame size (in bytes)",\
|
"Maximum frame size (in bytes)",\
|
||||||
OFFSET(common.max_frame_size), AV_OPT_TYPE_INT, \
|
OFFSET(common.max_frame_size), AV_OPT_TYPE_INT, \
|
||||||
{ .i64 = 0 }, 0, INT_MAX / 8, FLAGS }
|
{ .i64 = 0 }, 0, INT_MAX / 8, FLAGS }, \
|
||||||
|
{ "intra_refresh_mode", \
|
||||||
|
"Set intra refresh mode", \
|
||||||
|
OFFSET(common.intra_refresh.Mode), AV_OPT_TYPE_INT, \
|
||||||
|
{ .i64 = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE }, \
|
||||||
|
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, \
|
||||||
|
D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ROW_BASED, FLAGS, .unit = "intra_refresh_mode" }, \
|
||||||
|
D3D12VA_ENCODE_INTRA_REFRESH_MODE(none, NONE, "Disable intra refresh"), \
|
||||||
|
D3D12VA_ENCODE_INTRA_REFRESH_MODE(row_based, ROW_BASED, "Row-based intra refresh"), \
|
||||||
|
{ "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 }
|
||||||
|
|
||||||
#define D3D12VA_ENCODE_RC_MODE(name, desc) \
|
#define D3D12VA_ENCODE_RC_MODE(name, desc) \
|
||||||
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \
|
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \
|
||||||
|
|||||||
@@ -559,7 +559,7 @@ static int d3d12va_encode_av1_init_sequence_params(AVCodecContext *avctx)
|
|||||||
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
|
.Codec = D3D12_VIDEO_ENCODER_CODEC_AV1,
|
||||||
.InputFormat = hwctx->format,
|
.InputFormat = hwctx->format,
|
||||||
.RateControl = ctx->rc,
|
.RateControl = ctx->rc,
|
||||||
.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
|
.IntraRefresh = ctx->intra_refresh.Mode,
|
||||||
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
||||||
.ResolutionsListCount = 1,
|
.ResolutionsListCount = 1,
|
||||||
.pResolutionList = &ctx->resolution,
|
.pResolutionList = &ctx->resolution,
|
||||||
@@ -1082,6 +1082,7 @@ static int d3d12va_encode_av1_close(AVCodecContext *avctx)
|
|||||||
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
|
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
|
||||||
static const AVOption d3d12va_encode_av1_options[] = {
|
static const AVOption d3d12va_encode_av1_options[] = {
|
||||||
HW_BASE_ENCODE_COMMON_OPTIONS,
|
HW_BASE_ENCODE_COMMON_OPTIONS,
|
||||||
|
D3D12VA_ENCODE_COMMON_OPTIONS,
|
||||||
D3D12VA_ENCODE_RC_OPTIONS,
|
D3D12VA_ENCODE_RC_OPTIONS,
|
||||||
|
|
||||||
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
|
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ static int d3d12va_encode_h264_init_sequence_params(AVCodecContext *avctx)
|
|||||||
.Codec = D3D12_VIDEO_ENCODER_CODEC_H264,
|
.Codec = D3D12_VIDEO_ENCODER_CODEC_H264,
|
||||||
.InputFormat = hwctx->format,
|
.InputFormat = hwctx->format,
|
||||||
.RateControl = ctx->rc,
|
.RateControl = ctx->rc,
|
||||||
.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
|
.IntraRefresh = ctx->intra_refresh.Mode,
|
||||||
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
||||||
.ResolutionsListCount = 1,
|
.ResolutionsListCount = 1,
|
||||||
.pResolutionList = &ctx->resolution,
|
.pResolutionList = &ctx->resolution,
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ static int d3d12va_encode_hevc_init_sequence_params(AVCodecContext *avctx)
|
|||||||
.Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC,
|
.Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC,
|
||||||
.InputFormat = hwctx->format,
|
.InputFormat = hwctx->format,
|
||||||
.RateControl = ctx->rc,
|
.RateControl = ctx->rc,
|
||||||
.IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
|
.IntraRefresh = ctx->intra_refresh.Mode,
|
||||||
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
.SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
|
||||||
.ResolutionsListCount = 1,
|
.ResolutionsListCount = 1,
|
||||||
.pResolutionList = &ctx->resolution,
|
.pResolutionList = &ctx->resolution,
|
||||||
|
|||||||
Reference in New Issue
Block a user