Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Niklas Haas <ffmpeg@haasn.xyz>
To: ffmpeg-devel@ffmpeg.org
Cc: Niklas Haas <git@haasn.dev>
Subject: [FFmpeg-devel] [PATCH v2 3/4] avfilter/graphdump: add complex format
Date: Tue, 18 Feb 2025 13:46:02 +0100
Message-ID: <20250218124603.95398-3-ffmpeg@haasn.xyz> (raw)
In-Reply-To: <20250218124603.95398-1-ffmpeg@haasn.xyz>

From: Niklas Haas <git@haasn.dev>

As an example, the following filter graph (taken from FATE):

  sws_flags=+accurate_rnd+bitexact;
  split [main][over];
  [over] scale=88:72, pad=96:80:4:4 [overf];
  [main][overf] overlay=240:16:format=yuv422

Results in this output:

Filter graph:
  [L0] split=thread_type=0x00000000 [L1] [over];
  [over] scale=w=88:width=88:h=72:height=72:flags=+accurate_rnd+bitexact:thread_type=0x00000000 [L3];
  [L3] pad=width=96:w=96:height=80:h=80:x=4:y=4:thread_type=0x00000000 [overf];
  [main] [overf] overlay=x=240:y=16:format=2 [L6];
  buffer=width=352:video_size=352x288:height=288:pix_fmt=yuv420p:time_base=1/25:frame_rate=25/1:range=1:thread_type=0x00000000 [L0];
  [L6] buffersink=pixel_formats=:colorspaces=:colorranges=:thread_type=0x00000000;
  [L1] scale=w=iw:width=iw:h=ih:height=ih:flags=+accurate_rnd+bitexact:thread_type=0x00000000 [main];
Filter links:
  [L0: buffer -> split] yuv420p 352x288 [SAR 0:1] csp:unknown range:tv
  [L1: split -> scale] yuv420p 352x288 [SAR 0:1] csp:unknown range:tv
  [over: split -> scale] yuv420p 352x288 [SAR 0:1] csp:unknown range:tv
  [L3: scale -> pad] yuva422p 88x72 [SAR 0:1] csp:unknown range:tv
  [overf: pad -> overlay] yuva422p 96x80 [SAR 0:1] csp:unknown range:tv
  [main: scale -> overlay] yuva422p 352x288 [SAR 0:1] csp:unknown range:tv
  [L6: overlay -> buffersink] yuva422p 352x288 [SAR 0:1] csp:unknown range:tv

This format is intended to be more useful for logging, post-processing or
otherwise reusing the dumpled filter graph, since it can be directly ingested
again by `-filter_complex`.
---
 libavfilter/graphdump.c | 124 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/libavfilter/graphdump.c b/libavfilter/graphdump.c
index 80cbda50f5..e81ea0c001 100644
--- a/libavfilter/graphdump.c
+++ b/libavfilter/graphdump.c
@@ -32,6 +32,7 @@
 enum {
     FMT_NONE = 0,
     FMT_PRETTY,
+    FMT_COMPLEX,
     FMT_NB,
 };
 
@@ -46,6 +47,7 @@ static const AVOption graph_dump_options[] = {
     { "format",     "set the output format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = FMT_NONE }, 0, FMT_NB - 1, .unit = "format" },
     {     "none",   "don't produce any output",         0, AV_OPT_TYPE_CONST, {.i64 = FMT_NONE},    .unit = "format" },
     {     "pretty", "pretty printed ASCII art graph",   0, AV_OPT_TYPE_CONST, {.i64 = FMT_PRETTY},  .unit = "format" },
+    {     "complex","complex filter graph",             0, AV_OPT_TYPE_CONST, {.i64 = FMT_COMPLEX}, .unit = "format" },
     { NULL },
 };
 
@@ -176,6 +178,121 @@ static void dump_pretty(AVBPrint *buf, AVFilterGraph *graph)
     }
 }
 
