diff mbox

[FFmpeg-devel,2/2] lavfi: make filter_frame non-recursive.

Message ID 20161127160852.5460-2-george@nsup.org
State Accepted
Commit 02aa0701ae0dc2def8db640c9e3c06dc1b5de70c
Headers show

Commit Message

Nicolas George Nov. 27, 2016, 4:08 p.m. UTC
A lot of changes happen at the same time:

- Add a framequeue fifo to AVFilterLink.

- split AVFilterLink.status into status_in and status_out: requires
  changes to the few filters and programs that use it directly
  (f_interleave, split, filtfmts).

- Add a field ready to AVFilterContext, marking when the filter is ready
  and its activation priority.

- Add flags to mark blocked links.

- Change ff_filter_frame() to enqueue the frame.

- Change all filtering functions to update the ready field and the
  blocked flags.

- Update ff_filter_graph_run_once() to use the ready field.

- buffersrc: always push the frame immediately.

Signed-off-by: Nicolas George <george@nsup.org>
---
 libavfilter/avfilter.c         | 457 ++++++++++++++++++++++++++++++++++-------
 libavfilter/avfilter.h         |  71 +++++--
 libavfilter/avfiltergraph.c    |  53 ++---
 libavfilter/buffersink.c       |  21 +-
 libavfilter/buffersrc.c        |   6 +-
 libavfilter/f_interleave.c     |   8 +-
 libavfilter/internal.h         |   6 +
 libavfilter/split.c            |   5 +-
 libavfilter/tests/filtfmts.c   |   3 +
 libavfilter/vf_extractplanes.c |   6 +-
 10 files changed, 493 insertions(+), 143 deletions(-)


Changed the private fields: directly in avfilter.h with discreet ifdef
(preferred by Clément) with guarding reserved field (must urgent
countermeasure).

I made a few tests and it seems making AVFilterLink completely opaque would
be really not much work, just a few accessor functions for buffersink and
the corresponding change in ffmpeg*.c.

Still, it requires the whole deprecation dance, so it will not happen
immediately. And it conflicts with this patch, si I would like to apply it
before starting on it.

Please have a look at the rest of the code.

Comments

Andreas Cadhalpun Nov. 27, 2016, 11:08 p.m. UTC | #1
On 27.11.2016 17:08, Nicolas George wrote:
> Changed the private fields: directly in avfilter.h with discreet ifdef
> (preferred by Clément) with guarding reserved field (must urgent
> countermeasure).

Thanks.

> I made a few tests and it seems making AVFilterLink completely opaque would
> be really not much work, just a few accessor functions for buffersink and
> the corresponding change in ffmpeg*.c.
> 
> Still, it requires the whole deprecation dance, so it will not happen
> immediately. And it conflicts with this patch, si I would like to apply it
> before starting on it.

How would it conflict with this patch?
Unless I misunderstood something the deprecation should only affect the
public API, while this change should only affect the internal parts,
so the only possible conflict could be in avfilter.h, where it would be
easy to resolve.

Best regards,
Andreas
Nicolas George Nov. 27, 2016, 11:11 p.m. UTC | #2
L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
> How would it conflict with this patch?
> Unless I misunderstood something the deprecation should only affect the
> public API, while this change should only affect the internal parts,
> so the only possible conflict could be in avfilter.h, where it would be
> easy to resolve.

Making the structure opaque involves moving its definition from the
public header to a private one (first copying then removing after the
deprecation delay). Including the lines that deal with the new internal
fields.

A patch that adds lines and a patch that moves the surrounding code
conflict, there is no way about it.

Regards,
Andreas Cadhalpun Nov. 27, 2016, 11:20 p.m. UTC | #3
On 28.11.2016 00:11, Nicolas George wrote:
> L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
>> How would it conflict with this patch?
>> Unless I misunderstood something the deprecation should only affect the
>> public API, while this change should only affect the internal parts,
>> so the only possible conflict could be in avfilter.h, where it would be
>> easy to resolve.
> 
> Making the structure opaque involves moving its definition from the
> public header to a private one (first copying then removing after the
> deprecation delay). Including the lines that deal with the new internal
> fields.
> 
> A patch that adds lines and a patch that moves the surrounding code
> conflict, there is no way about it.

Sure, but that conflict should be easy to resolve.

Anyway, I'm not really thrilled by the idea of doing the deprecation
afterwards, but if you do it soon, it shouldn't be a problem in practice.

Worst case would be that if the deprecation isn't added before the next
release, this patch would need to be reverted.

Best regards,
Andreas
Nicolas George Nov. 27, 2016, 11:23 p.m. UTC | #4
L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
> Sure, but that conflict should be easy to resolve.

Easy, yes. But it must be done at each rebase, that is annoying. Wasted
time that can be better used.

> Worst case would be that if the deprecation isn't added before the next
> release, this patch would need to be reverted.

There is no reason to revert at all.

Regards,
Andreas Cadhalpun Nov. 27, 2016, 11:29 p.m. UTC | #5
On 28.11.2016 00:23, Nicolas George wrote:
> L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
>> Sure, but that conflict should be easy to resolve.
> 
> Easy, yes. But it must be done at each rebase, that is annoying. Wasted
> time that can be better used.

I'm not sure I understand. Just do the rebase once, commit the deprecation
to git master and happily work on the other changes.

>> Worst case would be that if the deprecation isn't added before the next
>> release, this patch would need to be reverted.
> 
> There is no reason to revert at all.

Not if the deprecation gets added, and thus this ifdeffery in avfilter.h
is reduced to a temporary problem.

Best regards,
Andreas
Nicolas George Nov. 28, 2016, 10:48 a.m. UTC | #6
L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
> I'm not sure I understand. Just do the rebase once, commit the deprecation
> to git master and happily work on the other changes.

Except that kind of patch requires at least three rounds of review.
Wasted time.

> Not if the deprecation gets added, and thus this ifdeffery in avfilter.h
> is reduced to a temporary problem.

With the provisions I added in the current patch, the only drawbacks
that remains are matter of elegance and minor convenience. That means it
is no matter if it stays that way a long time. Therefore, no need to
revert (urgh, you really like wasting your time! reverting that big a
patch!) even if the deprecation only reaches the repository after the
release.
Andreas Cadhalpun Nov. 28, 2016, 8:49 p.m. UTC | #7
On 28.11.2016 11:48, Nicolas George wrote:
> L'octidi 8 frimaire, an CCXXV, Andreas Cadhalpun a écrit :
>> I'm not sure I understand. Just do the rebase once, commit the deprecation
>> to git master and happily work on the other changes.
> 
> Except that kind of patch requires at least three rounds of review.
> Wasted time.

I see.

>> Not if the deprecation gets added, and thus this ifdeffery in avfilter.h
>> is reduced to a temporary problem.
> 
> With the provisions I added in the current patch, the only drawbacks
> that remains are matter of elegance and minor convenience. That means it
> is no matter if it stays that way a long time. Therefore, no need to
> revert (urgh, you really like wasting your time!

Rather the contrary, which is why I would prefer if the deprecation could
be applied first. However, I've no intention of wasting your time, either. ;)
So if it works better for you the other way around, I'm OK with that as well.

> reverting that big a patch!) even if the deprecation only reaches the
> repository after the release.

I'd rather not have a release that adds this ifdeffery to the public ABI
without deprecating direct use of the struct, though.

Best regards,
Andreas
Nicolas George Dec. 3, 2016, 3:58 p.m. UTC | #8
Le septidi 7 frimaire, an CCXXV, Nicolas George a écrit :
> A lot of changes happen at the same time:
> 
> - Add a framequeue fifo to AVFilterLink.
> 
> - split AVFilterLink.status into status_in and status_out: requires
>   changes to the few filters and programs that use it directly
>   (f_interleave, split, filtfmts).
> 
> - Add a field ready to AVFilterContext, marking when the filter is ready
>   and its activation priority.
> 
> - Add flags to mark blocked links.
> 
> - Change ff_filter_frame() to enqueue the frame.
> 
> - Change all filtering functions to update the ready field and the
>   blocked flags.
> 
> - Update ff_filter_graph_run_once() to use the ready field.
> 
> - buffersrc: always push the frame immediately.

Anybody intends to look at the code beyond the private fields issue? If
not, I could as well push now and start working on the next step and
making the structure private.

Regards,
Muhammad Faiz Dec. 3, 2016, 6:53 p.m. UTC | #9
On 11/27/16, Nicolas George <george@nsup.org> wrote:
> A lot of changes happen at the same time:
>
> - Add a framequeue fifo to AVFilterLink.
>
> - split AVFilterLink.status into status_in and status_out: requires
>   changes to the few filters and programs that use it directly
>   (f_interleave, split, filtfmts).
>
> - Add a field ready to AVFilterContext, marking when the filter is ready
>   and its activation priority.
>
> - Add flags to mark blocked links.
>
> - Change ff_filter_frame() to enqueue the frame.
>
> - Change all filtering functions to update the ready field and the
>   blocked flags.
>
> - Update ff_filter_graph_run_once() to use the ready field.
>
> - buffersrc: always push the frame immediately.

Does this make AV_BUFFERSRC_FLAG_PUSH useless?
Nicolas George Dec. 3, 2016, 6:55 p.m. UTC | #10
Le quartidi 14 frimaire, an CCXXV, Muhammad Faiz a écrit :
> Does this make AV_BUFFERSRC_FLAG_PUSH useless?

For the most part, yes. I intended to do the cleanup of these small
aspects of the API after implementing a cleaner over API.

Regards,
Nicolas George Dec. 11, 2016, 4:36 p.m. UTC | #11
Le tridi 13 frimaire, an CCXXV, Nicolas George a écrit :
> Anybody intends to look at the code beyond the private fields issue? If
> not, I could as well push now and start working on the next step and
> making the structure private.

I will do so after the end of this week if I see no news.

Regards,
Nicolas George Dec. 18, 2016, 9:51 a.m. UTC | #12
Le primidi 21 frimaire, an CCXXV, Nicolas George a écrit :
> I will do so after the end of this week if I see no news.

Done.

Regards,
Marton Balint Dec. 21, 2016, 6:57 p.m. UTC | #13
On Sun, 18 Dec 2016, Nicolas George wrote:

> Le primidi 21 frimaire, an CCXXV, Nicolas George a écrit :
>> I will do so after the end of this week if I see no news.
>
> Done.
>

It seems after this patch I got an infinite loop if I try to convert the 
attached file using this command line:

./ffmpeg -i amerge-test.mov -filter_complex "[0:a:0][0:a:1]amerge=2[aout]" 
-map "[aout]" out.wav

Note that in my build FF_BUFQUEUE_SIZE in libavfilter/bufferqueue.h is set 
to 256, because otherwise it errors out with ENOMEM, but that also happens 
with the old filtering code. On the other hand, the old filtering code and 
FF_BUFQUEUE_SIZE set to 256 does allow the file to properly convert.

Thanks,
Marton
Nicolas George Dec. 21, 2016, 7:07 p.m. UTC | #14
Le primidi 1er nivôse, an CCXXV, Marton Balint a écrit :
> It seems after this patch I got an infinite loop if I try to convert the
> attached file using this command line:
> 
> ./ffmpeg -i amerge-test.mov -filter_complex "[0:a:0][0:a:1]amerge=2[aout]"
> -map "[aout]" out.wav

That is a bigger glitch. I will look into it as soon as possible.

Regards,
James Almer Dec. 23, 2016, 12:23 a.m. UTC | #15
On 11/27/2016 1:08 PM, Nicolas George wrote:
> A lot of changes happen at the same time:
> 
> - Add a framequeue fifo to AVFilterLink.
> 
> - split AVFilterLink.status into status_in and status_out: requires
>   changes to the few filters and programs that use it directly
>   (f_interleave, split, filtfmts).
> 
> - Add a field ready to AVFilterContext, marking when the filter is ready
>   and its activation priority.
> 
> - Add flags to mark blocked links.
> 
> - Change ff_filter_frame() to enqueue the frame.
> 
> - Change all filtering functions to update the ready field and the
>   blocked flags.
> 
> - Update ff_filter_graph_run_once() to use the ready field.
> 
> - buffersrc: always push the frame immediately.
> 
> Signed-off-by: Nicolas George <george@nsup.org>
> ---
>  libavfilter/avfilter.c         | 457 ++++++++++++++++++++++++++++++++++-------
>  libavfilter/avfilter.h         |  71 +++++--
>  libavfilter/avfiltergraph.c    |  53 ++---
>  libavfilter/buffersink.c       |  21 +-
>  libavfilter/buffersrc.c        |   6 +-
>  libavfilter/f_interleave.c     |   8 +-
>  libavfilter/internal.h         |   6 +
>  libavfilter/split.c            |   5 +-
>  libavfilter/tests/filtfmts.c   |   3 +
>  libavfilter/vf_extractplanes.c |   6 +-
>  10 files changed, 493 insertions(+), 143 deletions(-)

This patch made audio encoding extremely slow.

time ./ffmpeg -i INPUT.wav -c:a tta -f null -

Before
size=N/A time=00:00:22.98 bitrate=N/A speed= 373x
video:0kB audio:1272kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
real    0m0.089s
user    0m0.000s
sys     0m0.000s

After
size=N/A time=00:00:22.98 bitrate=N/A speed=2.29x
video:0kB audio:1272kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
real    0m10.087s
user    0m0.015s
sys     0m0.000s

CPU usage for the latter is almost zero during the entire 10 seconds. And it can be reproduced
with pretty much any encoder.
Nicolas George Dec. 23, 2016, 8:50 a.m. UTC | #16
Le duodi 2 nivôse, an CCXXV, James Almer a écrit :
> This patch made audio encoding extremely slow.
> 
> time ./ffmpeg -i INPUT.wav -c:a tta -f null -
> 
> Before
> size=N/A time=00:00:22.98 bitrate=N/A speed= 373x
> video:0kB audio:1272kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
> real    0m0.089s
> user    0m0.000s
> sys     0m0.000s
> 
> After
> size=N/A time=00:00:22.98 bitrate=N/A speed=2.29x
> video:0kB audio:1272kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
> real    0m10.087s
> user    0m0.015s
> sys     0m0.000s
> 
> CPU usage for the latter is almost zero during the entire 10 seconds. And it can be reproduced
> with pretty much any encoder.

Yes, thanks. This is the glitch I am currently working on, and I think
it is almost fixed cleanly. Note that the actual time is still very
small, it is only ffmpeg keeps thinking the filter is not ready and
sleeping.

Regards,
Nicolas George Dec. 23, 2016, 10:41 p.m. UTC | #17
Le tridi 13 frimaire, an CCXXV, Nicolas George a écrit :
> > Does this make AV_BUFFERSRC_FLAG_PUSH useless?
> For the most part, yes. I intended to do the cleanup of these small
> aspects of the API after implementing a cleaner over API.

Hum, scratch that, it is still acutely needed with certain ways of
using the API. I intend to propose to deprecate them when I have
implemented better ones, but that is for later.

Regards,
diff mbox

Patch

diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index 662f933..56fb0be 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -34,6 +34,9 @@ 
 #include "libavutil/rational.h"
 #include "libavutil/samplefmt.h"
 
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "audio.h"
 #include "avfilter.h"
 #include "formats.h"
@@ -135,6 +138,10 @@  int avfilter_link(AVFilterContext *src, unsigned srcpad,
 {
     AVFilterLink *link;
 
+    av_assert0(src->graph);
+    av_assert0(dst->graph);
+    av_assert0(src->graph == dst->graph);
+
     if (src->nb_outputs <= srcpad || dst->nb_inputs <= dstpad ||
         src->outputs[srcpad]      || dst->inputs[dstpad])
         return AVERROR(EINVAL);
@@ -160,6 +167,7 @@  int avfilter_link(AVFilterContext *src, unsigned srcpad,
     link->type    = src->output_pads[srcpad].type;
     av_assert0(AV_PIX_FMT_NONE == -1 && AV_SAMPLE_FMT_NONE == -1);
     link->format  = -1;
+    ff_framequeue_init(&link->fifo, &src->graph->internal->frame_queues);
 
     return 0;
 }
@@ -170,6 +178,7 @@  void avfilter_link_free(AVFilterLink **link)
         return;
 
     av_frame_free(&(*link)->partial_buf);
+    ff_framequeue_free(&(*link)->fifo);
     ff_video_frame_pool_uninit((FFVideoFramePool**)&(*link)->video_frame_pool);
 
     av_freep(link);
@@ -180,16 +189,46 @@  int avfilter_link_get_channels(AVFilterLink *link)
     return link->channels;
 }
 
+static void ff_filter_set_ready(AVFilterContext *filter, unsigned priority)
+{
+    filter->ready = FFMAX(filter->ready, priority);
+}
+
+/**
+ * Clear frame_blocked_in on all outputs.
+ * This is necessary whenever something changes on input.
+ */
+static void filter_unblock(AVFilterContext *filter)
+{
+    unsigned i;
+
+    for (i = 0; i < filter->nb_outputs; i++)
+        filter->outputs[i]->frame_blocked_in = 0;
+}
+
+
 void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts)
 {
-    ff_avfilter_link_set_out_status(link, status, pts);
+    if (link->status_in == status)
+        return;
+    av_assert0(!link->status_in);
+    link->status_in = status;
+    link->status_in_pts = pts;
+    link->frame_wanted_out = 0;
+    link->frame_blocked_in = 0;
+    filter_unblock(link->dst);
+    ff_filter_set_ready(link->dst, 200);
 }
 
 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);
+    av_assert0(!link->frame_wanted_out);
+    av_assert0(!link->status_out);
+    link->status_out = status;
+    if (pts != AV_NOPTS_VALUE)
+        ff_update_link_current_pts(link, pts);
+    filter_unblock(link->dst);
+    ff_filter_set_ready(link->src, 200);
 }
 
 void avfilter_link_set_closed(AVFilterLink *link, int closed)
@@ -370,10 +409,23 @@  int ff_request_frame(AVFilterLink *link)
 {
     FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1);
 
-    if (link->status)
-        return link->status;
-    link->frame_wanted_in = 1;
+    if (link->status_out)
+        return link->status_out;
+    if (link->status_in) {
+        if (ff_framequeue_queued_frames(&link->fifo)) {
+            av_assert1(!link->frame_wanted_out);
+            av_assert1(link->dst->ready >= 300);
+            return 0;
+        } else {
+            /* Acknowledge status change. Filters using ff_request_frame() will
+               handle the change automatically. Filters can also check the
+               status directly but none do yet. */
+            ff_avfilter_link_set_out_status(link, link->status_in, link->status_in_pts);
+            return link->status_out;
+        }
+    }
     link->frame_wanted_out = 1;
+    ff_filter_set_ready(link->src, 100);
     return 0;
 }
 
@@ -382,22 +434,17 @@  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;
+    /* Assume the filter is blocked, let the method clear it if not */
+    link->frame_blocked_in = 1;
     if (link->srcpad->request_frame)
         ret = link->srcpad->request_frame(link);
     else if (link->src->inputs[0])
         ret = ff_request_frame(link->src->inputs[0]);
-    if (ret == AVERROR_EOF && link->partial_buf) {
-        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)
+        if (ret != AVERROR(EAGAIN) && ret != link->status_in)
             ff_avfilter_link_set_in_status(link, ret, AV_NOPTS_VALUE);
+        if (ret == AVERROR_EOF)
+            ret = 0;
     }
     return ret;
 }
@@ -1056,11 +1103,6 @@  static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame)
     AVFilterCommand *cmd= link->dst->command_queue;
     int64_t pts;
 
-    if (link->status) {
-        av_frame_free(&frame);
-        return link->status;
-    }
-
     if (!(filter_frame = dst->filter_frame))
         filter_frame = default_filter_frame;
 
@@ -1142,52 +1184,9 @@  fail:
     return ret;
 }
 
-static int ff_filter_frame_needs_framing(AVFilterLink *link, AVFrame *frame)
-{
-    int insamples = frame->nb_samples, inpos = 0, nb_samples;
-    AVFrame *pbuf = link->partial_buf;
-    int nb_channels = av_frame_get_channels(frame);
-    int ret = 0;
-
-    /* Handle framing (min_samples, max_samples) */
-    while (insamples) {
-        if (!pbuf) {
-            AVRational samples_tb = { 1, link->sample_rate };
-            pbuf = ff_get_audio_buffer(link, link->partial_buf_size);
-            if (!pbuf) {
-                av_log(link->dst, AV_LOG_WARNING,
-                       "Samples dropped due to memory allocation failure.\n");
-                return 0;
-            }
-            av_frame_copy_props(pbuf, frame);
-            pbuf->pts = frame->pts;
-            if (pbuf->pts != AV_NOPTS_VALUE)
-                pbuf->pts += av_rescale_q(inpos, samples_tb, link->time_base);
-            pbuf->nb_samples = 0;
-        }
-        nb_samples = FFMIN(insamples,
-                           link->partial_buf_size - pbuf->nb_samples);
-        av_samples_copy(pbuf->extended_data, frame->extended_data,
-                        pbuf->nb_samples, inpos,
-                        nb_samples, nb_channels, link->format);
-        inpos                   += nb_samples;
-        insamples               -= nb_samples;
-        pbuf->nb_samples += nb_samples;
-        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);
-    link->partial_buf = pbuf;
-    return ret;
-}
-
 int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
 {
+    int ret;
     FF_TPRINTF_START(NULL, filter_frame); ff_tlog_link(NULL, link, 1); ff_tlog(NULL, " "); ff_tlog_ref(NULL, frame, 1);
 
     /* Consistency checks */
@@ -1220,23 +1219,329 @@  int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
         }
     }
 
-    link->frame_wanted_out = 0;
+    link->frame_blocked_in = link->frame_wanted_out = 0;
     link->frame_count_in++;
-    /* Go directly to actual filtering if possible */
-    if (link->type == AVMEDIA_TYPE_AUDIO &&
-        link->min_samples &&
-        (link->partial_buf ||
-         frame->nb_samples < link->min_samples ||
-         frame->nb_samples > link->max_samples)) {
-        return ff_filter_frame_needs_framing(link, frame);
-    } else {
-        return ff_filter_frame_framed(link, frame);
+    filter_unblock(link->dst);
+    ret = ff_framequeue_add(&link->fifo, frame);
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
     }
+    ff_filter_set_ready(link->dst, 300);
+    return 0;
+
 error:
     av_frame_free(&frame);
     return AVERROR_PATCHWELCOME;
 }
 
+static int samples_ready(AVFilterLink *link)
+{
+    return ff_framequeue_queued_frames(&link->fifo) &&
+           (ff_framequeue_queued_samples(&link->fifo) >= link->min_samples ||
+            link->status_in);
+}
+
+static int take_samples(AVFilterLink *link, unsigned min, unsigned max,
+                        AVFrame **rframe)
+{
+    AVFrame *frame0, *frame, *buf;
+    unsigned nb_samples, nb_frames, i, p;
+    int ret;
+
+    /* Note: this function relies on no format changes and must only be
+       called with enough samples. */
+    av_assert1(samples_ready(link));
+    frame0 = frame = ff_framequeue_peek(&link->fifo, 0);
+    if (frame->nb_samples >= min && frame->nb_samples < max) {
+        *rframe = ff_framequeue_take(&link->fifo);
+        return 0;
+    }
+    nb_frames = 0;
+    nb_samples = 0;
+    while (1) {
+        if (nb_samples + frame->nb_samples > max) {
+            if (nb_samples < min)
+                nb_samples = max;
+            break;
+        }
+        nb_samples += frame->nb_samples;
+        nb_frames++;
+        if (nb_frames == ff_framequeue_queued_frames(&link->fifo))
+            break;
+        frame = ff_framequeue_peek(&link->fifo, nb_frames);
+    }
+
+    buf = ff_get_audio_buffer(link, nb_samples);
+    if (!buf)
+        return AVERROR(ENOMEM);
+    ret = av_frame_copy_props(buf, frame0);
+    if (ret < 0) {
+        av_frame_free(&buf);
+        return ret;
+    }
+    buf->pts = frame0->pts;
+
+    p = 0;
+    for (i = 0; i < nb_frames; i++) {
+        frame = ff_framequeue_take(&link->fifo);
+        av_samples_copy(buf->extended_data, frame->extended_data, p, 0,
+                        frame->nb_samples, link->channels, link->format);
+        p += frame->nb_samples;
+    }
+    if (p < nb_samples) {
+        unsigned n = nb_samples - p;
+        frame = ff_framequeue_peek(&link->fifo, 0);
+        av_samples_copy(buf->extended_data, frame->extended_data, p, 0, n,
+                        link->channels, link->format);
+        frame->nb_samples -= n;
+        av_samples_copy(frame->extended_data, frame->extended_data, 0, n,
+                        frame->nb_samples, link->channels, link->format);
+        if (frame->pts != AV_NOPTS_VALUE)
+            frame->pts += av_rescale_q(n, av_make_q(1, link->sample_rate), link->time_base);
+        ff_framequeue_update_peeked(&link->fifo, 0);
+        ff_framequeue_skip_samples(&link->fifo, n);
+    }
+
+    *rframe = buf;
+    return 0;
+}
+
+int ff_filter_frame_to_filter(AVFilterLink *link)
+{
+    AVFrame *frame;
+    AVFilterContext *dst = link->dst;
+    int ret;
+
+    av_assert1(ff_framequeue_queued_frames(&link->fifo));
+    if (link->min_samples) {
+        int min = link->min_samples;
+        if (link->status_in)
+            min = FFMIN(min, ff_framequeue_queued_samples(&link->fifo));
+        ret = take_samples(link, min, link->max_samples, &frame);
+        if (ret < 0)
+            return ret;
+    } else {
+        frame = ff_framequeue_take(&link->fifo);
+    }
+    /* The filter will soon have received a new frame, that may allow it to
+       produce one or more: unblock its outputs. */
+    filter_unblock(dst);
+    ret = ff_filter_frame_framed(link, frame);
+    if (ret < 0 && ret != link->status_out) {
+        ff_avfilter_link_set_out_status(link, ret, AV_NOPTS_VALUE);
+    } else {
+        /* Run once again, to see if several frames were available, or if
+           the input status has also changed, or any other reason. */
+        ff_filter_set_ready(dst, 300);
+    }
+    return ret;
+}
+
+static int forward_status_change(AVFilterContext *filter, AVFilterLink *in)
+{
+    unsigned out = 0, progress = 0;
+    int ret;
+
+    av_assert0(!in->status_out);
+    if (!filter->nb_outputs) {
+        /* not necessary with the current API and sinks */
+        return 0;
+    }
+    while (!in->status_out) {
+        if (!filter->outputs[out]->status_in) {
+            progress++;
+            ret = ff_request_frame_to_filter(filter->outputs[out]);
+            if (ret < 0)
+                return ret;
+        }
+        if (++out == filter->nb_outputs) {
+            if (!progress) {
+                /* Every output already closed: input no longer interesting
+                   (example: overlay in shortest mode, other input closed). */
+                ff_avfilter_link_set_out_status(in, in->status_in, in->status_in_pts);
+                return 0;
+            }
+            progress = 0;
+            out = 0;
+        }
+    }
+    ff_filter_set_ready(filter, 200);
+    return 0;
+}
+
+#define FFERROR_NOT_READY FFERRTAG('N','R','D','Y')
+
+static int ff_filter_activate_default(AVFilterContext *filter)
+{
+    unsigned i;
+
+    for (i = 0; i < filter->nb_inputs; i++) {
+        if (samples_ready(filter->inputs[i])) {
+            return ff_filter_frame_to_filter(filter->inputs[i]);
+        }
+    }
+    for (i = 0; i < filter->nb_inputs; i++) {
+        if (filter->inputs[i]->status_in && !filter->inputs[i]->status_out) {
+            av_assert1(!ff_framequeue_queued_frames(&filter->inputs[i]->fifo));
+            return forward_status_change(filter, filter->inputs[i]);
+        }
+    }
+    for (i = 0; i < filter->nb_outputs; i++) {
+        if (filter->outputs[i]->frame_wanted_out &&
+            !filter->outputs[i]->frame_blocked_in) {
+            return ff_request_frame_to_filter(filter->outputs[i]);
+        }
+    }
+    return FFERROR_NOT_READY;
+}
+
+/*
+   Filter scheduling and activation
+
+   When a filter is activated, it must:
+   - if possible, output a frame;
+   - else, if relevant, forward the input status change;
+   - else, check outputs for wanted frames and forward the requests.
+
+   The following AVFilterLink fields are used for activation:
+
+   - frame_wanted_out:
+
+     This field indicates if a frame is needed on this input of the
+     destination filter. A positive value indicates that a frame is needed
+     to process queued frames or internal data or to satisfy the
+     application; a zero value indicates that a frame is not especially
+     needed but could be processed anyway; a negative value indicates that a
+     frame would just be queued.
+
+     It is set by filters using ff_request_frame() or ff_request_no_frame(),
+     when requested by the application through a specific API or when it is
+     set on one of the outputs.
+
+     It is cleared when a frame is sent from the source using
+     ff_filter_frame().
+
+     It is also cleared when a status change is sent from the source using
+     ff_avfilter_link_set_in_status().
+
+   - frame_blocked_in:
+
+     This field means that the source filter can not generate a frame as is.
+     Its goal is to avoid repeatedly calling the request_frame() method on
+     the same link.
+
+     It is set by the framework on all outputs of a filter before activating it.
+
+     It is automatically cleared by ff_filter_frame().
+
+     It is also automatically cleared by ff_avfilter_link_set_in_status().
+
+     It is also cleared on all outputs (using filter_unblock()) when
+     something happens on an input: processing a frame or changing the
+     status.
+
+   - fifo:
+
+     Contains the frames queued on a filter input. If it contains frames and
+     frame_wanted_out is not set, then the filter can be activated. If that
+     result in the filter not able to use these frames, the filter must set
+     frame_wanted_out to ask for more frames.
+
+   - status_in and status_in_pts:
+
+     Status (EOF or error code) of the link and timestamp of the status
+     change (in link time base, same as frames) as seen from the input of
+     the link. The status change is considered happening after the frames
+     queued in fifo.
+
+     It is set by the source filter using ff_avfilter_link_set_in_status().
+
+   - status_out:
+
+     Status of the link as seen from the output of the link. The status
+     change is considered having already happened.
+
+     It is set by the destination filter using
+     ff_avfilter_link_set_out_status().
+
+   Filters are activated according to the ready field, set using the
+   ff_filter_set_ready(). Eventually, a priority queue will be used.
+   ff_filter_set_ready() is called whenever anything could cause progress to
+   be possible. Marking a filter ready when it is not is not a problem,
+   except for the small overhead it causes.
+
+   Conditions that cause a filter to be marked ready are:
+
+   - frames added on an input link;
+
+   - changes in the input or output status of an input link;
+
+   - requests for a frame on an output link;
+
+   - after any actual processing using the legacy methods (filter_frame(),
+     and request_frame() to acknowledge status changes), to run once more
+     and check if enough input was present for several frames.
+
+   Exemples of scenarios to consider:
+
+   - buffersrc: activate if frame_wanted_out to notify the application;
+     activate when the application adds a frame to push it immediately.
+
+   - testsrc: activate only if frame_wanted_out to produce and push a frame.
+
+   - concat (not at stitch points): can process a frame on any output.
+     Activate if frame_wanted_out on output to forward on the corresponding
+     input. Activate when a frame is present on input to process it
+     immediately.
+
+   - framesync: needs at least one frame on each input; extra frames on the
+     wrong input will accumulate. When a frame is first added on one input,
+     set frame_wanted_out<0 on it to avoid getting more (would trigger
+     testsrc) and frame_wanted_out>0 on the other to allow processing it.
+
+   Activation of old filters:
+
+   In order to activate a filter implementing the legacy filter_frame() and
+   request_frame() methods, perform the first possible of the following
+   actions:
+
+   - If an input has frames in fifo and frame_wanted_out == 0, dequeue a
+     frame and call filter_frame().
+
+     Ratinale: filter frames as soon as possible instead of leaving them
+     queued; frame_wanted_out < 0 is not possible since the old API does not
+     set it nor provides any similar feedback; frame_wanted_out > 0 happens
+     when min_samples > 0 and there are not enough samples queued.
+
+   - If an input has status_in set but not status_out, try to call
+     request_frame() on one of the outputs in the hope that it will trigger
+     request_frame() on the input with status_in and acknowledge it. This is
+     awkward and fragile, filters with several inputs or outputs should be
+     updated to direct activation as soon as possible.
+
+   - If an output has frame_wanted_out > 0 and not frame_blocked_in, call
+     request_frame().
+
+     Rationale: checking frame_blocked_in is necessary to avoid requesting
+     repeatedly on a blocked input if another is not blocked (example:
+     [buffersrc1][testsrc1][buffersrc2][testsrc2]concat=v=2).
+
+     TODO: respect needs_fifo and remove auto-inserted fifos.
+
+ */
+
+int ff_filter_activate(AVFilterContext *filter)
+{
+    int ret;
+
+    filter->ready = 0;
+    ret = ff_filter_activate_default(filter);
+    if (ret == FFERROR_NOT_READY)
+        ret = 0;
+    return ret;
+}
+
 const AVClass *avfilter_get_class(void)
 {
     return &avfilter_class;
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index d21b144..828b270 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -368,6 +368,13 @@  struct AVFilterContext {
      * Overrides global number of threads set per filter graph.
      */
     int nb_threads;
+
+    /**
+     * Ready status of the filter.
+     * A non-0 value means that the filter needs activating;
+     * a higher value suggests a more urgent activation.
+     */
+    unsigned ready;
 };
 
 /**
@@ -509,18 +516,6 @@  struct AVFilterLink {
     int max_samples;
 
     /**
-     * Link status.
-     * If not zero, all attempts of filter_frame or request_frame
-     * will fail with the corresponding code, and if necessary the reference
-     * will be destroyed.
-     * If request_frame returns an error, the status is set on the
-     * corresponding link.
-     * It can be set also be set by either the source or the destination
-     * filter.
-     */
-    int status;
-
-    /**
      * Number of channels.
      */
     int channels;
@@ -541,13 +536,6 @@  struct AVFilterLink {
     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.
@@ -559,6 +547,51 @@  struct AVFilterLink {
      * AVHWFramesContext describing the frames.
      */
     AVBufferRef *hw_frames_ctx;
+
+#ifndef FF_INTERNAL_FIELDS
+
+    /**
+     * Internal structure members.
+     * The fields below this limit are internal for libavfilter's use
+     * and must in no way be accessed by applications.
+     */
+    char reserved[0xF000];
+
+#else /* FF_INTERNAL_FIELDS */
+
+    /**
+     * Queue of frames waiting to be filtered.
+     */
+    FFFrameQueue fifo;
+
+    /**
+     * If set, the source filter can not generate a frame as is.
+     * The goal is to avoid repeatedly calling the request_frame() method on
+     * the same link.
+     */
+    int frame_blocked_in;
+
+    /**
+     * Link input status.
+     * If not zero, all attempts of filter_frame will fail with the
+     * corresponding code.
+     */
+    int status_in;
+
+    /**
+     * Timestamp of the input status change.
+     */
+    int64_t status_in_pts;
+
+    /**
+     * Link output status.
+     * If not zero, all attempts of request_frame will fail with the
+     * corresponding code.
+     */
+    int status_out;
+
+#endif /* FF_INTERNAL_FIELDS */
+
 };
 
 /**
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index 3af698d..6b5a6f3 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -32,6 +32,9 @@ 
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
 
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "avfilter.h"
 #include "formats.h"
 #include "internal.h"
@@ -87,6 +90,7 @@  AVFilterGraph *avfilter_graph_alloc(void)
 
     ret->av_class = &filtergraph_class;
     av_opt_set_defaults(ret);
+    ff_framequeue_global_init(&ret->internal->frame_queues);
 
     return ret;
 }
@@ -1377,10 +1381,10 @@  void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link)
     heap_bubble_down(graph, link, link->age_index);
 }
 
-
 int avfilter_graph_request_oldest(AVFilterGraph *graph)
 {
     AVFilterLink *oldest = graph->sink_links[0];
+    int64_t frame_count;
     int r;
 
     while (graph->sink_links_count) {
@@ -1400,7 +1404,8 @@  int avfilter_graph_request_oldest(AVFilterGraph *graph)
     if (!graph->sink_links_count)
         return AVERROR_EOF;
     av_assert1(oldest->age_index >= 0);
-    while (oldest->frame_wanted_out) {
+    frame_count = oldest->frame_count_out;
+    while (frame_count == oldest->frame_count_out) {
         r = ff_filter_graph_run_once(graph);
         if (r < 0)
             return r;
@@ -1408,41 +1413,17 @@  int avfilter_graph_request_oldest(AVFilterGraph *graph)
     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");
+    AVFilterContext *filter;
+    unsigned i;
+
+    av_assert0(graph->nb_filters);
+    filter = graph->filters[0];
+    for (i = 1; i < graph->nb_filters; i++)
+        if (graph->filters[i]->ready > filter->ready)
+            filter = graph->filters[i];
+    if (!filter->ready)
         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;
+    return ff_filter_activate(filter);
 }
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index 2feb56d..7b7b477 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -31,6 +31,9 @@ 
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
 
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "audio.h"
 #include "avfilter.h"
 #include "buffersink.h"
@@ -129,18 +132,26 @@  int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFr
 {
     BufferSinkContext *buf = ctx->priv;
     AVFilterLink *inlink = ctx->inputs[0];
-    int ret;
+    int peek_in_framequeue = 0, ret;
+    int64_t frame_count;
     AVFrame *cur_frame;
 
     /* no picref available, fetch it from the filterchain */
     while (!av_fifo_size(buf->fifo)) {
-        if (inlink->status)
-            return inlink->status;
-        if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST)
+        /* if peek_in_framequeue is true later, then ff_request_frame() and
+           the ff_filter_graph_run_once() loop will take a frame from it and
+           move it to the internal fifo, ending the global loop */
+        av_assert0(!peek_in_framequeue);
+        if (inlink->status_out)
+            return inlink->status_out;
+        peek_in_framequeue = ff_framequeue_queued_frames(&inlink->fifo) &&
+                             ff_framequeue_queued_samples(&inlink->fifo) >= inlink->min_samples;
+        if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST) && !peek_in_framequeue)
             return AVERROR(EAGAIN);
         if ((ret = ff_request_frame(inlink)) < 0)
             return ret;
