Commit 64d91f9787 for ffmpeg

commit 64d91f978704eb5c1aeba760731dfcc947ca1c71
Author: James Almer <jamrial@gmail.com>
Date:   Mon Oct 5 19:02:57 2026 -0300

    fftools/ffprobe: support printing child groups within groups

    Signed-off-by: James Almer <jamrial@gmail.com>

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 123e0b8d9b..dc5fab949a 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -205,6 +205,8 @@ typedef enum {
     SECTION_ID_PROGRAMS,
     SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
     SECTION_ID_STREAM_GROUP_STREAM_TAGS,
+    SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION,
+    SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS,
     SECTION_ID_STREAM_GROUP,
     SECTION_ID_STREAM_GROUP_COMPONENTS,
     SECTION_ID_STREAM_GROUP_COMPONENT,
@@ -220,6 +222,8 @@ typedef enum {
     SECTION_ID_STREAM_GROUP_SIDE_DATA,
     SECTION_ID_STREAM_GROUP_STREAMS,
     SECTION_ID_STREAM_GROUP_STREAM,
+    SECTION_ID_STREAM_GROUP_STREAM_GROUPS,
+    SECTION_ID_STREAM_GROUP_STREAM_GROUP,
     SECTION_ID_STREAM_GROUP_DISPOSITION,
     SECTION_ID_STREAM_GROUP_TAGS,
     SECTION_ID_STREAM_GROUPS,
@@ -297,9 +301,11 @@ static const AVTextFormatSection sections[] = {
     [SECTION_ID_PROGRAM_TAGS] =               { SECTION_ID_PROGRAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
     [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
     [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
+    [SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION] = { SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_stream_group_disposition" },
+    [SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS] =  { SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_stream_group_tags" },
     [SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION] = { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_stream_disposition" },
     [SECTION_ID_STREAM_GROUP_STREAM_TAGS] =        { SECTION_ID_STREAM_GROUP_STREAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_stream_tags" },
-    [SECTION_ID_STREAM_GROUP] =                    { SECTION_ID_STREAM_GROUP, "stream_group", 0, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 } },
+    [SECTION_ID_STREAM_GROUP] =                    { SECTION_ID_STREAM_GROUP, "stream_group", 0, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, SECTION_ID_STREAM_GROUP_STREAM_GROUPS, -1 } },
     [SECTION_ID_STREAM_GROUP_COMPONENTS] =         { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_COMPONENT, -1 }, .element_name = "component", .unique_name = "stream_group_components" },
     [SECTION_ID_STREAM_GROUP_COMPONENT] =          { SECTION_ID_STREAM_GROUP_COMPONENT, "component", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SIDE_DATA_LIST, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_stream_group_type },
     [SECTION_ID_STREAM_GROUP_SUBCOMPONENTS] =      { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, "subcomponents", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, -1 }, .element_name = "component" },
@@ -314,6 +320,8 @@ static const AVTextFormatSection sections[] = {
     [SECTION_ID_STREAM_GROUP_SIDE_DATA] =          { SECTION_ID_STREAM_GROUP_SIDE_DATA, "side_data", AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_group_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
     [SECTION_ID_STREAM_GROUP_STREAMS] =            { SECTION_ID_STREAM_GROUP_STREAMS, "streams", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM, -1 }, .unique_name = "stream_group_streams" },
     [SECTION_ID_STREAM_GROUP_STREAM] =             { SECTION_ID_STREAM_GROUP_STREAM, "stream", 0, { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAM_TAGS, -1 }, .unique_name = "stream_group_stream" },
+    [SECTION_ID_STREAM_GROUP_STREAM_GROUPS] =      { SECTION_ID_STREAM_GROUP_STREAM_GROUPS, "stream_groups", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM_GROUP, -1 }, .unique_name = "stream_group_stream_groups" },
+    [SECTION_ID_STREAM_GROUP_STREAM_GROUP] =       { SECTION_ID_STREAM_GROUP_STREAM_GROUP, "stream_group", 0, { SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS, -1 }, .unique_name = "stream_group_stream_group" },
     [SECTION_ID_STREAM_GROUP_DISPOSITION] =        { SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_disposition" },
     [SECTION_ID_STREAM_GROUP_TAGS] =               { SECTION_ID_STREAM_GROUP_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_tags" },
     [SECTION_ID_STREAM_GROUPS] =                   { SECTION_ID_STREAM_GROUPS, "stream_groups", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP, -1 } },
@@ -2374,14 +2382,31 @@ static void print_stream_group_params(AVTextFormatContext *tfc, AVStreamGroup *s
     avtext_print_section_footer(tfc); // SECTION_ID_STREAM_GROUP_COMPONENTS
 }

-static int show_stream_group(AVTextFormatContext *tfc, InputFile *ifile, AVStreamGroup *stg)
+static int show_stream_group(AVTextFormatContext *tfc, InputFile *ifile, AVStreamGroup *stg, int container)
 {
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     AVBPrint pbuf;
+    const SectionID section_header[] = {
+        SECTION_ID_STREAM_GROUP,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP,
+    };
+    const SectionID section_disposition[] = {
+        SECTION_ID_STREAM_GROUP_DISPOSITION,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP_DISPOSITION,
+    };
+    const SectionID section_tags[] = {
+        SECTION_ID_STREAM_GROUP_TAGS,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS,
+        SECTION_ID_STREAM_GROUP_STREAM_GROUP_TAGS,
+    };
     int ret = 0;

+    av_assert0(container < FF_ARRAY_ELEMS(section_header));
+
     av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
-    avtext_print_section_header(tfc, NULL, SECTION_ID_STREAM_GROUP);
+    avtext_print_section_header(tfc, NULL, section_header[container]);
     print_int("index", stg->index);
     if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%"PRIx64, stg->id);
     else                                          print_str_opt("id", "N/A");
@@ -2390,18 +2415,37 @@ static int show_stream_group(AVTextFormatContext *tfc, InputFile *ifile, AVStrea
         print_str("type", av_x_if_null(avformat_stream_group_name(stg->type), "unknown"));
     else
         print_str_opt("type", "unknown");
+
+    if (container)
+        goto end;
+
     if (do_show_stream_group_components)
         print_stream_group_params(tfc, stg);

     /* Print disposition information */
-    if (do_show_stream_group_disposition)
-        print_dispositions(tfc, stg->disposition, SECTION_ID_STREAM_GROUP_DISPOSITION);
+    if (do_show_stream_group_disposition) {
+        av_assert0(container < FF_ARRAY_ELEMS(section_disposition));
+        print_dispositions(tfc, stg->disposition, section_disposition[container]);
+    }

-    if (do_show_stream_group_tags)
-        ret = show_tags(tfc, stg->metadata, SECTION_ID_STREAM_GROUP_TAGS);
+    if (do_show_stream_group_tags) {
+        av_assert0(container < FF_ARRAY_ELEMS(section_tags));
+        ret = show_tags(tfc, stg->metadata, section_tags[container]);
+    }
     if (ret < 0)
         goto end;

+    avtext_print_section_header(tfc, NULL, SECTION_ID_STREAM_GROUP_STREAM_GROUPS);
+    for (unsigned i = 0; i < stg->nb_entries; i++) {
+        AVStreamGroupEntry *entry = stg->entries[i];
+        if (entry->type != AV_STREAM_GROUP_ENTRY_STREAM_GROUP)
+            continue;
+        ret = show_stream_group(tfc, ifile, entry->stream_group, IN_STREAM_GROUP);
+        if (ret < 0)
+            break;
+    }
+    avtext_print_section_footer(tfc);
+
     avtext_print_section_header(tfc, NULL, SECTION_ID_STREAM_GROUP_STREAMS);
     for (unsigned i = 0; i < stg->nb_entries; i++) {
         AVStreamGroupEntry *entry = stg->entries[i];
@@ -2430,7 +2474,7 @@ static int show_stream_groups(AVTextFormatContext *tfc, InputFile *ifile)
     for (unsigned i = 0; i < fmt_ctx->nb_stream_groups; i++) {
         AVStreamGroup *stg = fmt_ctx->stream_groups[i];

-        ret = show_stream_group(tfc, ifile, stg);
+        ret = show_stream_group(tfc, ifile, stg, 0);
         if (ret < 0)
             break;
     }