diff mbox

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

Message ID 20161222111828.GA767956@phare.normalesup.org
State Superseded
Headers show

Commit Message

Nicolas George Dec. 22, 2016, 11:18 a.m. UTC
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

Can you confirm the attached patch fixes the issue?

If so, please do not hesitate to push it without waiting for me if (and
only if) that is convenient for you.

> 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.

I wish I remembered that precision before spending time tracking the
ENOMEM :(

Fortunately, one of the perks of these changes is they are a step closer
to getting rid of that particular issue once and for all. I suspect
rudimentary memory limitation will be needed first, but that can be
managed.

Regards,

Comments

Marton Balint Dec. 23, 2016, 3:22 a.m. UTC | #1
On Thu, 22 Dec 2016, Nicolas George wrote:

> 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
>
> Can you confirm the attached patch fixes the issue?

Yes, thanks.

>
> If so, please do not hesitate to push it without waiting for me if (and
> only if) that is convenient for you.

Ok, pushed.

>
>> 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.
>
> I wish I remembered that precision before spending time tracking the
> ENOMEM :(
>
> Fortunately, one of the perks of these changes is they are a step closer
> to getting rid of that particular issue once and for all. I suspect
> rudimentary memory limitation will be needed first, but that can be
> managed.

I guess we could buffer the undecoded packets instead of the decoded 
frames if there is a higher-than-usual delay between the streams. Is this 
also your plan?

Thanks,
Marton
Nicolas George Dec. 23, 2016, 11:42 p.m. UTC | #2
Le tridi 3 nivôse, an CCXXV, Marton Balint a écrit :
> I guess we could buffer the undecoded packets instead of the decoded frames
> if there is a higher-than-usual delay between the streams. Is this also your
> plan?

Well, at some point in the far future I would have libavfilter capable
of handling AVMEDIA_TYPE_DATA frames and codecs as filters. If that
happens, then tweaking the scheduling to prioritize consuming large
frames first would be useful.

But that is not the issue at all.

The problem with bufferqueue is this:

For normal cases, cases that are supposed to work, the need of
bufferqueue is bounded. But with some codecs (those with huge frames
that get split, for example) or formats (depending on the A-V muxing
delay), it can sometimes be quite large. You observed that 64 is not
enough for your uses but 256 is. Probably, someone somewhere needs 1024
or more.

Increasing the size of the queue wastes a little memory. Not much, but
still not satisfactory. The obvious solution is to make it dynamic.
Inserting the fifo filter as input achieves it, because fifo has an
unlimited buffer, not implemented using bufferqueue. Bug fifo breaks the
scheduling because the buffer is hidden.

The new data structure I added to AVFilterLink a few days ago is exactly
what we need, it gives us a dynamic FIFO that works fine with the
scheduling. It still requires some code to permit filters to use it
directly (done, in my work tree), and then adapt amerge and the other
filters to do it (not yet done).

But there is a catch: sometimes people do something crazy, often
inadvertently, and that causes unlimited amounts of frames to accumulate
in the filter's input before any processing can be done.

Currently, when that happens, they see "buffer queue overflow" and the
encoding stops, and they come and ask for help on the mailing-list.

If the queue is not bounded, it will grow, eat all memory, then start
filling the swap, until the OOM-killer intervenes. Not good, really not
good.

My solution: make the queue unbounded in principle, but keep stats on
the use and add a configurable limit. Quite obvious, actually.

I have two bonus in my plans.

First, make the stats global to the filter graph. Instead of limiting to
(numbers completely arbitrary) 1000 frames on each of the 2 inputs of
each of the 4 instances of amerge (which would never be used, since when
all inputs on amerge have frames, amerge can process), limit to 8000
frames on the whole filter graph.

Second, keep stats on the approximate memory use of the frames, and set
a limit on that. That way, we can allow thousandths of 16-samples
frames, but bail after only a dozen 4k video frames.

Regards,
diff mbox

Patch

From 7006d17b617279f1fc0a35650869e552ca5eefcb Mon Sep 17 00:00:00 2001
From: Nicolas George <george@nsup.org>
Date: Thu, 22 Dec 2016 12:04:12 +0100
Subject: [PATCH] af_amerge: detect EOF immediately.

Fix an infinite loop in forward_status_change().

Signed-off-by: Nicolas George <george@nsup.org>
---
 libavfilter/af_amerge.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c
index 4a8c6d5bd0..40bf7ab209 100644
--- a/libavfilter/af_amerge.c
+++ b/libavfilter/af_amerge.c
@@ -23,6 +23,9 @@ 
  * Audio merging filter
  */
 
+#define FF_INTERNAL_FIELDS 1
+#include "framequeue.h"
+
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
@@ -182,7 +185,9 @@  static int request_frame(AVFilterLink *outlink)
     int i, ret;
 
     for (i = 0; i < s->nb_inputs; i++)
-        if (!s->in[i].nb_samples)
+        if (!s->in[i].nb_samples ||
+            /* detect EOF immediately */
+            (ctx->inputs[i]->status_in && !ctx->inputs[i]->status_out))
             if ((ret = ff_request_frame(ctx->inputs[i])) < 0)
                 return ret;
     return 0;
-- 
2.11.0