Use the frame-skip feature to maintain a specified framerate from the point of view of the driver. --- "max_fps" collides with an option used in lavf, so renamed to "vfr_max_fps" here. (That hopefully also makes it clearer what the use of the option is.)
doc/encoders.texi | 7 +++ libavcodec/vaapi_encode.c | 116 +++++++++++++++++++++++++++++++++++--- libavcodec/vaapi_encode.h | 18 +++++- 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index 29625ba07c..39b8fc1d37 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -2848,6 +2848,13 @@ Quality-defined variable-bitrate. Average variable bitrate. @end table +@item vfr_max_fps +Enable VFR encoding with this maximum framerate. This is implemented by +indicating to the driver that frames have been skipped at some locations in +the stream, and requires driver support. The minimum interval between frames +must not be smaller than this, and there may be problems if the maximum +interval is more than a small multiple of it. + @end table Each encoder also has its own specific options: diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 2dda451882..df8f65e431 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -412,6 +412,29 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } +#if VA_CHECK_VERSION(0, 40, 0) + if (ctx->vfr_mode && pic->frame_skips > 0) { + struct { + VAEncMiscParameterBuffer misc; + VAEncMiscParameterSkipFrame skip; + } param = { + .misc = { + .type = VAEncMiscParameterTypeSkipFrame, + }, + .skip = { + .skip_frame_flag = 1, + .num_skip_frames = pic->frame_skips, + }, + }; + + err = vaapi_encode_make_param_buffer(avctx, pic, + VAEncMiscParameterBufferType, + (char*)¶m, sizeof(param)); + if (err < 0) + goto fail; + } +#endif + vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { @@ -942,6 +965,36 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) pic->input_surface = (VASurfaceID)(uintptr_t)frame->data[3]; pic->pts = frame->pts; + if (ctx->vfr_mode && ctx->input_order > 0) { + if (frame->pts < ctx->prev_pts) { + av_log(avctx, AV_LOG_WARNING, "Timestamp discontinuity " + "(backward step: %"PRId64" -> %"PRId64"): " + "VFR mode reset.\n", ctx->prev_pts, frame->pts); + ctx->ticks_outstanding = av_make_q(0, 1); + } else { + AVRational step_ticks, ticks; + int ticks_int; + step_ticks = av_div_q(av_make_q(frame->pts - ctx->prev_pts, 1), + ctx->ticks_per_frame); + ticks = av_add_q(ctx->ticks_outstanding, step_ticks); + ticks_int = ticks.num / ticks.den; + if (ticks_int < 1) { + av_log(avctx, AV_LOG_WARNING, "Max FPS exceeded!\n"); + } else if (ticks_int > 256) { + av_log(avctx, AV_LOG_WARNING, "Timestamp discontinuity " + "(forward step: %"PRId64" -> %"PRId64"): " + "VFR mode reset.\n", ctx->prev_pts, frame->pts); + } else { + av_log(avctx, AV_LOG_DEBUG, "Inserting %d frame skips before " + "frame %"PRId64".\n", ticks_int - 1, frame->pts); + pic->frame_skips = ticks_int - 1; + } + ctx->ticks_outstanding = + av_sub_q(ticks, av_make_q(ticks_int, 1)); + } + } + ctx->prev_pts = frame->pts; + if (ctx->input_order == 0) ctx->first_pts = pic->pts; if (ctx->input_order == ctx->decode_delay) @@ -1315,7 +1368,6 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) int rc_quality; int64_t hrd_buffer_size; int64_t hrd_initial_buffer_fullness; - int fr_num, fr_den; VAConfigAttrib rc_attr = { VAConfigAttribRateControl }; VAStatus vas; char supported_rc_modes_string[64]; @@ -1609,22 +1661,66 @@ rc_mode_found: sizeof(ctx->hrd_params)); } - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) - av_reduce(&fr_num, &fr_den, - avctx->framerate.num, avctx->framerate.den, 65535); - else + return 0; +} + +static av_cold int vaapi_encode_init_framerate(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + +#if VA_CHECK_VERSION(0, 40, 0) + int fr_num, fr_den; + + ctx->vfr_mode = ctx->vfr_max_fps.num > 0 && ctx->vfr_max_fps.den > 0; + if (ctx->vfr_mode) { + VAConfigAttrib attr = { VAConfigAttribEncSkipFrame }; + VAStatus vas; + + vas = vaGetConfigAttributes(ctx->hwctx->display, + ctx->va_profile, ctx->va_entrypoint, + &attr, 1); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query skip-frame " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } + if (attr.value == VA_ATTRIB_NOT_SUPPORTED || + attr.value == 0) { + av_log(avctx, AV_LOG_ERROR, "Skip-frame attribute is not " + "supported by driver: VFR mode cannot be used.\n"); + return AVERROR(EINVAL); + } + av_reduce(&fr_num, &fr_den, - avctx->time_base.den, avctx->time_base.num, 65535); + ctx->vfr_max_fps.num, ctx->vfr_max_fps.den, 65535); + + ctx->ticks_per_frame = av_inv_q(av_mul_q(avctx->time_base, + av_make_q(fr_num, fr_den))); + ctx->ticks_outstanding = av_make_q(0, 1); + } else { + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + av_reduce(&fr_num, &fr_den, + avctx->framerate.num, avctx->framerate.den, 65535); + else + av_reduce(&fr_num, &fr_den, + avctx->time_base.den, avctx->time_base.num, 65535); + } - av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n", + av_log(avctx, AV_LOG_VERBOSE, "RC framerate (%s mode): " + "%d/%d (%.2f fps).\n", ctx->vfr_mode ? "VFR" : "CFR", fr_num, fr_den, (double)fr_num / fr_den); ctx->fr_params.misc.type = VAEncMiscParameterTypeFrameRate; ctx->fr_params.fr.framerate = (unsigned int)fr_den << 16 | fr_num; -#if VA_CHECK_VERSION(0, 40, 0) vaapi_encode_add_global_param(avctx, &ctx->fr_params.misc, sizeof(ctx->fr_params)); + +#else + if (ctx->vfr_max_fps.num > 0 && ctx->vfr_max_fps.den > 0) { + av_log(avctx, AV_LOG_WARNING, "Variable framerate is " + "not supported with this VAAPI version.\n"); + } #endif return 0; @@ -2077,6 +2173,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) if (err < 0) goto fail; + err = vaapi_encode_init_framerate(avctx); + if (err < 0) + goto fail; + err = vaapi_encode_init_gop_structure(avctx); if (err < 0) goto fail; diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index 44a8db566e..860b67dbe2 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -69,6 +69,9 @@ typedef struct VAAPIEncodePicture { int64_t pts; int force_idr; + // Number of frame-skips to insert before this frame in VFR mode. + int frame_skips; + int type; int b_depth; int encode_issued; @@ -184,6 +187,10 @@ typedef struct VAAPIEncodeContext { // (Forces CQP mode when set, overriding everything else.) int explicit_qp; + // When set, enable skip-frame VFR mode with this maximum + // framerate. + AVRational vfr_max_fps; + // Desired packed headers. unsigned int desired_packed_headers; @@ -297,6 +304,12 @@ typedef struct VAAPIEncodeContext { int64_t dts_pts_diff; int64_t ts_ring[MAX_REORDER_DELAY * 3]; + // VFR state. + int vfr_mode; + AVRational ticks_per_frame; + AVRational ticks_outstanding; + int64_t prev_pts; + // Slice structure. int slice_block_rows; int slice_block_cols; @@ -445,7 +458,10 @@ int ff_vaapi_encode_close(AVCodecContext *avctx); VAAPI_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \ VAAPI_ENCODE_RC_MODE(ICQ, "Intelligent constant-quality"), \ VAAPI_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate"), \ - VAAPI_ENCODE_RC_MODE(AVBR, "Average variable-bitrate") + VAAPI_ENCODE_RC_MODE(AVBR, "Average variable-bitrate"), \ + { "vfr_max_fps", "Enable VFR mode with this maximum framerate", \ + OFFSET(common.vfr_max_fps), AV_OPT_TYPE_RATIONAL, \ + { .dbl = 0.0 }, 0, INT_MAX, FLAGS } #endif /* AVCODEC_VAAPI_ENCODE_H */ -- 2.19.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel