* [FFmpeg-devel] [PATCH] fftools/tf_mermaid: close subgraph header when there are no inputs. (PR #20898)
@ 2025-11-12 8:05 Ayose C. via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: Ayose C. via ffmpeg-devel @ 2025-11-12 8:05 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Ayose C.
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 <ayosec@gmail.com>
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 <ayosec@gmail.com>
---
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 -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-11-12 8:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-12 8:05 [FFmpeg-devel] [PATCH] fftools/tf_mermaid: close subgraph header when there are no inputs. (PR #20898) Ayose C. via ffmpeg-devel
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