PR #20898 opened by Ayose C. (ayosec) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20898 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20898.patch
### Problem FFmpeg can be used with no inputs. For example, the command ```bash $ ffmpeg -filter_complex color=blue:d=1 /tmp/blue.mp4 ``` creates a 1-second video with a solid color. When the `-print_graphs_format mermaid` (or `mermaidhtml`) is used on a command like that, the `subgraph` section for `ff-inputfiles` and `ff-decoders` is incomplete: ``` subgraph G1_Inputs["<div class="ff-inputfiles"> end class G1_Inputs ff-inputfiles subgraph G1_Decoders["<div class="ff-decoders"> end class G1_Decoders ff-decoders ``` Opening the generated file in a browser throws this error: ``` Parse error on line 26: ...subgraph G1_Encoders["<div class='ff-enc -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'SQS' ``` (`G1_Encoders` is right after `G1_Decoders`) The mermaid code is missing the `</div>"]` fragment to complete the `subgraph` line. ### Cause The fragment to complete the `subgraph` header [is written in `mermaid_print_section_header`](https://code.ffmpeg.org/FFmpeg/FFmpeg/src/commit/418235e98a42681a7f243ba6de8f7e9284a677b5/fftools/textformat/tf_mermaid.c#L303-L312): ```c static void mermaid_print_section_header(AVTextFormatContext *tfc, const void *data) { // ... if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_SUBGRAPH) { // ... if (parent_sec_data.subgraph_start_incomplete) { // ... writer_put_str(tfc, "</div>\"]\n"); mmc->section_data[tfc->level - 1].subgraph_start_incomplete = 0; } } // ... if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_SUBGRAPH) { // ... writer_printf(tfc, "subgraph %s[\"<div class=\"ff-%s\">", sec_ctx->context_id, section->name); mmc->section_data[tfc->level].subgraph_start_incomplete = 1; ``` The function is invoked from [`print_filter`](https://code.ffmpeg.org/FFmpeg/FFmpeg/src/commit/418235e98a42681a7f243ba6de8f7e9284a677b5/fftools/graph/graphprint.c#L406-L436). It assumes that the `header` function is called at least once before the footer, but this is not the case when there are no inputs: ```c avtext_print_section_header(tfc, NULL, SECTION_ID_FILTER_INPUTS); for (unsigned i = 0; i < filter->nb_inputs; i++) { // ... avtext_print_section_header(tfc, &sec_ctx, SECTION_ID_FILTER_INPUT); // ... } avtext_print_section_footer(tfc); ``` ### Fix The field `subgraph_start_incomplete` tracks if the `subgraph` is line is completed or no. So I moved the code to complete it a function, which is called from the original point (inside the `mermaid_print_section_header`) and from `mermaid_print_section_footer`. ### Test Running the following command: ```bash ffmpeg \ -print_graphs \ -print_graphs_format mermaidhtml \ -print_graphs_file /tmp/after.html -filter_complex color \ -frames:v 1 \ -f null - ``` Before/after the fix shows the expected diff: ```diff --- /tmp/before.html +++ /tmp/after.html @@ -84,11 +84,13 @@ G0_Parsed_color_0 video-G0_Parsed_color_0-out__0_0@== "<span>yuv420p</span><br><span>320x240</span><br><span>1:1</span><br> <br> <br> " ==> out__0_0 - subgraph G1_Inputs["<div class="ff-inputfiles"> end + subgraph G1_Inputs["<div class="ff-inputfiles"></div>"] + end class G1_Inputs ff-inputfiles - subgraph G1_Decoders["<div class="ff-decoders"> end + subgraph G1_Decoders["<div class="ff-decoders"></div>"] + end class G1_Decoders ff-decoders ``` The `after.html` also loads correctly:  I didn't find FATE tests related to the mermaid generation, but I did a before/after comparison with multiple filtergraphs, and in all cases the only difference is the fix. >From f21ddefb50ca169c90ff2e37cea228e7866d285b Mon Sep 17 00:00:00 2001 From: Ayose <[email protected]> Date: Wed, 12 Nov 2025 07:21:23 +0000 Subject: [PATCH] fftools/tf_mermaid: close subgraph header when there are no inputs. Ensure that the fragment to close the header (`</div>\"]`) is written when the function `mermaid_print_section_header` is called only once, which happens when the filtergraph has no inputs. Signed-off-by: Ayose <[email protected]> --- fftools/textformat/tf_mermaid.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fftools/textformat/tf_mermaid.c b/fftools/textformat/tf_mermaid.c index ef730d570b..fdd4ab7e57 100644 --- a/fftools/textformat/tf_mermaid.c +++ b/fftools/textformat/tf_mermaid.c @@ -240,6 +240,21 @@ static void set_str(const char **dst, const char *src) *dst = av_strdup(src); } +static void mermaid_subgraph_complete_start(MermaidContext *mmc, AVTextFormatContext *tfc, int level) { + struct section_data parent_sec_data = mmc->section_data[level]; + AVBPrint *parent_buf = &tfc->section_pbuf[level]; + + if (parent_sec_data.subgraph_start_incomplete) { + + if (parent_buf->len > 0) + writer_printf(tfc, "%s", parent_buf->str); + + writer_put_str(tfc, "</div>\"]\n"); + + mmc->section_data[level].subgraph_start_incomplete = 0; + } +} + #define MM_INDENT() writer_printf(tfc, "%*c", mmc->indent_level * 2, ' ') static void mermaid_print_section_header(AVTextFormatContext *tfc, const void *data) @@ -296,19 +311,7 @@ static void mermaid_print_section_header(AVTextFormatContext *tfc, const void *d } if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_SUBGRAPH) { - - struct section_data parent_sec_data = mmc->section_data[tfc->level - 1]; - AVBPrint *parent_buf = &tfc->section_pbuf[tfc->level - 1]; - - if (parent_sec_data.subgraph_start_incomplete) { - - if (parent_buf->len > 0) - writer_printf(tfc, "%s", parent_buf->str); - - writer_put_str(tfc, "</div>\"]\n"); - - mmc->section_data[tfc->level - 1].subgraph_start_incomplete = 0; - } + mermaid_subgraph_complete_start(mmc, tfc, tfc->level - 1); } av_freep(&mmc->section_data[tfc->level].section_id); @@ -454,6 +457,8 @@ static void mermaid_print_section_footer(AVTextFormatContext *tfc) } else if ((section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_SUBGRAPH)) { + mermaid_subgraph_complete_start(mmc, tfc, tfc->level); + MM_INDENT(); writer_put_str(tfc, "end\n"); -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
