lavfi: make request_frame() non-recursive.
authorNicolas George <george@nsup.org>
Wed, 30 Sep 2015 14:11:57 +0000 (16:11 +0200)
committerNicolas George <george@nsup.org>
Tue, 22 Dec 2015 15:04:30 +0000 (16:04 +0100)
Instead of calling the input filter request_frame() method,
ff_request_frame() now marks the link and returns immediately.
buffersink is changed to activate the marked filters until
a frame is obtained.

libavfilter/avfilter.c
libavfilter/avfilter.h
libavfilter/avfiltergraph.c
libavfilter/buffersink.c
libavfilter/internal.h

index cdb47f7..2f4d59f 100644 (file)
@@ -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 &&
index c52175b..599369d 100644 (file)
@@ -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;
 };
 
 /**
index ec2245f..9f50b41 100644 (file)
@@ -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;
 }
index 7a19df2..2feb56d 100644 (file)
@@ -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) {
index 836733f..766debe 100644 (file)
@@ -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,                   \
@@ -380,6 +382,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
  *       cannot be optimal