From: Anton Khirnov <anton@khirnov.net> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 30/31] fftools/ffmpeg_filter: implement filtergraph chaining Date: Fri, 5 Apr 2024 18:12:11 +0200 Message-ID: <20240405161212.26167-30-anton@khirnov.net> (raw) In-Reply-To: <20240405161212.26167-1-anton@khirnov.net> This allows one complex filtergraph's output to be sent as input to another one, which is useful in certain situations (one is described in the docs). Chaining filtergraphs was already effectively possible by using a wrapped_avframe encoder connected to a loopback decoder, but it is ugly, non-obvious and inefficient. --- Changelog | 1 + doc/ffmpeg.texi | 58 ++++++++++++++++++++++++--- fftools/ffmpeg_filter.c | 89 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 8 deletions(-) diff --git a/Changelog b/Changelog index 18e83b99a1..b7a1af4083 100644 --- a/Changelog +++ b/Changelog @@ -4,6 +4,7 @@ releases are sorted from youngest to oldest. version <next>: - Raw Captions with Time (RCWT) closed caption demuxer - LC3/LC3plus decoding/encoding using external library liblc3 +- ffmpeg CLI filtergraph chaining version 7.0: diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 801c083705..9bd548ce4e 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -2145,14 +2145,62 @@ type -- see the @option{-filter} options. @var{filtergraph} is a description of the filtergraph, as described in the ``Filtergraph syntax'' section of the ffmpeg-filters manual. -Input link labels must refer to either input streams or loopback decoders. For -input streams, use the @code{[file_index:stream_specifier]} syntax (i.e. the -same as @option{-map} uses). If @var{stream_specifier} matches multiple streams, -the first one will be used. +Inputs to a complex filtergraph may come from different source types, +distinguished by the format of the corresponding link label: +@itemize +@item +To connect an input stream, use @code{[file_index:stream_specifier]} (i.e. the +same syntax as @option{-map}). If @var{stream_specifier} matches multiple +streams, the first one will be used. -For decoders, the link label must be [dec:@var{dec_idx}], where @var{dec_idx} is +@item +To connect a loopback decoder use [dec:@var{dec_idx}], where @var{dec_idx} is the index of the loopback decoder to be connected to given input. +@item +To connect an output from another complex filtergraph, use its link label. E.g +the following example: + +@example +ffmpeg -i input.mkv \ + -filter_complex '[0:v]scale=size=hd1080,split=outputs=2[for_enc][orig_scaled]' \ + -c:v libx264 -map '[for_enc]' output.mkv \ + -dec 0:0 \ + -filter_complex '[dec:0][orig_scaled]hstack[stacked]' \ + -map '[stacked]' -c:v ffv1 comparison.mkv +@end example + +reads an input video and +@itemize +@item +(line 2) uses a complex filtergraph with one input and two outputs +to scale the video to 1920x1080 and duplicate the result to both +outputs; + +@item +(line 3) encodes one scaled output with @code{libx264} and writes the result to +@file{output.mkv}; + +@item +(line 4) decodes this encoded stream with a loopback decoder; + +@item +(line 5) places the output of the loopback decoder (i.e. the +@code{libx264}-encoded video) side by side with the scaled original input; + +@item +(line 6) combined video is then losslessly encoded and written into +@file{comparison.mkv}. + +@end itemize + +Note that the two filtergraphs cannot be combined into one, because then there +would be a cycle in the transcoding pipeline (filtergraph output goes to +encoding, from there to decoding, then back to the same graph), and such cycles +are not allowed. + +@end itemize + An unlabeled input will be connected to the first unused input stream of the matching type. diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 1e14962f41..f108f8daf9 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -902,6 +902,63 @@ int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost, return 0; } +static int ofilter_bind_ifilter(OutputFilter *ofilter, InputFilterPriv *ifp, + const OutputFilterOptions *opts) +{ + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + + av_assert0(!ofilter->bound); + av_assert0(ofilter->type == ifp->type); + + ofilter->bound = 1; + av_freep(&ofilter->linklabel); + + ofp->name = av_strdup(opts->name); + if (!ofp->name) + return AVERROR(EINVAL); + + av_strlcatf(ofp->log_name, sizeof(ofp->log_name), "->%s", ofp->name); + + return 0; +} + +static int ifilter_bind_fg(InputFilterPriv *ifp, FilterGraph *fg_src, int out_idx) +{ + FilterGraphPriv *fgp = fgp_from_fg(ifp->ifilter.graph); + OutputFilter *ofilter_src = fg_src->outputs[out_idx]; + OutputFilterOptions opts; + char name[32]; + int ret; + + av_assert0(!ifp->bound); + ifp->bound = 1; + + if (ifp->type != ofilter_src->type) { + av_log(fgp, AV_LOG_ERROR, "Tried to connect %s output to %s input\n", + av_get_media_type_string(ofilter_src->type), + av_get_media_type_string(ifp->type)); + return AVERROR(EINVAL); + } + + ifp->type_src = ifp->type; + + memset(&opts, 0, sizeof(opts)); + + snprintf(name, sizeof(name), "fg:%d:%d", fgp->fg.index, ifp->index); + opts.name = name; + + ret = ofilter_bind_ifilter(ofilter_src, ifp, &opts); + if (ret < 0) + return ret; + + ret = sch_connect(fgp->sch, SCH_FILTER_OUT(fg_src->index, out_idx), + SCH_FILTER_IN(fgp->sch_idx, ifp->index)); + if (ret < 0) + return ret; + + return 0; +} + static InputFilter *ifilter_alloc(FilterGraph *fg) { InputFilterPriv *ifp; @@ -1213,12 +1270,38 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) ifilter->name); return ret; } else if (ifp->linklabel) { - // bind to an explicitly specified demuxer stream AVFormatContext *s; AVStream *st = NULL; char *p; - int file_idx = strtol(ifp->linklabel, &p, 0); + int file_idx; + // try finding an unbound filtergraph output with this label + for (int i = 0; i < nb_filtergraphs; i++) { + FilterGraph *fg_src = filtergraphs[i]; + + if (fg == fg_src) + continue; + + for (int j = 0; j < fg_src->nb_outputs; j++) { + OutputFilter *ofilter = fg_src->outputs[j]; + + if (!ofilter->bound && ofilter->linklabel && + !strcmp(ofilter->linklabel, ifp->linklabel)) { + av_log(fg, AV_LOG_VERBOSE, + "Binding input with label '%s' to filtergraph output %d:%d\n", + ifp->linklabel, i, j); + + ret = ifilter_bind_fg(ifp, fg_src, j); + if (ret < 0) + av_log(fg, AV_LOG_ERROR, "Error binding filtergraph input %s\n", + ifp->linklabel); + return ret; + } + } + } + + // bind to an explicitly specified demuxer stream + file_idx = strtol(ifp->linklabel, &p, 0); if (file_idx < 0 || file_idx >= nb_input_files) { av_log(fg, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", file_idx, fgp->graph_desc); @@ -1274,7 +1357,7 @@ static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) static int bind_inputs(FilterGraph *fg) { - // bind filtergraph inputs to input streams + // bind filtergraph inputs to input streams or other filtergraphs for (int i = 0; i < fg->nb_inputs; i++) { InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); int ret; -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next prev parent reply other threads:[~2024-04-05 16:20 UTC|newest] Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-04-05 16:11 [FFmpeg-devel] [PATCH 01/31] lavfi/vf_scale: fix AVOption flags for "size"/"s" Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 02/31] lavfi/avfilter: add an "auto" constant to the threads option Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 03/31] fftools/ffmpeg_filter: do not pass OutputStream to set_channel_layout() Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 04/31] fftools/ffmpeg_filter: stop accessing AVCodecContext.codec Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 05/31] fftools/ffmpeg_filter: check that filter type matches output stream type Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 06/31] fftools/ffmpeg_filter: pass ts offset through OutputFilterOptions Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 07/31] fftools/ffmpeg_filter: pass keep_pix_fmt " Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 08/31] fftools/ffmpeg: warn about ignored -enc_time_base for subtitles earlier Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 09/31] fftools/ffmpeg_filter: pass enc_timebase through OutputFilterOptions Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 10/31] fftools/ffmpeg_filter: move the MJPEG format selection hack to muxer setup Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 11/31] fftools/ffmpeg_filter: stop accessing encoder AVCodecContext Anton Khirnov 2024-04-05 16:50 ` Dennis Mungai 2024-04-05 16:54 ` Gyan Doshi 2024-04-05 17:07 ` Dennis Mungai 2024-04-05 17:09 ` Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 12/31] fftools/ffmpeg_filter: pass vsync method through OutputFilterOptions Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 13/31] fftools/ffmpeg: drop OutputStream.is_cfr Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 14/31] fftools/ffmpeg_filter: accept a caller-provided output name Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 15/31] fftools/ffmpeg_filter: drop a redundant check Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 16/31] fftools/ffmpeg_filter: simplify retrieving filter type Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 17/31] fftools/ffmpeg_filter: add an AVClass to OutputFilter Anton Khirnov 2024-04-05 16:11 ` [FFmpeg-devel] [PATCH 18/31] fftools/ffmpeg_filter: drop an unnecessary use of OutputStream Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 19/31] fftools/ffmpeg_filter: pass sws/swr opts through OutputFilterOptions Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 20/31] fftools/ffmpeg_filter: pass autoscale " Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 21/31] fftools/ffmpeg_filter: pass trim parameters " Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 22/31] fftools/ffmpeg_filter: move most of -apad logic to the muxer Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 23/31] fftools/ffmpeg_mux: drop OutputFile.shortest Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 24/31] fftools/ffmpeg_mux: drop OutputFile.format Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 25/31] fftools/ffmpeg_filter: accept encoder thread count through OutputFilterOptions Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 26/31] fftools/ffmpeg_filter: drop OutputFilter.ost Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 27/31] fftools/ffmpeg_filter: only store complex filtergraphs in global array Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 28/31] fftools/ffmpeg_filter: change processing order in fg_finalise_bindings() Anton Khirnov 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 29/31] fftools/ffmpeg_sched: allow filtergraphs to send to filtergraphs Anton Khirnov 2024-04-05 16:12 ` Anton Khirnov [this message] 2024-04-05 16:12 ` [FFmpeg-devel] [PATCH 31/31] doc/ffmpeg: document that there can be multiple complex filtergraphs Anton Khirnov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20240405161212.26167-30-anton@khirnov.net \ --to=anton@khirnov.net \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git