diff mbox series

[FFmpeg-devel,08/24] fftools/ffmpeg_filter: remove an unnecessary sub2video_push_ref() call

Message ID 20231104092125.10213-9-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,01/24] lavf/mux: do not apply max_interleave_delta to subtitles | expand

Commit Message

Anton Khirnov Nov. 4, 2023, 7:56 a.m. UTC
It only seems to produce duplicate frames.
---
 fftools/ffmpeg_filter.c               | 3 ---
 tests/ref/fate/sub2video_basic        | 3 ---
 tests/ref/fate/sub2video_time_limited | 1 -
 3 files changed, 7 deletions(-)

Comments

Nicolas George Nov. 4, 2023, 2:19 p.m. UTC | #1
Anton Khirnov (12023-11-04):
> It only seems to produce duplicate frames.

Yes it does, that is the point of it. Without the duplicated frame, a
filter with synchronized inputs (like overlay) will accumulate video
frames while waiting for the next sub2video frame.

It is actually super easy to test: any file with both video and bitmap
subtitles will cause a huge memory consumption if there is a long enough
time without subtitles.

Since I am feeling super helpful, here is how I just tested:

./ffmpeg_g -lavfi testsrc2=s=720x480:d=150 -preset ultrafast -y /tmp/dummy.mkv
mkvmerge -o /tmp/dummy_with_sub.mkv /tmp/dummy.mkv $ffmpeg_fate/sub/vobsub.idx
limit addressspace 2G
./ffmpeg_g -xerror -i /tmp/dummy_with_sub.mkv -preset ultrafast -lavfi '[0:v][0:s]overlay' -y /tmp/dummy_with_hardsub.mkv
mplayer /tmp/dummy_with_hardsub.mkv -ss 2:05

The limit addressspace 2G is there so that the huge memory consumption
will hit something. Another way is to log the number of queued frames:

--- a/libavfilter/framequeue.c
+++ b/libavfilter/framequeue.c
@@ -90,2 +90,3 @@ int ff_framequeue_add(FFFrameQueue *fq, AVFrame *frame)
     fq->queued++;
+    av_log(0, 16, "queued = %zd\n", fq->queued);
     fq->total_frames_head++;

ffmpeg version N-112710-g86e0dea620 Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 13 (Debian 13.2.0-5)
  configuration: --enable-shared --disable-static --enable-gpl --enable-libx264 --enable-libopus --enable-libass --enable-libfreetype --enable-opengl --assert-level=2
  libavutil      58. 31.100 / 58. 31.100
  libavcodec     60. 32.102 / 60. 32.102
  libavformat    60. 17.100 / 60. 17.100
  libavdevice    60.  4.100 / 60.  4.100
  libavfilter     9. 13.100 /  9. 13.100
  libswscale      7.  6.100 /  7.  6.100
  libswresample   4. 13.100 /  4. 13.100
  libpostproc    57.  4.100 / 57.  4.100
Input #0, matroska,webm, from '/tmp/dummy_with_sub.mkv':
[…]
queued = 2210
queued = 2211
queued = 2212
queued = 2213
[h264 @ 0x562874ac4f40] get_buffer() failed
[h264 @ 0x562874ac4f40] thread_get_buffer() failed
[h264 @ 0x562874ac4f40] decode_slice_header error
[h264 @ 0x562874ac4f40] no frame!
[…]
Anton Khirnov Nov. 9, 2023, 10:42 a.m. UTC | #2
Quoting Nicolas George (2023-11-04 15:19:00)
> Anton Khirnov (12023-11-04):
> > It only seems to produce duplicate frames.
> 
> Yes it does, that is the point of it. Without the duplicated frame, a
> filter with synchronized inputs (like overlay) will accumulate video
> frames while waiting for the next sub2video frame.

The problem with that code (and the reason this patch exists), is that
it depends on the order in which the frames arrive on filtergraph
inputs, which is not meaningful (and becomes non-deterministic with
threading).
Nicolas George Nov. 9, 2023, 10:47 a.m. UTC | #3
Anton Khirnov (12023-11-09):
> The problem with that code (and the reason this patch exists), is that
> it depends on the order in which the frames arrive on filtergraph
> inputs, which is not meaningful (and becomes non-deterministic with
> threading).

I can understand that. We will have to find a solution, won't we? (I
hope you do not consider removing a feature that people have been using
for years an option.)

I can suggest this: have demuxer code emit virtual subtitles packets to
trigger the sending of the heartbeat frames.

Regards,
Anton Khirnov Nov. 9, 2023, 9:29 p.m. UTC | #4
Quoting Nicolas George (2023-11-09 11:47:23)
> Anton Khirnov (12023-11-09):
> > The problem with that code (and the reason this patch exists), is that
> > it depends on the order in which the frames arrive on filtergraph
> > inputs, which is not meaningful (and becomes non-deterministic with
> > threading).
> 
> I can understand that. We will have to find a solution, won't we? (I
> hope you do not consider removing a feature that people have been using
> for years an option.)

I am obviously not proposing that, given the amount of patches I sent so
far to keep sub2video working.

> I can suggest this: have demuxer code emit virtual subtitles packets to
> trigger the sending of the heartbeat frames.

That's what already happens, unless I misunderstand what you mean.

Another possibility is to make the call independently of the state of
the graph, like this
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -2274,8 +2274,7 @@ void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational t
            or if we need to initialize the system, update the
            overlayed subpicture and its start/end times */
         sub2video_update(ifp, pts2 + 1, NULL);
-
-    if (av_buffersrc_get_nb_failed_requests(ifp->filter))
+    else
         sub2video_push_ref(ifp, pts2);
 }


This actually seems to improve filter-overlay-dvdsub-2397, where
the first subtitle appears two frames later, which more closely matches
the subtitles timestamp stored in the file.
Nicolas George Nov. 17, 2023, 9:44 a.m. UTC | #5
Anton Khirnov (12023-11-09):
> I am obviously not proposing that, given the amount of patches I sent so
> far to keep sub2video working.

Ok, sorry.

> 
> > I can suggest this: have demuxer code emit virtual subtitles packets to
> > trigger the sending of the heartbeat frames.
> 
> That's what already happens, unless I misunderstand what you mean.

Right now, the heartbeat code path is:

demuxer → heartbeat → filter

But the normal frame code path is:

demuxer → decode frame → filter

And it causes a problem because decode frame is in a different thread,
so there is a race condition between the heartbeat frames and the normal
frames.

Instead, if you make the demuxer generate a dummy packet instead of a
heartbeat frame, you get these code paths:

demuxer → real sub packet → decode frame → filter
demuxer → dummy hb packet → decode frame → filter

There is no race condition between the real sub packet and the dummy hb
packet because they both happen in the demuxer thread, and after that
they follow the same code path so there is no race condition either.

> 
> Another possibility is to make the call independently of the state of
> the graph, like this
> --- a/fftools/ffmpeg_filter.c
> +++ b/fftools/ffmpeg_filter.c
> @@ -2274,8 +2274,7 @@ void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational t
>             or if we need to initialize the system, update the
>             overlayed subpicture and its start/end times */
>          sub2video_update(ifp, pts2 + 1, NULL);
> -
> -    if (av_buffersrc_get_nb_failed_requests(ifp->filter))
> +    else
>          sub2video_push_ref(ifp, pts2);
>  }

Sending frames when they are not wanted can have bad consequences. I do
not have the time to look for a test case right now, but I have already
delayed replying to this mail too much.

I think a situation where subtitles are delayed with the setpts filter
would trigger a problem with this change: the heartbeat frames will
accumulate in the input of the overlay filter while without this patch
only the real subtitles frames accumulate. For a medium delay, normal
frames would be in the dozens while heartbeat frames would be in the
thousands.

> This actually seems to improve filter-overlay-dvdsub-2397, where
> the first subtitle appears two frames later, which more closely matches
> the subtitles timestamp stored in the file.

I would need to look into it. Does it improve it with your changes or
also with the precedent non-threaded code?

Regards,
Anton Khirnov Nov. 17, 2023, 11:52 a.m. UTC | #6
Quoting Nicolas George (2023-11-17 10:44:22)
> Anton Khirnov (12023-11-09):
> > I am obviously not proposing that, given the amount of patches I sent so
> > far to keep sub2video working.
> 
> Ok, sorry.
> 
> > 
> > > I can suggest this: have demuxer code emit virtual subtitles packets to
> > > trigger the sending of the heartbeat frames.
> > 
> > That's what already happens, unless I misunderstand what you mean.
> 
> Right now, the heartbeat code path is:
> 
> demuxer → heartbeat → filter
> 
> But the normal frame code path is:
> 
> demuxer → decode frame → filter
> 
> And it causes a problem because decode frame is in a different thread,
> so there is a race condition between the heartbeat frames and the normal
> frames.

There is no race in current master, because the decoding thread only
runs while the main thread is waiting, and vice versa. That changes
after this patchet of course, but...

> Instead, if you make the demuxer generate a dummy packet instead of a
> heartbeat frame, you get these code paths:
> 
> demuxer → real sub packet → decode frame → filter
> demuxer → dummy hb packet → decode frame → filter
> 
> There is no race condition between the real sub packet and the dummy hb
> packet because they both happen in the demuxer thread, and after that
> they follow the same code path so there is no race condition either.

...that is exactly what I'm doing in 19-20/24.

> > 
> > Another possibility is to make the call independently of the state of
> > the graph, like this
> > --- a/fftools/ffmpeg_filter.c
> > +++ b/fftools/ffmpeg_filter.c
> > @@ -2274,8 +2274,7 @@ void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational t
> >             or if we need to initialize the system, update the
> >             overlayed subpicture and its start/end times */
> >          sub2video_update(ifp, pts2 + 1, NULL);
> > -
> > -    if (av_buffersrc_get_nb_failed_requests(ifp->filter))
> > +    else
> >          sub2video_push_ref(ifp, pts2);
> >  }
> 
> Sending frames when they are not wanted can have bad consequences. I do
> not have the time to look for a test case right now, but I have already
> delayed replying to this mail too much.
> 
> I think a situation where subtitles are delayed with the setpts filter
> would trigger a problem with this change: the heartbeat frames will
> accumulate in the input of the overlay filter while without this patch
> only the real subtitles frames accumulate. For a medium delay, normal
> frames would be in the dozens while heartbeat frames would be in the
> thousands.

I do not hink there should be any excessive buffering, because the
scheduling code will simply stop reading from that input and only read
from the lagging one until it catches up.

> > This actually seems to improve filter-overlay-dvdsub-2397, where
> > the first subtitle appears two frames later, which more closely matches
> > the subtitles timestamp stored in the file.
> 
> I would need to look into it. Does it improve it with your changes or
> also with the precedent non-threaded code?

The improvement happens immediately when the change is made, i.e.
without threading. With threading it remains the same.
diff mbox series

Patch

diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index c738fc3397..d48974581b 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -2266,9 +2266,6 @@  void ifilter_sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational t
            or if we need to initialize the system, update the
            overlayed subpicture and its start/end times */
         sub2video_update(ifp, pts2 + 1, NULL);
-
-    if (av_buffersrc_get_nb_failed_requests(ifp->filter))
-        sub2video_push_ref(ifp, pts2);
 }
 
 int ifilter_sub2video(InputFilter *ifilter, const AVFrame *frame)
diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic
index a6eb1a34ea..314b7a07dd 100644
--- a/tests/ref/fate/sub2video_basic
+++ b/tests/ref/fate/sub2video_basic
@@ -12,7 +12,6 @@ 
 0,     183357,     183357,        0,  1382400, 0x00000000
 0,     183433,     183433,        0,  1382400, 0x85547fd1
 0,     185799,     185799,        0,  1382400, 0x00000000
-0,     185909,     185909,        0,  1382400, 0x00000000
 0,     185910,     185910,        0,  1382400, 0xb6a8f181
 0,     188606,     188606,        0,  1382400, 0x00000000
 0,     188663,     188663,        0,  1382400, 0xb64d1a2c
@@ -59,7 +58,6 @@ 
 0,     296776,     296776,        0,  1382400, 0x00000000
 0,     300049,     300049,        0,  1382400, 0xaf08b10d
 0,     301949,     301949,        0,  1382400, 0x00000000
-0,     302034,     302034,        0,  1382400, 0x00000000
 0,     302035,     302035,        0,  1382400, 0x853a9d93
 0,     303559,     303559,        0,  1382400, 0x00000000
 0,     304203,     304203,        0,  1382400, 0x7491a87d
@@ -76,7 +74,6 @@ 
 0,     326403,     326403,        0,  1382400, 0x00000000
 0,     327193,     327193,        0,  1382400, 0x35b85f2e
 0,     328285,     328285,        0,  1382400, 0x00000000
-0,     328360,     328360,        0,  1382400, 0x00000000
 0,     328361,     328361,        0,  1382400, 0x83f103e5
 0,     329885,     329885,        0,  1382400, 0x00000000
 0,     329946,     329946,        0,  1382400, 0xbc1ca9b3
diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited
index c7d48d639f..0634d5857e 100644
--- a/tests/ref/fate/sub2video_time_limited
+++ b/tests/ref/fate/sub2video_time_limited
@@ -5,4 +5,3 @@ 
 #sar 0: 0/1
 0,       6072,       6072,        0,  8294400, 0x00000000
 0,       6072,       6072,        0,  8294400, 0xa87c518f
-0,      36101,      36101,        0,  8294400, 0xa87c518f