diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index cdb47f7f3e..2f4d59f316 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -186,6 +186,7 @@ void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts) void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts) { link->status = status; + link->frame_wanted_in = link->frame_wanted_out = 0; ff_update_link_current_pts(link, pts); } @@ -354,11 +355,21 @@ void ff_tlog_link(void *ctx, AVFilterLink *link, int end) int ff_request_frame(AVFilterLink *link) { - int ret = -1; FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1); if (link->status) return link->status; + link->frame_wanted_in = 1; + link->frame_wanted_out = 1; + return 0; +} + +int ff_request_frame_to_filter(AVFilterLink *link) +{ + int ret = -1; + + FF_TPRINTF_START(NULL, request_frame_to_filter); ff_tlog_link(NULL, link, 1); + link->frame_wanted_in = 0; if (link->srcpad->request_frame) ret = link->srcpad->request_frame(link); else if (link->src->inputs[0]) @@ -367,6 +378,9 @@ int ff_request_frame(AVFilterLink *link) AVFrame *pbuf = link->partial_buf; link->partial_buf = NULL; ret = ff_filter_frame_framed(link, pbuf); + ff_avfilter_link_set_in_status(link, AVERROR_EOF, AV_NOPTS_VALUE); + link->frame_wanted_out = 0; + return ret; } if (ret < 0) { if (ret != AVERROR(EAGAIN) && ret != link->status) @@ -1136,6 +1150,9 @@ static int ff_filter_frame_needs_framing(AVFilterLink *link, AVFrame *frame) if (pbuf->nb_samples >= link->min_samples) { ret = ff_filter_frame_framed(link, pbuf); pbuf = NULL; + } else { + if (link->frame_wanted_out) + link->frame_wanted_in = 1; } } av_frame_free(&frame); @@ -1177,6 +1194,7 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) } } + link->frame_wanted_out = 0; /* Go directly to actual filtering if possible */ if (link->type == AVMEDIA_TYPE_AUDIO && link->min_samples && diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index c52175b4c9..599369dcb0 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -520,6 +520,20 @@ struct AVFilterLink { * A pointer to a FFVideoFramePool struct. */ void *video_frame_pool; + + /** + * True if a frame is currently wanted on the input of this filter. + * Set when ff_request_frame() is called by the output, + * cleared when the request is handled or forwarded. + */ + int frame_wanted_in; + + /** + * True if a frame is currently wanted on the output of this filter. + * Set when ff_request_frame() is called by the output, + * cleared when a frame is filtered. + */ + int frame_wanted_out; }; /** diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index ec2245f15b..9f50b4120d 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -1367,11 +1367,14 @@ void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link) int avfilter_graph_request_oldest(AVFilterGraph *graph) { + AVFilterLink *oldest = graph->sink_links[0]; + int r; + while (graph->sink_links_count) { - AVFilterLink *oldest = graph->sink_links[0]; - int r = ff_request_frame(oldest); + oldest = graph->sink_links[0]; + r = ff_request_frame(oldest); if (r != AVERROR_EOF) - return r; + break; av_log(oldest->dst, AV_LOG_DEBUG, "EOF on sink link %s:%s.\n", oldest->dst ? oldest->dst->name : "unknown", oldest->dstpad ? oldest->dstpad->name : "unknown"); @@ -1381,5 +1384,52 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) oldest->age_index); oldest->age_index = -1; } - return AVERROR_EOF; + if (!graph->sink_links_count) + return AVERROR_EOF; + av_assert1(oldest->age_index >= 0); + while (oldest->frame_wanted_out) { + r = ff_filter_graph_run_once(graph); + if (r < 0) + return r; + } + return 0; +} + +static AVFilterLink *graph_run_once_find_filter(AVFilterGraph *graph) +{ + unsigned i, j; + AVFilterContext *f; + + /* TODO: replace scanning the graph with a priority list */ + for (i = 0; i < graph->nb_filters; i++) { + f = graph->filters[i]; + for (j = 0; j < f->nb_outputs; j++) + if (f->outputs[j]->frame_wanted_in) + return f->outputs[j]; + } + for (i = 0; i < graph->nb_filters; i++) { + f = graph->filters[i]; + for (j = 0; j < f->nb_outputs; j++) + if (f->outputs[j]->frame_wanted_out) + return f->outputs[j]; + } + return NULL; +} + +int ff_filter_graph_run_once(AVFilterGraph *graph) +{ + AVFilterLink *link; + int ret; + + link = graph_run_once_find_filter(graph); + if (!link) { + av_log(NULL, AV_LOG_WARNING, "Useless run of a filter graph\n"); + return AVERROR(EAGAIN); + } + ret = ff_request_frame_to_filter(link); + if (ret == AVERROR_EOF) + /* local EOF will be forwarded through request_frame() / + set_status() until it reaches the sink */ + ret = 0; + return ret < 0 ? ret : 1; } diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index 7a19df2c3a..2feb56dee9 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -140,6 +140,11 @@ int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFr return AVERROR(EAGAIN); if ((ret = ff_request_frame(inlink)) < 0) return ret; + while (inlink->frame_wanted_out) { + ret = ff_filter_graph_run_once(ctx->graph); + if (ret < 0) + return ret; + } } if (flags & AV_BUFFERSINK_FLAG_PEEK) { diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 836733ff76..766debeb71 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -334,6 +334,8 @@ int ff_poll_frame(AVFilterLink *link); */ int ff_request_frame(AVFilterLink *link); +int ff_request_frame_to_filter(AVFilterLink *link); + #define AVFILTER_DEFINE_CLASS(fname) \ static const AVClass fname##_class = { \ .class_name = #fname, \ @@ -379,6 +381,11 @@ AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name); */ void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter); +/** + * Run one round of processing on a filter graph. + */ +int ff_filter_graph_run_once(AVFilterGraph *graph); + /** * Normalize the qscale factor * FIXME the H264 qscale is a log based scale, mpeg1/2 is not, the code below