[FFmpeg-devel] [PATCH] libavfilter/f_select: switch to activate and properly handle EOF pts
Li-Heng Chen
lihengc at netflix.com
Tue Sep 13 23:50:46 EEST 2022
This patch solves a potential EOF pts bug that can be triggered with other filters: when placing select filter before fps filter, the EOF pts in `f_select` always indicate the last input frame regardless of the frame selected. This may cause unwanted duplication of the last-selected-frame in `vf_fps`. Switching the filtering process from `filter_frame` to `activate` allows to properly set EOF pts by ff_outlink_set_status.
This bug can be reproduced by the ffmpeg cmd below (bitstreams in
fate-suite can reproduce this issue):
ffmpeg -y -i /path/ffmpeg/fate-suite/h264/bbc2.sample.h264 -vf select='gte(n\,0)*gte(24\,n)',fps=25/1 out.y4m
Signed-off-by: Li-Heng Chen <lihengc at netflix.com>
---
libavfilter/f_select.c | 49 +++++++++++++++++++++++++++++++++---------
1 file changed, 39 insertions(+), 10 deletions(-)
diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c
index 1cfe2d59e5..e01f476b5b 100644
--- a/libavfilter/f_select.c
+++ b/libavfilter/f_select.c
@@ -33,6 +33,7 @@
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
+#include "filters.h"
#include "audio.h"
#include "formats.h"
#include "internal.h"
@@ -159,6 +160,7 @@ typedef struct SelectContext {
double select;
int select_out; ///< mark the selected output pad index
int nb_outputs;
+ int64_t eof_pts;
} SelectContext;
#define OFFSET(x) offsetof(SelectContext, x)
@@ -215,6 +217,7 @@ static int config_input(AVFilterLink *inlink)
select->bitdepth = desc->comp[0].depth;
select->nb_planes = is_yuv ? 1 : av_pix_fmt_count_planes(inlink->format);
+ select->eof_pts = AV_NOPTS_VALUE;
for (int plane = 0; plane < select->nb_planes; plane++) {
ptrdiff_t line_size = av_image_get_linesize(inlink->format, inlink->w, plane);
@@ -336,7 +339,7 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame)
if (isnan(select->var_values[VAR_START_T]))
select->var_values[VAR_START_T] = TS2D(frame->pts) * av_q2d(inlink->time_base);
- select->var_values[VAR_N ] = inlink->frame_count_out;
+ select->var_values[VAR_N ] = inlink->frame_count_out - 1;
select->var_values[VAR_PTS] = TS2D(frame->pts);
select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base);
select->var_values[VAR_POS] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos;
@@ -409,17 +412,43 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame)
select->var_values[VAR_PREV_T] = select->var_values[VAR_T];
}
-static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+static int activate(AVFilterContext *ctx)
{
- AVFilterContext *ctx = inlink->dst;
+ int ret, status;
SelectContext *select = ctx->priv;
+ AVFilterLink *inlink = ctx->inputs[0];
+ AVFilterLink *outlink = ctx->outputs[0];
+ AVFrame *in;
+ int64_t pts;
- select_frame(ctx, frame);
- if (select->select)
- return ff_filter_frame(ctx->outputs[select->select_out], frame);
+ FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
- av_frame_free(&frame);
- return 0;
+ ret = ff_inlink_consume_frame(inlink, &in);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ select_frame(ctx, in);
+ if (select->select)
+ return ff_filter_frame(ctx->outputs[select->select_out], in);
+ }
+ av_frame_free(&in);
+
+ ret = ff_inlink_acknowledge_status(inlink, &status, &pts);
+
+ if (((int64_t)select->var_values[VAR_N] - (int64_t)select->var_values[VAR_PREV_SELECTED_N] == 1) ||
+ (status == AVERROR_EOF && select->select))
+ select->eof_pts = pts;
+
+ if (ret && status == AVERROR_EOF) {
+ av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%"PRId64"\n",
+ select->eof_pts);
+ ff_outlink_set_status(outlink, status, select->eof_pts);
+ return 0;
+ }
+
+ FF_FILTER_FORWARD_WANTED(outlink, inlink);
+
+ return FFERROR_NOT_READY;
}
static int request_frame(AVFilterLink *outlink)
@@ -467,7 +496,6 @@ static const AVFilterPad avfilter_af_aselect_inputs[] = {
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
- .filter_frame = filter_frame,
},
};
@@ -475,6 +503,7 @@ const AVFilter ff_af_aselect = {
.name = "aselect",
.description = NULL_IF_CONFIG_SMALL("Select audio frames to pass in output."),
.init = aselect_init,
+ .activate = activate,
.uninit = uninit,
.priv_size = sizeof(SelectContext),
FILTER_INPUTS(avfilter_af_aselect_inputs),
@@ -522,7 +551,6 @@ static const AVFilterPad avfilter_vf_select_inputs[] = {
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_input,
- .filter_frame = filter_frame,
},
};
@@ -530,6 +558,7 @@ const AVFilter ff_vf_select = {
.name = "select",
.description = NULL_IF_CONFIG_SMALL("Select video frames to pass in output."),
.init = select_init,
+ .activate = activate,
.uninit = uninit,
.priv_size = sizeof(SelectContext),
.priv_class = &select_class,
--
2.32.1 (Apple Git-133)
More information about the ffmpeg-devel
mailing list