+/* Assign a unique ID to each link by keeping track of them in an array */
+static int get_link_id(AVFilterLink ***links, int *nb_links, AVFilterLink *link)
+{
+    int ret;
+    for (int i = 0; i < *nb_links; i++) {
+        if ((*links)[i] == link)
+            return i;
+    }
+
+    ret = av_dynarray_add_nofree(links, nb_links, link);
+    return ret ? ret : *nb_links - 1;
+}
+
+static const char *get_filter_name(const AVFilterContext *filter)
+{
+    /* Reuse the filter instance name if present */
+    return strchr(filter->name, '@') ? filter->name : filter->filter->name;
+}
+
+static void print_link_label(AVBPrint *buf, AVFilterLink *link, int id)
+{
+    if (link->srcpad->label)
+        av_bprintf(buf, "%s", link->srcpad->label);
+    else if (link->dstpad->label)
+        av_bprintf(buf, "%s", link->dstpad->label);
+    else
+        av_bprintf(buf, "L%d", id);
+}
+
+static int dump_complex(AVBPrint *buf, AVFilterGraph *graph)
+{
+    /* Keep track of seen filter links to assign a unique ID to each */
+    AVFilterLink **links = NULL;
+    int nb_links = 0;
+    int ret = AVERROR(ENOMEM);
+    char *filter_opts = NULL;
+
+    for (int i = 0; i < graph->nb_filters; i++) {
+        AVFilterContext *filter = graph->filters[i];
+        if (i == 0)
+            av_bprintf(buf, "Filter graph:\n");
+
+        ret = av_opt_serialize(filter, AV_OPT_FLAG_FILTERING_PARAM,
+                               AV_OPT_SERIALIZE_SKIP_DEFAULTS |
+                               AV_OPT_SERIALIZE_SEARCH_CHILDREN,
+                               &filter_opts, '=', ':');
+        if (ret < 0)
+            goto fail;
+
+        av_bprintf(buf, "  ");
+        for (int j = 0; j < filter->nb_inputs; j++) {
+            AVFilterLink *link = filter->inputs[j];
+            ret = get_link_id(&links, &nb_links, link);
+            if (ret < 0)
+                goto fail;
+            av_bprintf(buf, "[");
+            print_link_label(buf, link, ret);
+            av_bprintf(buf, "] ");
+        }
+
+        av_bprintf(buf, "%s", get_filter_name(filter));
+        if (filter_opts && filter_opts[0])
+            av_bprintf(buf, "=%s", filter_opts);
+        av_freep(&filter_opts);
+
+        for (int j = 0; j < filter->nb_outputs; j++) {
+            AVFilterLink *link = filter->outputs[j];
+            ret = get_link_id(&links, &nb_links, link);
+            if (ret < 0)
+                goto fail;
+            av_bprintf(buf, " [");
+            print_link_label(buf, link, ret);
+            av_bprintf(buf, "]");
+        }
+        av_bprintf(buf, ";\n");
+    }
+
+    /* Dump a summary of all seen links */
+    for (int i = 0; i < nb_links; i++) {
+        AVFilterLink *link = links[i];
+        if (i == 0)
+            av_bprintf(buf, "Filter links:\n");
+        av_bprintf(buf, "  [");
+        print_link_label(buf, link, i);
+        av_bprintf(buf, ": %s -> %s] ", get_filter_name(link->src),
+                   get_filter_name(link->dst));
+
+        switch (link->type) {
+        case AVMEDIA_TYPE_VIDEO:
+            av_bprintf(buf, "%s %dx%d [SAR %d:%d] csp:%s range:%s\n",
+                       av_get_pix_fmt_name(link->format), link->w, link->h,
+                       link->sample_aspect_ratio.num, link->sample_aspect_ratio.den,
+                       av_color_space_name(link->colorspace),
+                       av_color_range_name(link->color_range));
+            break;
+        case AVMEDIA_TYPE_AUDIO:
+            av_bprintf(buf, "%s %dHz ",
+                       av_get_sample_fmt_name(link->format), link->sample_rate);
+            av_channel_layout_describe_bprint(&link->ch_layout, buf);
+            av_bprintf(buf, "\n");
+            break;
+        default:
+            av_bprintf(buf, "unknown\n");
+            continue;
+        }
+    }
+
+    ret = 0;
+fail:
+    av_free(links);
+    av_free(filter_opts);
+    return ret;
+}
+
+
 char *avfilter_graph_dump(AVFilterGraph *graph, const char *options)
 {
     AVBPrint buf;
@@ -202,6 +319,13 @@ char *avfilter_graph_dump(AVFilterGraph *graph, const char *options)
     case FMT_PRETTY:
         dump_pretty(&buf, graph);
         break;
+    case FMT_COMPLEX:
+        ret = dump_complex(&buf, graph);
+        if (ret < 0) {
+            av_bprint_finalize(&buf, NULL);
+            return NULL;
+        }
+        break;
     }
 
     av_bprint_finalize(&buf, &dump);
-- 
2.47.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".

  parent reply	other threads:[~2025-02-18 12:46 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-18 12:46 [FFmpeg-devel] [PATCH v2 1/4] avfilter/graphdump: implement options parsing Niklas Haas
2025-02-18 12:46 ` [FFmpeg-devel] [PATCH v2 2/4] avfilter/filters: keep track of AVFilterPad labels Niklas Haas
2025-02-18 12:46 ` Niklas Haas [this message]
2025-02-18 12:46 ` [FFmpeg-devel] [PATCH v2 4/4] fftools/ffmpeg_filter: add -dump_filter_graph option Niklas Haas
2025-02-18 16:43   ` epirat07
2025-02-19 17:36     ` Niklas Haas
2025-02-18 12:50 ` [FFmpeg-devel] [PATCH v2 1/4] avfilter/graphdump: implement options parsing Niklas Haas

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=20250218124603.95398-3-ffmpeg@haasn.xyz \
    --to=ffmpeg@haasn.xyz \
    --cc=ffmpeg-devel@ffmpeg.org \
    --cc=git@haasn.dev \
    /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