PR #24546 opened by golgiwaffles URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24546 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24546.patch
Fixes ticket #7361. # Summary of changes When the input duration rounds to zero in the output timebase, the rounded EOF timestamp can equal next_pts before any frame has been output. In that case, preserve a buffered frame whose timestamp matches next_pts instead of dropping it. This preserves the existing eof_action rounding semantics while preventing a valid first output frame from being dropped. Adds a self-contained FATE regression test covering a short single-frame input. >From 7f41a43814e1e2f63a0294477c537de6f6aeb4d0 Mon Sep 17 00:00:00 2001 From: Skanda Vyas <[email protected]> Date: Wed, 16 Sep 2026 19:15:04 -0700 Subject: [PATCH] avfilter/vf_fps: preserve first output frame at EOF When the input duration rounds to zero in the output timebase, the rounded EOF timestamp can equal next_pts before any frame has been output. In that case, do not discard a buffered frame whose timestamp matches next_pts. This preserves the existing eof_action rounding semantics while preventing a valid first output frame from being dropped. Add a FATE regression test covering a short single-frame input. Fixes ticket #7361. Signed-off-by: Skanda Vyas <[email protected]> --- libavfilter/vf_fps.c | 12 ++++++++++-- tests/fate/filter-video.mak | 3 ++- tests/ref/fate/filter-fps-single | 6 ++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 tests/ref/fate/filter-fps-single diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index 0257a8ef78..2b116ed1c2 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -264,6 +264,8 @@ static int read_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *inlink, static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlink, int *again) { AVFrame *frame; + int eof_before_or_at_next; + int first_output_due; av_assert1(s->frames_count == 2 || (s->status && s->frames_count == 1)); @@ -282,13 +284,19 @@ static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlin } } + eof_before_or_at_next = s->status && s->status_pts <= s->next_pts; + first_output_due = !s->frames_out && !s->cur_frame_out && + s->frames[0]->pts == s->next_pts && + s->status_pts == s->next_pts; + /* There are two conditions where we want to drop a frame: * - If we have two buffered frames and the second frame is acceptable * as the next output frame, then drop the first buffered frame. * - If we have status (EOF) set, drop frames when we hit the - * status timestamp. */ + * status timestamp, unless no frame has been output yet and the + * buffered frame is due at the next output timestamp. */ if ((s->frames_count == 2 && s->frames[1]->pts <= s->next_pts) || - (s->status && s->status_pts <= s->next_pts)) { + (eof_before_or_at_next && !first_output_due)) { frame = shift_frame(ctx, s); av_frame_free(&frame); diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index b0284c9813..23b8a9f9eb 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -490,7 +490,7 @@ fate-filter-mpdecimate-mode-1: CMD = framecrc -lavfi testsrc2=r=4:d=5,fps=5,mpde FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 FPS MPDECIMATE) += fate-filter-mpdecimate-mode-1-min-3 fate-filter-mpdecimate-mode-1-min-3: CMD = framecrc -lavfi testsrc2=r=2:d=7,fps=7,mpdecimate=mode=1:min=3 -pix_fmt yuv420p -FATE_FILTER-$(call FILTERFRAMECRC, FPS TESTSRC2) += $(addprefix fate-filter-fps-, up up-round-down up-round-up down down-round-down down-round-up down-eof-pass start-drop start-fill) +FATE_FILTER-$(call FILTERFRAMECRC, FPS TESTSRC2) += $(addprefix fate-filter-fps-, up up-round-down up-round-up down down-round-down down-round-up down-eof-pass start-drop start-fill single) fate-filter-fps-up: CMD = framecrc -lavfi testsrc2=r=3:d=2,fps=7 fate-filter-fps-up-round-down: CMD = framecrc -lavfi testsrc2=r=3:d=2,fps=7:round=down fate-filter-fps-up-round-up: CMD = framecrc -lavfi testsrc2=r=3:d=2,fps=7:round=up @@ -500,6 +500,7 @@ fate-filter-fps-down-round-up: CMD = framecrc -lavfi testsrc2=r=7:d=3.5,fps=3:ro fate-filter-fps-down-eof-pass: CMD = framecrc -lavfi testsrc2=r=7:d=3.5,fps=3:eof_action=pass fate-filter-fps-start-drop: CMD = framecrc -lavfi testsrc2=r=7:d=3.5,fps=3:start_time=1.5 fate-filter-fps-start-fill: CMD = framecrc -lavfi testsrc2=r=7:d=1.5,setpts=PTS+14,fps=3:start_time=1.5 +fate-filter-fps-single: CMD = framecrc -lavfi testsrc2=s=16x16:r=25:d=0.04,fps=1 DRAWVG_SCRIPT_ALL = $(SRC_PATH)/tests/ref/lavf/drawvg.all diff --git a/tests/ref/fate/filter-fps-single b/tests/ref/fate/filter-fps-single new file mode 100644 index 0000000000..d315556861 --- /dev/null +++ b/tests/ref/fate/filter-fps-single @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 16x16 +#sar 0: 1/1 +0, 0, 0, 1, 384, 0x92e591ca -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
