lavc/vaapi_encode: add support for maxframesize

Add support for max frame size:
    - max_frame_size (bytes) to indicate the max allowed size for frame.

Control each encoded frame size into target limitation size by adjusting
whole frame's average QP value. The driver will use multi passes to
adjust average QP setp by step to achieve the target, and the result
may not strictly guaranteed. Frame size may exceed target alone with
using the maximum average QP value. The failure always happens on the
intra(especially the first intra frame of a new GOP) frames or set
max_frame_size with a very small number.

example cmdline:
    ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -f rawvideo \
        -v verbose -s:v 352x288 -i ./input.yuv -vf format=nv12,hwupload \
        -c:v h264_vaapi -profile:v main -g 30 -rc_mode VBR -b:v 500k   \
        -bf 3 -max_frame_size 40000 -vframes 100 -y ./max_frame_size.h264

Max frame size was enabled since VA-API version (0, 33, 0), but query
is available since (1, 5, 0). It will be passed as a parameter in picParam
and should be set for each frame.

Signed-off-by: Linjie Fu <linjie.fu@intel.com>
Signed-off-by: Fei Wang <fei.w.wang@intel.com>
This commit is contained in:
Linjie Fu 2022-05-05 17:07:14 +08:00 committed by Haihao Xiang
parent 5929ea6d4b
commit 99446c74cf
2 changed files with 83 additions and 1 deletions

View File

@ -365,6 +365,17 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
goto fail;
}
#if VA_CHECK_VERSION(1, 5, 0)
if (ctx->max_frame_size) {
err = vaapi_encode_make_misc_param_buffer(avctx, pic,
VAEncMiscParameterTypeMaxFrameSize,
&ctx->mfs_params,
sizeof(ctx->mfs_params));
if (err < 0)
goto fail;
}
#endif
if (pic->type == PICTURE_TYPE_IDR) {
if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
ctx->codec->write_sequence_header) {
@ -1869,6 +1880,63 @@ rc_mode_found:
return 0;
}
static av_cold int vaapi_encode_init_max_frame_size(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(1, 5, 0)
VAAPIEncodeContext *ctx = avctx->priv_data;
VAConfigAttrib attr = { VAConfigAttribMaxFrameSize };
VAStatus vas;
if (ctx->va_rc_mode == VA_RC_CQP) {
ctx->max_frame_size = 0;
av_log(avctx, AV_LOG_ERROR, "Max frame size is invalid in CQP rate "
"control mode.\n");
return AVERROR(EINVAL);
}
vas = vaGetConfigAttributes(ctx->hwctx->display,
ctx->va_profile,
ctx->va_entrypoint,
&attr, 1);
if (vas != VA_STATUS_SUCCESS) {
ctx->max_frame_size = 0;
av_log(avctx, AV_LOG_ERROR, "Failed to query max frame size "
"config attribute: %d (%s).\n", vas, vaErrorStr(vas));
return AVERROR_EXTERNAL;
}
if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
ctx->max_frame_size = 0;
av_log(avctx, AV_LOG_ERROR, "Max frame size attribute "
"is not supported.\n");
return AVERROR(EINVAL);
} else {
VAConfigAttribValMaxFrameSize attr_mfs;
attr_mfs.value = attr.value;
// Prefer to use VAEncMiscParameterTypeMaxFrameSize for max frame size.
if (!attr_mfs.bits.max_frame_size && attr_mfs.bits.multiple_pass) {
ctx->max_frame_size = 0;
av_log(avctx, AV_LOG_ERROR, "Driver only supports multiple pass "
"max frame size which has not been implemented in FFmpeg.\n");
return AVERROR(EINVAL);
}
ctx->mfs_params = (VAEncMiscParameterBufferMaxFrameSize){
.max_frame_size = ctx->max_frame_size * 8,
};
av_log(avctx, AV_LOG_VERBOSE, "Set max frame size: %d bytes.\n",
ctx->max_frame_size);
}
#else
av_log(avctx, AV_LOG_ERROR, "The max frame size option is not supported with "
"this VAAPI version.\n");
return AVERROR(EINVAL);
#endif
return 0;
}
static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@ -2548,6 +2616,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
goto fail;
}
if (ctx->max_frame_size) {
err = vaapi_encode_init_max_frame_size(avctx);
if (err < 0)
goto fail;
}
vas = vaCreateConfig(ctx->hwctx->display,
ctx->va_profile, ctx->va_entrypoint,
ctx->config_attributes, ctx->nb_config_attributes,

View File

@ -191,6 +191,9 @@ typedef struct VAAPIEncodeContext {
// Desired B frame reference depth.
int desired_b_depth;
// Max Frame Size
int max_frame_size;
// Explicitly set RC mode (otherwise attempt to pick from
// available modes).
int explicit_rc_mode;
@ -268,6 +271,7 @@ typedef struct VAAPIEncodeContext {
VAEncMiscParameterRateControl rc_params;
VAEncMiscParameterHRD hrd_params;
VAEncMiscParameterFrameRate fr_params;
VAEncMiscParameterBufferMaxFrameSize mfs_params;
#if VA_CHECK_VERSION(0, 36, 0)
VAEncMiscParameterBufferQualityLevel quality_params;
#endif
@ -478,7 +482,11 @@ int ff_vaapi_encode_close(AVCodecContext *avctx);
"Increase this to improve single channel performance. This option " \
"doesn't work if driver doesn't implement vaSyncBuffer function.", \
OFFSET(common.async_depth), AV_OPT_TYPE_INT, \
{ .i64 = 2 }, 1, MAX_ASYNC_DEPTH, FLAGS }
{ .i64 = 2 }, 1, MAX_ASYNC_DEPTH, FLAGS }, \
{ "max_frame_size", \
"Maximum frame size (in bytes)",\
OFFSET(common.max_frame_size), AV_OPT_TYPE_INT, \
{ .i64 = 0 }, 0, INT_MAX, FLAGS }
#define VAAPI_ENCODE_RC_MODE(name, desc) \
{ #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \