[PATCH 01/12] avfilter: factorize requesting an input frame from multi output filters
Signed-off-by: Marton Balint <cus@passwd.hu> --- doc/filter_design.txt | 1 + libavfilter/af_acrossover.c | 10 +--------- libavfilter/af_channelsplit.c | 10 +--------- libavfilter/filters.h | 15 +++++++++++++++ libavfilter/split.c | 10 +--------- libavfilter/vf_extractplanes.c | 10 +--------- 6 files changed, 20 insertions(+), 36 deletions(-) diff --git a/doc/filter_design.txt b/doc/filter_design.txt index 885b19b6f6..b6bfca12f5 100644 --- a/doc/filter_design.txt +++ b/doc/filter_design.txt @@ -214,6 +214,7 @@ Frame scheduling FF_FILTER_FORWARD_STATUS(inlink, outlink); FF_FILTER_FORWARD_STATUS_ALL(inlink, filter); FF_FILTER_FORWARD_WANTED(outlink, inlink); + FF_FILTER_FORWARD_WANTED_ANY(filter, inlink); filter_frame ------------ diff --git a/libavfilter/af_acrossover.c b/libavfilter/af_acrossover.c index 2c9a1e00bc..e24bf9eb73 100644 --- a/libavfilter/af_acrossover.c +++ b/libavfilter/af_acrossover.c @@ -588,15 +588,7 @@ static int activate(AVFilterContext *ctx) return 0; } - for (int i = 0; i < ctx->nb_outputs; i++) { - if (ff_outlink_get_status(ctx->outputs[i])) - continue; - - if (ff_outlink_frame_wanted(ctx->outputs[i])) { - ff_inlink_request_frame(inlink); - return 0; - } - } + FF_FILTER_FORWARD_WANTED_ANY(ctx, inlink); return FFERROR_NOT_READY; } diff --git a/libavfilter/af_channelsplit.c b/libavfilter/af_channelsplit.c index 0156c0c28c..638e81593c 100644 --- a/libavfilter/af_channelsplit.c +++ b/libavfilter/af_channelsplit.c @@ -228,15 +228,7 @@ static int activate(AVFilterContext *ctx) return 0; } - for (int i = 0; i < ctx->nb_outputs; i++) { - if (ff_outlink_get_status(ctx->outputs[i])) - continue; - - if (ff_outlink_frame_wanted(ctx->outputs[i])) { - ff_inlink_request_frame(inlink); - return 0; - } - } + FF_FILTER_FORWARD_WANTED_ANY(ctx, inlink); return FFERROR_NOT_READY; } diff --git a/libavfilter/filters.h b/libavfilter/filters.h index 654aed5c9e..66dc611a98 100644 --- a/libavfilter/filters.h +++ b/libavfilter/filters.h @@ -696,6 +696,21 @@ static inline void ff_outlink_set_status(AVFilterLink *link, int status, int64_t } \ } while (0) +/** + * Forward the frame_wanted_out flag from any of the output links to an input link. + * If the flag is set on any of the outputs, this macro will return immediately. + */ +#define FF_FILTER_FORWARD_WANTED_ANY(filter, inlink) do { \ + for (int i = 0; i < filter->nb_outputs; i++) { \ + if (ff_outlink_get_status(filter->outputs[i])) \ + continue; \ + if (ff_outlink_frame_wanted(filter->outputs[i])) { \ + ff_inlink_request_frame(inlink); \ + return 0; \ + } \ + } \ +} while (0) + /** * Check for flow control between input and output. * This is necessary for filters that may produce several output frames for diff --git a/libavfilter/split.c b/libavfilter/split.c index 0557f54cce..d0bd0c3b2a 100644 --- a/libavfilter/split.c +++ b/libavfilter/split.c @@ -110,15 +110,7 @@ static int activate(AVFilterContext *ctx) return 0; } - for (int i = 0; i < ctx->nb_outputs; i++) { - if (ff_outlink_get_status(ctx->outputs[i])) - continue; - - if (ff_outlink_frame_wanted(ctx->outputs[i])) { - ff_inlink_request_frame(inlink); - return 0; - } - } + FF_FILTER_FORWARD_WANTED_ANY(ctx, inlink); return FFERROR_NOT_READY; } diff --git a/libavfilter/vf_extractplanes.c b/libavfilter/vf_extractplanes.c index 337ad12ac7..99e37ba04a 100644 --- a/libavfilter/vf_extractplanes.c +++ b/libavfilter/vf_extractplanes.c @@ -367,15 +367,7 @@ static int activate(AVFilterContext *ctx) return 0; } - for (int i = 0; i < ctx->nb_outputs; i++) { - if (ff_outlink_get_status(ctx->outputs[i])) - continue; - - if (ff_outlink_frame_wanted(ctx->outputs[i])) { - ff_inlink_request_frame(inlink); - return 0; - } - } + FF_FILTER_FORWARD_WANTED_ANY(ctx, inlink); return FFERROR_NOT_READY; } -- 2.43.0
The status check is unneeded because an outlink with a nonzero status should always return 0 for ff_outlink_frame_wanted(). Also use unsigned for index because nb_outputs is unsigned as well. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/filters.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libavfilter/filters.h b/libavfilter/filters.h index 66dc611a98..e3013eda65 100644 --- a/libavfilter/filters.h +++ b/libavfilter/filters.h @@ -701,9 +701,7 @@ static inline void ff_outlink_set_status(AVFilterLink *link, int status, int64_t * If the flag is set on any of the outputs, this macro will return immediately. */ #define FF_FILTER_FORWARD_WANTED_ANY(filter, inlink) do { \ - for (int i = 0; i < filter->nb_outputs; i++) { \ - if (ff_outlink_get_status(filter->outputs[i])) \ - continue; \ + for (unsigned i = 0; i < filter->nb_outputs; i++) { \ if (ff_outlink_frame_wanted(filter->outputs[i])) { \ ff_inlink_request_frame(inlink); \ return 0; \ -- 2.43.0
EOF only need to be forwarded back if all outputs have reached EOF. Fixes infinte loop with ffprobe -f lavfi -i "smptebars=d=1,select=n=2:e=1[out0][out1]" Regression since d9e41ead82263e96ebd14d4d88d6e7f858dd944c. Fixes ticket #10959. Fixes ticket #11366. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/avfilter.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 56f635a413..dd12533208 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -1253,16 +1253,14 @@ static int forward_status_change(AVFilterContext *filter, FilterLinkInternal *li static int filter_activate_default(AVFilterContext *filter) { unsigned i; + int nb_eofs = 0; - for (i = 0; i < filter->nb_outputs; i++) { - FilterLinkInternal *li = ff_link_internal(filter->outputs[i]); - int ret = li->status_in; - - if (ret) { - for (int j = 0; j < filter->nb_inputs; j++) - ff_inlink_set_status(filter->inputs[j], ret); - return 0; - } + for (i = 0; i < filter->nb_outputs; i++) + nb_eofs += ff_outlink_get_status(filter->outputs[i]) == AVERROR_EOF; + if (filter->nb_outputs && nb_eofs == filter->nb_outputs) { + for (int j = 0; j < filter->nb_inputs; j++) + ff_inlink_set_status(filter->inputs[j], AVERROR_EOF); + return 0; } for (i = 0; i < filter->nb_inputs; i++) { -- 2.43.0
Signed-off-by: Marton Balint <cus@passwd.hu> --- tests/fate-run.sh | 4 ++++ tests/fate/filter-video.mak | 3 +++ tests/ref/fate/filter-select-ffprobe | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 tests/ref/fate/filter-select-ffprobe diff --git a/tests/fate-run.sh b/tests/fate-run.sh index ff44c45757..efc8b3bc6a 100755 --- a/tests/fate-run.sh +++ b/tests/fate-run.sh @@ -123,6 +123,10 @@ probechapters(){ run ffprobe${PROGSUF}${EXECSUF} -bitexact -show_chapters "$@" } +probe(){ + run ffprobe${PROGSUF}${EXECSUF} -bitexact "$@" +} + probegaplessinfo(){ filename="$1" shift diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index df8a932750..33127387e5 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -325,6 +325,9 @@ FATE_FILTER_VSYNTH_PGMYUV-$(CONFIG_SELECT_FILTER) += fate-filter-select-alternat fate-filter-select-alternate: tests/data/filtergraphs/select-alternate fate-filter-select-alternate: CMD = framecrc -c:v pgmyuv -i $(SRC) -/filter $(TARGET_PATH)/tests/data/filtergraphs/select-alternate +FATE_FILTER-$(call ALLYES, FFPROBE SELECT_FILTER SMPTEBARS_FILTER LAVFI_INDEV) += fate-filter-select-ffprobe +fate-filter-select-ffprobe: CMD = probe -print_format compact -show_entries packet=stream_index,pts,pts_time -f lavfi "smptebars=d=1,select=n=2:e=1[out0][out1]" + FATE_FILTER_VSYNTH_PGMYUV-$(call ALLYES, SETPTS_FILTER SETTB_FILTER) += fate-filter-setpts fate-filter-setpts: tests/data/filtergraphs/setpts fate-filter-setpts: CMD = framecrc -c:v pgmyuv -i $(SRC) -/filter $(TARGET_PATH)/tests/data/filtergraphs/setpts diff --git a/tests/ref/fate/filter-select-ffprobe b/tests/ref/fate/filter-select-ffprobe new file mode 100644 index 0000000000..48ebf9b156 --- /dev/null +++ b/tests/ref/fate/filter-select-ffprobe @@ -0,0 +1,25 @@ +packet|stream_index=0|pts=0|pts_time=0.000000 +packet|stream_index=0|pts=1|pts_time=0.040000 +packet|stream_index=0|pts=2|pts_time=0.080000 +packet|stream_index=0|pts=3|pts_time=0.120000 +packet|stream_index=0|pts=4|pts_time=0.160000 +packet|stream_index=0|pts=5|pts_time=0.200000 +packet|stream_index=0|pts=6|pts_time=0.240000 +packet|stream_index=0|pts=7|pts_time=0.280000 +packet|stream_index=0|pts=8|pts_time=0.320000 +packet|stream_index=0|pts=9|pts_time=0.360000 +packet|stream_index=0|pts=10|pts_time=0.400000 +packet|stream_index=0|pts=11|pts_time=0.440000 +packet|stream_index=0|pts=12|pts_time=0.480000 +packet|stream_index=0|pts=13|pts_time=0.520000 +packet|stream_index=0|pts=14|pts_time=0.560000 +packet|stream_index=0|pts=15|pts_time=0.600000 +packet|stream_index=0|pts=16|pts_time=0.640000 +packet|stream_index=0|pts=17|pts_time=0.680000 +packet|stream_index=0|pts=18|pts_time=0.720000 +packet|stream_index=0|pts=19|pts_time=0.760000 +packet|stream_index=0|pts=20|pts_time=0.800000 +packet|stream_index=0|pts=21|pts_time=0.840000 +packet|stream_index=0|pts=22|pts_time=0.880000 +packet|stream_index=0|pts=23|pts_time=0.920000 +packet|stream_index=0|pts=24|pts_time=0.960000 -- 2.43.0
Even if all inputs are blocked an activate callback should request a frame on some if its inputs if a frame is requested on any of its outputs. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/avfilter.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index dd12533208..e03dc65fc6 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -1283,6 +1283,11 @@ static int filter_activate_default(AVFilterContext *filter) return request_frame_to_filter(filter->outputs[i]); } } + for (i = 0; i < filter->nb_outputs; i++) { + FilterLinkInternal * const li = ff_link_internal(filter->outputs[i]); + if (li->frame_wanted_out) + return request_frame_to_filter(filter->outputs[i]); + } return FFERROR_NOT_READY; } @@ -1416,6 +1421,12 @@ static int filter_activate_default(AVFilterContext *filter) 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). + + - If an output has frame_wanted_out > 0 call request_frame(). + + Rationale: even if all inputs are blocked an activate callback should + request a frame on some if its inputs if a frame is requested on any of + its output. */ int ff_filter_activate(AVFilterContext *filter) -- 2.43.0
Sinks without an activate callback have no means to request frames in their input, therefore the default activate callback should do it for them. Fixes ticket #11624. Fixes ticket #10988. Fixes ticket #10990. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/avfilter.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index e03dc65fc6..5bcf0b4ef7 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -1288,6 +1288,10 @@ static int filter_activate_default(AVFilterContext *filter) if (li->frame_wanted_out) return request_frame_to_filter(filter->outputs[i]); } + if (!filter->nb_outputs) { + ff_inlink_request_frame(filter->inputs[0]); + return 0; + } return FFERROR_NOT_READY; } @@ -1427,6 +1431,11 @@ static int filter_activate_default(AVFilterContext *filter) Rationale: even if all inputs are blocked an activate callback should request a frame on some if its inputs if a frame is requested on any of its output. + + - Request a frame on the input for sinks. + + Rationale: sinks using the old api have no way to request a frame on their + input, so we need to do it for them. */ int ff_filter_activate(AVFilterContext *filter) -- 2.43.0
Tests ticket #11624 with a slight modification. Signed-off-by: Marton Balint <cus@passwd.hu> --- tests/fate/filter-audio.mak | 3 +++ tests/ref/fate/filter-anullsink | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/ref/fate/filter-anullsink diff --git a/tests/fate/filter-audio.mak b/tests/fate/filter-audio.mak index 343fa40e9d..129122f408 100644 --- a/tests/fate/filter-audio.mak +++ b/tests/fate/filter-audio.mak @@ -421,6 +421,9 @@ fate-filter-atempo: REF = $(SAMPLES)/filter-reference/atempo.pcm fate-filter-aloop: CMD = framecrc -filter_complex "sine=r=48000:f=480:d=4,aloop=loop=4:start=48000:size=48000,asetnsamples=9600" FATE_AFILTER-$(call ALLYES, SINE_FILTER ALOOP_FILTER ASETNSAMPLES_FILTER PCM_S16LE_ENCODER FRAMECRC_MUXER PIPE_PROTOCOL) += fate-filter-aloop +fate-filter-anullsink: CMD = framecrc -f lavfi -i "sine=d=1" -af "aresample=22050,asplit[a][b];[a]anullsink;[b]atrim=end_sample=256" +FATE_AFILTER-$(call ALLYES, LAVFI_INDEV SINE_FILTER ARESAMPLE_FILTER ASPLIT_FILTER ANULLSINK_FILTER ATRIM_FILTER PCM_S16LE_ENCODER FRAMECRC_MUXER PIPE_PROTOCOL) += fate-filter-anullsink + fate-filter-crazychannels: tests/data/filtergraphs/crazychannels fate-filter-crazychannels: CMD = framecrc -auto_conversion_filters -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/crazychannels FATE_AFILTER-$(call FILTERFRAMECRC, ARESAMPLE SINE JOIN ATRIM CHANNELMAP CHANNELSPLIT) += fate-filter-crazychannels diff --git a/tests/ref/fate/filter-anullsink b/tests/ref/fate/filter-anullsink new file mode 100644 index 0000000000..f480a2fefc --- /dev/null +++ b/tests/ref/fate/filter-anullsink @@ -0,0 +1,6 @@ +#tb 0: 1/22050 +#media_type 0: audio +#codec_id 0: pcm_s16le +#sample_rate 0: 22050 +#channel_layout_name 0: mono +0, 0, 0, 256, 512, 0x270ff949 -- 2.43.0
No change in functionality. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/avfiltergraph.c | 2 ++ libavfilter/buffersink.c | 5 ++++- libavfilter/buffersrc.c | 4 ++-- libavfilter/filters.h | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 2d6036df74..38b89db22a 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -1460,6 +1460,8 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) frame_count = oldesti->l.frame_count_out; while (frame_count == oldesti->l.frame_count_out) { r = ff_filter_graph_run_once(graph); + if (r == FFERROR_BUFFERSRC_EMPTY) + r = 0; if (r == AVERROR(EAGAIN) && !oldesti->frame_wanted_out && !oldesti->frame_blocked_in && !oldesti->status_in) diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index 5cd47ba69f..fb33ad59c5 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -131,8 +131,11 @@ static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, i return AVERROR(EAGAIN); } else if (li->frame_wanted_out) { ret = ff_filter_graph_run_once(ctx->graph); - if (ret < 0) + if (ret == FFERROR_BUFFERSRC_EMPTY) { + // Do nothing for now... + } else if (ret < 0) { return ret; + } } else { ff_inlink_request_frame(inlink); } diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index af27306b3b..7e86d6ffd3 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -195,7 +195,7 @@ static int push_frame(AVFilterGraph *graph) ret = ff_filter_graph_run_once(graph); if (ret == AVERROR(EAGAIN)) break; - if (ret < 0) + if (ret < 0 && ret != FFERROR_BUFFERSRC_EMPTY) return ret; } return 0; @@ -552,7 +552,7 @@ static int activate(AVFilterContext *ctx) return 0; } c->nb_failed_requests++; - return FFERROR_NOT_READY; + return FFERROR_BUFFERSRC_EMPTY; } static const AVFilterPad avfilter_vsrc_buffer_outputs[] = { diff --git a/libavfilter/filters.h b/libavfilter/filters.h index e3013eda65..11152cfabf 100644 --- a/libavfilter/filters.h +++ b/libavfilter/filters.h @@ -31,6 +31,7 @@ * Special return code when activate() did not do anything. */ #define FFERROR_NOT_READY FFERRTAG('N','R','D','Y') +#define FFERROR_BUFFERSRC_EMPTY FFERRTAG('M','P','T','Y') /** * A filter pad used for either input or output. -- 2.43.0
A frame graph activation might not produce a frame in the requested sink, so keep on requesting a frame there unless we encounter a filter activation with buffersrc empty error. This makes av_buffersink_get_frame(_flags) work according to its documentation which claims that EAGAIN is only returned if additional frames must be inserted into the graph. Fate changes are because audio frames will have different sizes at segment boundaries, but content is the same. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/buffersink.c | 7 ++++++- tests/ref/fate/filter-asegment-samples-absolute | 6 ++++-- tests/ref/fate/filter-asegment-samples-relative | 6 ++++-- tests/ref/fate/filter-asegment-timestamps-absolute | 3 ++- tests/ref/fate/filter-asegment-timestamps-relative | 3 ++- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index fb33ad59c5..50a7da2756 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -113,6 +113,7 @@ static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, i int status, ret; AVFrame *cur_frame; int64_t pts; + int buffersrc_empty = 0; if (buf->peeked_frame) return return_or_keep_frame(buf, frame, buf->peeked_frame, flags); @@ -132,7 +133,11 @@ static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, int flags, i } else if (li->frame_wanted_out) { ret = ff_filter_graph_run_once(ctx->graph); if (ret == FFERROR_BUFFERSRC_EMPTY) { - // Do nothing for now... + buffersrc_empty = 1; + } else if (ret == AVERROR(EAGAIN)) { + if (buffersrc_empty) + return ret; + ff_inlink_request_frame(inlink); } else if (ret < 0) { return ret; } diff --git a/tests/ref/fate/filter-asegment-samples-absolute b/tests/ref/fate/filter-asegment-samples-absolute index 4090459aa7..ba9e9e269a 100644 --- a/tests/ref/fate/filter-asegment-samples-absolute +++ b/tests/ref/fate/filter-asegment-samples-absolute @@ -16,9 +16,11 @@ 0, 0, 0, 4096, 16384, 0x02ebe66b 0, 4096, 4096, 4096, 16384, 0x35bfe081 0, 8192, 8192, 1808, 7232, 0xa585202c -1, 10000, 10000, 6384, 25536, 0x17309ccf +1, 10000, 10000, 2288, 9152, 0x319ac07d +1, 12288, 12288, 4096, 16384, 0xd389dc43 1, 16384, 16384, 3616, 14464, 0x67e82600 -2, 20000, 20000, 4576, 18304, 0x4bc89a8b +2, 20000, 20000, 480, 1920, 0x24bfb749 +2, 20480, 20480, 4096, 16384, 0x378ee333 2, 24576, 24576, 4096, 16384, 0xabf6df0f 2, 28672, 28672, 4096, 16384, 0xedefe76f 2, 32768, 32768, 4096, 16384, 0x02ebe66b diff --git a/tests/ref/fate/filter-asegment-samples-relative b/tests/ref/fate/filter-asegment-samples-relative index 4090459aa7..ba9e9e269a 100644 --- a/tests/ref/fate/filter-asegment-samples-relative +++ b/tests/ref/fate/filter-asegment-samples-relative @@ -16,9 +16,11 @@ 0, 0, 0, 4096, 16384, 0x02ebe66b 0, 4096, 4096, 4096, 16384, 0x35bfe081 0, 8192, 8192, 1808, 7232, 0xa585202c -1, 10000, 10000, 6384, 25536, 0x17309ccf +1, 10000, 10000, 2288, 9152, 0x319ac07d +1, 12288, 12288, 4096, 16384, 0xd389dc43 1, 16384, 16384, 3616, 14464, 0x67e82600 -2, 20000, 20000, 4576, 18304, 0x4bc89a8b +2, 20000, 20000, 480, 1920, 0x24bfb749 +2, 20480, 20480, 4096, 16384, 0x378ee333 2, 24576, 24576, 4096, 16384, 0xabf6df0f 2, 28672, 28672, 4096, 16384, 0xedefe76f 2, 32768, 32768, 4096, 16384, 0x02ebe66b diff --git a/tests/ref/fate/filter-asegment-timestamps-absolute b/tests/ref/fate/filter-asegment-timestamps-absolute index b3f72c958a..5807194fc8 100644 --- a/tests/ref/fate/filter-asegment-timestamps-absolute +++ b/tests/ref/fate/filter-asegment-timestamps-absolute @@ -24,7 +24,8 @@ 0, 32768, 32768, 4096, 16384, 0x02ebe66b 0, 36864, 36864, 4096, 16384, 0x35bfe081 0, 40960, 40960, 3140, 12560, 0x13b5698a -1, 44100, 44100, 5052, 20208, 0xadc32273 +1, 44100, 44100, 956, 3824, 0xee464a2f +1, 45056, 45056, 4096, 16384, 0xe92bd835 1, 49152, 49152, 4096, 16384, 0x1126dca3 1, 53248, 53248, 4096, 16384, 0x9647edcf 1, 57344, 57344, 4096, 16384, 0x5cc345aa diff --git a/tests/ref/fate/filter-asegment-timestamps-relative b/tests/ref/fate/filter-asegment-timestamps-relative index b3f72c958a..5807194fc8 100644 --- a/tests/ref/fate/filter-asegment-timestamps-relative +++ b/tests/ref/fate/filter-asegment-timestamps-relative @@ -24,7 +24,8 @@ 0, 32768, 32768, 4096, 16384, 0x02ebe66b 0, 36864, 36864, 4096, 16384, 0x35bfe081 0, 40960, 40960, 3140, 12560, 0x13b5698a -1, 44100, 44100, 5052, 20208, 0xadc32273 +1, 44100, 44100, 956, 3824, 0xee464a2f +1, 45056, 45056, 4096, 16384, 0xe92bd835 1, 49152, 49152, 4096, 16384, 0x1126dca3 1, 53248, 53248, 4096, 16384, 0x9647edcf 1, 57344, 57344, 4096, 16384, 0x5cc345aa -- 2.43.0
Multi-input or multi-output filters should use activate now. Signed-off-by: Marton Balint <cus@passwd.hu> --- libavfilter/f_select.c | 45 ++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c index aa374c6ad0..a5609ec178 100644 --- a/libavfilter/f_select.c +++ b/libavfilter/f_select.c @@ -179,8 +179,6 @@ static const AVOption filt_name##_options[] = { \ { NULL } \ } -static int request_frame(AVFilterLink *outlink); - static av_cold int init(AVFilterContext *ctx) { SelectContext *select = ctx->priv; @@ -201,7 +199,6 @@ static av_cold int init(AVFilterContext *ctx) if (!pad.name) return AVERROR(ENOMEM); pad.type = ctx->filter->inputs[0].type; - pad.request_frame = request_frame; if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) return ret; } @@ -349,7 +346,7 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) if (isnan(select->var_values[VAR_START_T])) select->var_values[VAR_START_T] = TS2D(frame->pts) * av_q2d(inlink->time_base); - select->var_values[VAR_N ] = inl->frame_count_out; + select->var_values[VAR_N ] = inl->frame_count_out - 1; select->var_values[VAR_PTS] = TS2D(frame->pts); select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base); select->var_values[VAR_KEY] = !!(frame->flags & AV_FRAME_FLAG_KEY); @@ -428,24 +425,35 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) select->var_values[VAR_PREV_T] = select->var_values[VAR_T]; } -static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = inlink->dst; SelectContext *select = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFrame *in; + int nb_eofs = 0; + int ret; - select_frame(ctx, frame); - if (select->select) - return ff_filter_frame(ctx->outputs[select->select_out], frame); + for (int i = 0; i < ctx->nb_outputs; i++) + nb_eofs += ff_outlink_get_status(ctx->outputs[i]) == AVERROR_EOF; - av_frame_free(&frame); - return 0; -} + if (nb_eofs == ctx->nb_outputs) { + ff_inlink_set_status(inlink, AVERROR_EOF); + return 0; + } -static int request_frame(AVFilterLink *outlink) -{ - AVFilterLink *inlink = outlink->src->inputs[0]; - int ret = ff_request_frame(inlink); - return ret; + while ((ret = ff_inlink_consume_frame(inlink, &in))) { + if (ret < 0) + return ret; + select_frame(ctx, in); + if (select->select && !ff_outlink_get_status(ctx->outputs[select->select_out])) + return ff_filter_frame(ctx->outputs[select->select_out], in); + av_frame_free(&in); + }; + + FF_FILTER_FORWARD_STATUS_ALL(inlink, ctx); + FF_FILTER_FORWARD_WANTED_ANY(ctx, inlink); + + return FFERROR_NOT_READY; } static av_cold void uninit(AVFilterContext *ctx) @@ -486,7 +494,6 @@ static const AVFilterPad avfilter_af_aselect_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, }; @@ -542,7 +549,6 @@ static const AVFilterPad avfilter_vf_select_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_input, - .filter_frame = filter_frame, }, }; @@ -553,6 +559,7 @@ const FFFilter ff_vf_select = { .p.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY, .init = select_init, .uninit = uninit, + .activate = activate, .priv_size = sizeof(SelectContext), FILTER_INPUTS(avfilter_vf_select_inputs), FILTER_QUERY_FUNC2(query_formats), -- 2.43.0
alfilter_graph_request_oldest() might return EAGAIN and produce a frame on not the oldest sink. Fixes ticket #11597. Fixes excessive frame buffering in #10959. Fixes excessive frame buffering in #11366. Signed-off-by: Marton Balint <cus@passwd.hu> --- fftools/ffmpeg_filter.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index a0dc4c745e..f6e496158c 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -2669,6 +2669,22 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, while (fgp->nb_outputs_done < fg->nb_outputs) { int ret; + /* Reap all buffers present in the buffer sinks */ + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]); + + ret = 0; + while (!ret) { + ret = fg_output_step(ofp, fgt, frame); + if (ret < 0) + return ret; + } + } + + // return after one iteration, so that scheduler can rate-control us + if (did_step && fgp->have_sources) + return 0; + ret = avfilter_graph_request_oldest(fgt->graph); if (ret == AVERROR(EAGAIN)) { fgt->next_in = choose_input(fg, fgt); @@ -2684,21 +2700,6 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, } fgt->next_in = fg->nb_inputs; - // return after one iteration, so that scheduler can rate-control us - if (did_step && fgp->have_sources) - return 0; - - /* Reap all buffers present in the buffer sinks */ - for (int i = 0; i < fg->nb_outputs; i++) { - OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]); - - ret = 0; - while (!ret) { - ret = fg_output_step(ofp, fgt, frame); - if (ret < 0) - return ret; - } - } did_step = 1; } -- 2.43.0
It was never reliable to detect if a filtergraph have sources, because a filter can act as a source only after some time, for example the loop filter. So it is better to remove the source detection entirely and always give the scheduler an oppurtunity to stop processing. Fixes ticket #11604. Signed-off-by: Marton Balint <cus@passwd.hu> --- fftools/ffmpeg_filter.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index f6e496158c..2822335e15 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -53,8 +53,6 @@ typedef struct FilterGraphPriv { // true when the filtergraph contains only meta filters // that do not modify the frame data int is_meta; - // source filters are present in the graph - int have_sources; int disable_conversions; unsigned nb_outputs_done; @@ -1112,16 +1110,6 @@ int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) if (ret < 0) goto fail; - for (unsigned i = 0; i < graph->nb_filters; i++) { - const AVFilter *f = graph->filters[i]->filter; - if ((!avfilter_filter_pad_count(f, 0) && - !(f->flags & AVFILTER_FLAG_DYNAMIC_INPUTS)) || - !strcmp(f->name, "apad")) { - fgp->have_sources = 1; - break; - } - } - for (AVFilterInOut *cur = inputs; cur; cur = cur->next) { InputFilter *const ifilter = ifilter_alloc(fg); @@ -1648,10 +1636,8 @@ static int configure_output_audio_filter(FilterGraphPriv *fgp, AVFilterGraph *gr pad_idx = 0; } - if (ofilter->apad) { + if (ofilter->apad) AUTO_INSERT_FILTER("-apad", "apad", ofilter->apad); - fgp->have_sources = 1; - } snprintf(name, sizeof(name), "trim for output %s", ofilter->output_name); ret = insert_trim(fgp, ofp->trim_start_us, ofp->trim_duration_us, @@ -2647,7 +2633,6 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, AVFrame *frame) { FilterGraphPriv *fgp = fgp_from_fg(fg); - int did_step = 0; // graph not configured, just select the input to request if (!fgt->graph) { @@ -2666,7 +2651,7 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, return AVERROR_BUG; } - while (fgp->nb_outputs_done < fg->nb_outputs) { + if (fgp->nb_outputs_done < fg->nb_outputs) { int ret; /* Reap all buffers present in the buffer sinks */ @@ -2681,9 +2666,6 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, } } - // return after one iteration, so that scheduler can rate-control us - if (did_step && fgp->have_sources) - return 0; ret = avfilter_graph_request_oldest(fgt->graph); if (ret == AVERROR(EAGAIN)) { @@ -2700,7 +2682,8 @@ static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, } fgt->next_in = fg->nb_inputs; - did_step = 1; + // return so that scheduler can rate-control us + return 0; } return AVERROR_EOF; -- 2.43.0
On Tue, Jun 24, 2025 at 09:23:10PM +0200, Marton Balint wrote:
It was never reliable to detect if a filtergraph have sources, because a filter can act as a source only after some time, for example the loop filter.
So it is better to remove the source detection entirely and always give the scheduler an oppurtunity to stop processing.
Fixes ticket #11604.
Signed-off-by: Marton Balint <cus@passwd.hu> --- fftools/ffmpeg_filter.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-)
breaks: (infinite loop) ./ffmpeg -y -i tickets//507/avdesync_cut.avi -bitexact -af aresample=first_pts=-50000 -r 24000/1001 -t 145 out.avi sample seems attached to the ticket: https://trac.ffmpeg.org/ticket/507 thx [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB I am the wisest man alive, for I know one thing, and that is that I know nothing. -- Socrates
On Wed, 25 Jun 2025, Michael Niedermayer wrote:
On Tue, Jun 24, 2025 at 09:23:10PM +0200, Marton Balint wrote:
It was never reliable to detect if a filtergraph have sources, because a filter can act as a source only after some time, for example the loop filter.
So it is better to remove the source detection entirely and always give the scheduler an oppurtunity to stop processing.
Fixes ticket #11604.
Signed-off-by: Marton Balint <cus@passwd.hu> --- fftools/ffmpeg_filter.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-)
breaks: (infinite loop)
./ffmpeg -y -i tickets//507/avdesync_cut.avi -bitexact -af aresample=first_pts=-50000 -r 24000/1001 -t 145 out.avi
sample seems attached to the ticket: https://trac.ffmpeg.org/ticket/507
Seems like an issue already present before the patch series if "sources" were used, because this hangs for me even without the patch series: ffmpeg -threads 16 -i avdesync_cut.avi -bitexact -af aresample=first_pts=100,apad=pad_len=1000 -f null none I have no better idea than to drop this patch until this is figured out, because this issue would become more common if all filters are going to be treated as sources... Regards, Marton
On Thu, 26 Jun 2025, Marton Balint wrote:
On Wed, 25 Jun 2025, Michael Niedermayer wrote:
On Tue, Jun 24, 2025 at 09:23:10PM +0200, Marton Balint wrote:
It was never reliable to detect if a filtergraph have sources, because a filter can act as a source only after some time, for example the loop filter.
So it is better to remove the source detection entirely and always give the scheduler an oppurtunity to stop processing.
Fixes ticket #11604.
Signed-off-by: Marton Balint <cus@passwd.hu> --- fftools/ffmpeg_filter.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-)
breaks: (infinite loop)
./ffmpeg -y -i tickets//507/avdesync_cut.avi -bitexact -af aresample=first_pts=-50000 -r 24000/1001 -t 145 out.avi
sample seems attached to the ticket: https://trac.ffmpeg.org/ticket/507
Seems like an issue already present before the patch series if "sources" were used, because this hangs for me even without the patch series:
ffmpeg -threads 16 -i avdesync_cut.avi -bitexact -af aresample=first_pts=100,apad=pad_len=1000 -f null none
I have no better idea than to drop this patch until this is figured out, because this issue would become more common if all filters are going to be treated as sources...
Will apply the series except this last patch.... Regards, Marton
Marton Balint (HE12025-06-24):
Signed-off-by: Marton Balint <cus@passwd.hu> --- doc/filter_design.txt | 1 + libavfilter/af_acrossover.c | 10 +--------- libavfilter/af_channelsplit.c | 10 +--------- libavfilter/filters.h | 15 +++++++++++++++ libavfilter/split.c | 10 +--------- libavfilter/vf_extractplanes.c | 10 +--------- 6 files changed, 20 insertions(+), 36 deletions(-)
Looks useful, thanks. Regards, -- Nicolas George
participants (3)
-
Marton Balint -
Michael Niedermayer -
Nicolas George