From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 165AA4D1E4 for ; Tue, 18 Feb 2025 12:46:31 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id AB09768C0BA; Tue, 18 Feb 2025 14:46:15 +0200 (EET) Received: from haasn.dev (haasn.dev [78.46.187.166]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id E25DE68BD76 for ; Tue, 18 Feb 2025 14:46:07 +0200 (EET) Received: from haasn.dev (unknown [10.30.1.1]) by haasn.dev (Postfix) with ESMTP id A8DF040C0A; Tue, 18 Feb 2025 13:46:07 +0100 (CET) From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Tue, 18 Feb 2025 13:46:02 +0100 Message-ID: <20250218124603.95398-3-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.47.0 In-Reply-To: <20250218124603.95398-1-ffmpeg@haasn.xyz> References: <20250218124603.95398-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 3/4] avfilter/graphdump: add complex format X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Niklas Haas Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: From: Niklas Haas 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".