-        while (inlink->frame_wanted_out) {
+        frame_count = inlink->frame_count_out;
+        while (frame_count == inlink->frame_count_out) {
             ret = ff_filter_graph_run_once(ctx->graph);
             if (ret < 0)
                 return ret;
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index 9294811..1314397 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -184,6 +184,7 @@  static int av_buffersrc_add_frame_internal(AVFilterContext *ctx,
 
     if (!frame) {
         s->eof = 1;
+        ff_avfilter_link_set_in_status(ctx->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE);
         return 0;
     } else if (s->eof)
         return AVERROR(EINVAL);
@@ -235,9 +236,8 @@  static int av_buffersrc_add_frame_internal(AVFilterContext *ctx,
         return ret;
     }
 
-    if ((flags & AV_BUFFERSRC_FLAG_PUSH))
-        if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0)
-            return ret;
+    if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0)
+        return ret;
 
     return 0;
 }
diff --git a/libavfilter/f_interleave.c b/libavfilter/f_interleave.c
index 422f2bf..b9192e9 100644
--- a/libavfilter/f_interleave.c
+++ b/libavfilter/f_interleave.c
@@ -26,6 +26,10 @@ 
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/opt.h"
+
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "avfilter.h"
 #include "bufferqueue.h"
 #include "formats.h"
