[FFmpeg-devel] [PATCH v3 10/12] vaapi_encode: Add support for VFR mode
Mark Thompson
sw at jkqxz.net
Sun Feb 10 21:47:03 EET 2019
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
+ at 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
More information about the ffmpeg-devel
mailing list