@@ -59,7 +63,7 @@  inline static int push_frame(AVFilterContext *ctx)
     for (i = 0; i < ctx->nb_inputs; i++) {
         struct FFBufQueue *q = &s->queues[i];
 
-        if (!q->available && !ctx->inputs[i]->status)
+        if (!q->available && !ctx->inputs[i]->status_out)
             return 0;
         if (q->available) {
             frame = ff_bufqueue_peek(q, 0);
@@ -190,7 +194,7 @@  static int request_frame(AVFilterLink *outlink)
     int i, ret;
 
     for (i = 0; i < ctx->nb_inputs; i++) {
-        if (!s->queues[i].available && !ctx->inputs[i]->status) {
+        if (!s->queues[i].available && !ctx->inputs[i]->status_out) {
             ret = ff_request_frame(ctx->inputs[i]);
             if (ret != AVERROR_EOF)
                 return ret;
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index 3856012..a8b69fd 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -29,6 +29,7 @@ 
 #include "avfiltergraph.h"
 #include "formats.h"
 #include "framepool.h"
+#include "framequeue.h"
 #include "thread.h"
 #include "version.h"
 #include "video.h"
@@ -147,6 +148,7 @@  struct AVFilterPad {
 struct AVFilterGraphInternal {
     void *thread;
     avfilter_execute_func *thread_execute;
+    FFFrameQueueGlobal frame_queues;
 };
 
 struct AVFilterInternal {
@@ -336,6 +338,8 @@  int ff_request_frame(AVFilterLink *link);
 
 int ff_request_frame_to_filter(AVFilterLink *link);
 
+int ff_filter_frame_to_filter(AVFilterLink *link);
+
 #define AVFILTER_DEFINE_CLASS(fname)            \
     static const AVClass fname##_class = {      \
         .class_name = #fname,                   \
@@ -376,6 +380,8 @@  int ff_filter_frame(AVFilterLink *link, AVFrame *frame);
  */
 AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name);
 
+int ff_filter_activate(AVFilterContext *filter);
+
 /**
  * Remove a filter from a graph;
  */
diff --git a/libavfilter/split.c b/libavfilter/split.c
index 6cf4ab1..d1121d6 100644
--- a/libavfilter/split.c
+++ b/libavfilter/split.c
@@ -30,6 +30,9 @@ 
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
 
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "avfilter.h"
 #include "audio.h"
 #include "formats.h"
@@ -78,7 +81,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
     for (i = 0; i < ctx->nb_outputs; i++) {
         AVFrame *buf_out;
 
-        if (ctx->outputs[i]->status)
+        if (ctx->outputs[i]->status_in)
             continue;
         buf_out = av_frame_clone(frame);
         if (!buf_out) {
diff --git a/libavfilter/tests/filtfmts.c b/libavfilter/tests/filtfmts.c
index 46a2d94..f59199c 100644
--- a/libavfilter/tests/filtfmts.c
+++ b/libavfilter/tests/filtfmts.c
@@ -25,6 +25,9 @@ 
 #include "libavutil/pixdesc.h"
 #include "libavutil/samplefmt.h"
 
+#define FF_INTERNAL_FIELDS 1
+#include "libavfilter/framequeue.h"
+
 #include "libavfilter/avfilter.h"
 #include "libavfilter/formats.h"
 
diff --git a/libavfilter/vf_extractplanes.c b/libavfilter/vf_extractplanes.c
index a23f838..e9433b3 100644
--- a/libavfilter/vf_extractplanes.c
+++ b/libavfilter/vf_extractplanes.c
@@ -22,6 +22,10 @@ 
 #include "libavutil/imgutils.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
+
+#define FF_INTERNAL_FIELDS 1
+#include "libavfilter/framequeue.h"
+
 #include "avfilter.h"
 #include "drawutils.h"
 #include "internal.h"
@@ -245,7 +249,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
         const int idx = s->map[i];
         AVFrame *out;
 
-        if (outlink->status)
+        if (outlink->status_in)
             continue;
 
         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);