diff mbox series

[FFmpeg-devel,6/9] fftools/ffprobe: export Tile Grid Stream Group parameters

Message ID 20240305000033.10379-6-jamrial@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/9,v2] fftools/ffprobe: fix printing side data components and pieces | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

James Almer March 5, 2024, midnight UTC
Signed-off-by: James Almer <jamrial@gmail.com>
---
 fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 36 insertions(+), 5 deletions(-)

Comments

James Almer March 5, 2024, 3:14 p.m. UTC | #1
On 3/4/2024 9:00 PM, James Almer wrote:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>   fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 36 insertions(+), 5 deletions(-)
> 
> diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
> index ff05c4c1db..e736023bcd 100644
> --- a/fftools/ffprobe.c
> +++ b/fftools/ffprobe.c
> @@ -209,6 +209,10 @@ typedef enum {
>       SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
>       SECTION_ID_STREAM_GROUP_STREAM_TAGS,
>       SECTION_ID_STREAM_GROUP,
> +    SECTION_ID_STREAM_GROUP_COMPONENTS,
> +    SECTION_ID_STREAM_GROUP_COMPONENT,
> +    SECTION_ID_STREAM_GROUP_PIECES,
> +    SECTION_ID_STREAM_GROUP_PIECE,
>       SECTION_ID_STREAM_GROUP_STREAMS,
>       SECTION_ID_STREAM_GROUP_STREAM,
>       SECTION_ID_STREAM_GROUP_DISPOSITION,
> @@ -282,8 +286,8 @@ static struct section sections[] = {
>       [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] =       { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
>       [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" },
>       [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] =      { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type },
> -    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece" },
> -    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .get_type = get_raw_string_type },
> +    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" },
> +    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type },
>       [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
>       [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
>       [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
> @@ -309,7 +313,11 @@ static struct section sections[] = {
>       [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
>       [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", 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", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
> +    [SECTION_ID_STREAM_GROUP] =                    { SECTION_ID_STREAM_GROUP, "stream_group", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
> +    [SECTION_ID_STREAM_GROUP_COMPONENTS] =         { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", 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", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_raw_string_type },
> +    [SECTION_ID_STREAM_GROUP_PIECES] =             { SECTION_ID_STREAM_GROUP_PIECES, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" },
> +    [SECTION_ID_STREAM_GROUP_PIECE] =              { SECTION_ID_STREAM_GROUP_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "stream_group_piece", .get_type = get_raw_string_type },

I think I'll withdraw this patch for now. Some stream group types have 
several layers of data, so components and pieces would not be enough. 
This is the case for IAMF Audio Elements and Mix Presentation, for example.
A generic way to add N amount of layers would be needed.
Stefano Sabatini March 5, 2024, 3:21 p.m. UTC | #2
On date Monday 2024-03-04 21:00:26 -0300, James Almer wrote:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>  fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 36 insertions(+), 5 deletions(-)
> 
> diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
> index ff05c4c1db..e736023bcd 100644
> --- a/fftools/ffprobe.c
> +++ b/fftools/ffprobe.c
> @@ -209,6 +209,10 @@ typedef enum {
>      SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
>      SECTION_ID_STREAM_GROUP_STREAM_TAGS,
>      SECTION_ID_STREAM_GROUP,
> +    SECTION_ID_STREAM_GROUP_COMPONENTS,
> +    SECTION_ID_STREAM_GROUP_COMPONENT,
> +    SECTION_ID_STREAM_GROUP_PIECES,
> +    SECTION_ID_STREAM_GROUP_PIECE,
>      SECTION_ID_STREAM_GROUP_STREAMS,
>      SECTION_ID_STREAM_GROUP_STREAM,
>      SECTION_ID_STREAM_GROUP_DISPOSITION,
> @@ -282,8 +286,8 @@ static struct section sections[] = {
>      [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] =       { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
>      [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" },
>      [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] =      { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type },
> -    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece" },
> -    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .get_type = get_raw_string_type },
> +    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" },
> +    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type },
>      [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
>      [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
>      [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
> @@ -309,7 +313,11 @@ static struct section sections[] = {
>      [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
>      [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", 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", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
> +    [SECTION_ID_STREAM_GROUP] =                    { SECTION_ID_STREAM_GROUP, "stream_group", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
> +    [SECTION_ID_STREAM_GROUP_COMPONENTS] =         { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", 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", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_raw_string_type },
> +    [SECTION_ID_STREAM_GROUP_PIECES] =             { SECTION_ID_STREAM_GROUP_PIECES, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" },
> +    [SECTION_ID_STREAM_GROUP_PIECE] =              { SECTION_ID_STREAM_GROUP_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "stream_group_piece", .get_type = get_raw_string_type },
>      [SECTION_ID_STREAM_GROUP_STREAMS] =            { SECTION_ID_STREAM_GROUP_STREAMS, "streams", 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_DISPOSITION] =        { SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_disposition" },
> @@ -3388,9 +3396,32 @@ static int show_programs(WriterContext *w, InputFile *ifile)
>  static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg)
>  {
>      const char *unknown = "unknown";
> -    if (stg->type != AV_STREAM_GROUP_PARAMS_NONE)

> +    if (stg->type != AV_STREAM_GROUP_PARAMS_NONE) {
>          print_str("type", av_x_if_null(avformat_stream_group_name(stg->type), unknown));

nit: you can avoid the indent if you check the NONE case and return
immediately in that case

> -    else
> +        if (stg->type == AV_STREAM_GROUP_PARAMS_TILE_GRID) {
> +            AVStreamGroupTileGrid *tile_grid = stg->params.tile_grid;
> +            writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_COMPONENTS);
> +            writer_print_section_header(w, "parameters", SECTION_ID_STREAM_GROUP_COMPONENT);
> +            print_int("nb_tiles",          tile_grid->nb_tiles);
> +            print_int("coded_width",       tile_grid->coded_width);
> +            print_int("coded_height",      tile_grid->coded_height);
> +            print_int("horizontal_offset", tile_grid->horizontal_offset);
> +            print_int("vertical_offset",   tile_grid->vertical_offset);
> +            print_int("width",             tile_grid->width);
> +            print_int("height",            tile_grid->height);
> +            writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_PIECES);
> +            for (int i = 0; i < tile_grid->nb_tiles; i++) {
> +                writer_print_section_header(w, "tile_offset", SECTION_ID_STREAM_GROUP_PIECE);
> +                print_int("stream_index",           tile_grid->offsets[i].idx);
> +                print_int("tile_horizontal_offset", tile_grid->offsets[i].horizontal);
> +                print_int("tile_vertical_offset",   tile_grid->offsets[i].vertical);
> +                writer_print_section_footer(w);
> +            }
> +            writer_print_section_footer(w);
> +            writer_print_section_footer(w);
> +            writer_print_section_footer(w);
> +        }
> +    } else
>          print_str_opt("type", unknown);
>  }

LGTM, thanks.
Stefano Sabatini March 5, 2024, 3:24 p.m. UTC | #3
On date Tuesday 2024-03-05 12:14:53 -0300, James Almer wrote:
> On 3/4/2024 9:00 PM, James Almer wrote:
> > Signed-off-by: James Almer <jamrial@gmail.com>
> > ---
> >   fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
> >   1 file changed, 36 insertions(+), 5 deletions(-)
[...] 

> I think I'll withdraw this patch for now. Some stream group types have
> several layers of data, so components and pieces would not be enough. This
> is the case for IAMF Audio Elements and Mix Presentation, for example.
> A generic way to add N amount of layers would be needed.

I see.

Does it mean that the nesting level is greater than 3 (side_data,
components, pieces)?

Probably the best we can do is some kind of flattening (ffprobe is not
designed for generic level of nesting, as we need to define the
structure of the layout, at least for some formats (e.g. XML)).
James Almer March 5, 2024, 3:50 p.m. UTC | #4
On 3/5/2024 12:24 PM, Stefano Sabatini wrote:
> On date Tuesday 2024-03-05 12:14:53 -0300, James Almer wrote:
>> On 3/4/2024 9:00 PM, James Almer wrote:
>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>> ---
>>>    fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
>>>    1 file changed, 36 insertions(+), 5 deletions(-)
> [...]
> 
>> I think I'll withdraw this patch for now. Some stream group types have
>> several layers of data, so components and pieces would not be enough. This
>> is the case for IAMF Audio Elements and Mix Presentation, for example.
>> A generic way to add N amount of layers would be needed.
> 
> I see.
> 
> Does it mean that the nesting level is greater than 3 (side_data,
> components, pieces)?

Yes. Look at AVIAMFMixPresentation, which has an array of AVIAMFSubmix, 
which in turn have an array of AVIAMFSubmixLayout plus an array of 
AVIAMFSubmixElement per submix, the latter which has a 
AVIAMFParamDefinition per element.
If the AVIAMFMixPresentation is the component, the AVIAMFSubmix would be 
the piece, and then we ran out of levels.

> 
> Probably the best we can do is some kind of flattening (ffprobe is not
> designed for generic level of nesting, as we need to define the
> structure of the layout, at least for some formats (e.g. XML)).
> _______________________________________________
> 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".
Stefano Sabatini March 6, 2024, 1:39 p.m. UTC | #5
On date Tuesday 2024-03-05 12:50:09 -0300, James Almer wrote:
> On 3/5/2024 12:24 PM, Stefano Sabatini wrote:
> > On date Tuesday 2024-03-05 12:14:53 -0300, James Almer wrote:
> > > On 3/4/2024 9:00 PM, James Almer wrote:
> > > > Signed-off-by: James Almer <jamrial@gmail.com>
> > > > ---
> > > >    fftools/ffprobe.c | 41 ++++++++++++++++++++++++++++++++++++-----
> > > >    1 file changed, 36 insertions(+), 5 deletions(-)
> > [...]
> > 
> > > I think I'll withdraw this patch for now. Some stream group types have
> > > several layers of data, so components and pieces would not be enough. This
> > > is the case for IAMF Audio Elements and Mix Presentation, for example.
> > > A generic way to add N amount of layers would be needed.
> > 
> > I see.
> > 
> > Does it mean that the nesting level is greater than 3 (side_data,
> > components, pieces)?
> 

> Yes. Look at AVIAMFMixPresentation, which has an array of AVIAMFSubmix,
> which in turn have an array of AVIAMFSubmixLayout plus an array of
> AVIAMFSubmixElement per submix, the latter which has a AVIAMFParamDefinition
> per element.
> If the AVIAMFMixPresentation is the component, the AVIAMFSubmix would be the
> piece, and then we ran out of levels.

Well, the simplemst approach is to add more levels to the layout
(side_data, components, pieces, subpieces?, ...).
diff mbox series

Patch

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index ff05c4c1db..e736023bcd 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -209,6 +209,10 @@  typedef enum {
     SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
     SECTION_ID_STREAM_GROUP_STREAM_TAGS,
     SECTION_ID_STREAM_GROUP,
+    SECTION_ID_STREAM_GROUP_COMPONENTS,
+    SECTION_ID_STREAM_GROUP_COMPONENT,
+    SECTION_ID_STREAM_GROUP_PIECES,
+    SECTION_ID_STREAM_GROUP_PIECE,
     SECTION_ID_STREAM_GROUP_STREAMS,
     SECTION_ID_STREAM_GROUP_STREAM,
     SECTION_ID_STREAM_GROUP_DISPOSITION,
@@ -282,8 +286,8 @@  static struct section sections[] = {
     [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] =       { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
     [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" },
     [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] =      { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type },
-    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece" },
-    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .get_type = get_raw_string_type },
+    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" },
+    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type },
     [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
     [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
     [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
@@ -309,7 +313,11 @@  static struct section sections[] = {
     [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
     [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", 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", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
+    [SECTION_ID_STREAM_GROUP] =                    { SECTION_ID_STREAM_GROUP, "stream_group", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type = get_stream_group_type },
+    [SECTION_ID_STREAM_GROUP_COMPONENTS] =         { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", 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", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_raw_string_type },
+    [SECTION_ID_STREAM_GROUP_PIECES] =             { SECTION_ID_STREAM_GROUP_PIECES, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" },
+    [SECTION_ID_STREAM_GROUP_PIECE] =              { SECTION_ID_STREAM_GROUP_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "stream_group_piece", .get_type = get_raw_string_type },
     [SECTION_ID_STREAM_GROUP_STREAMS] =            { SECTION_ID_STREAM_GROUP_STREAMS, "streams", 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_DISPOSITION] =        { SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_disposition" },
@@ -3388,9 +3396,32 @@  static int show_programs(WriterContext *w, InputFile *ifile)
 static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg)
 {
     const char *unknown = "unknown";
-    if (stg->type != AV_STREAM_GROUP_PARAMS_NONE)
+    if (stg->type != AV_STREAM_GROUP_PARAMS_NONE) {
         print_str("type", av_x_if_null(avformat_stream_group_name(stg->type), unknown));
-    else
+        if (stg->type == AV_STREAM_GROUP_PARAMS_TILE_GRID) {
+            AVStreamGroupTileGrid *tile_grid = stg->params.tile_grid;
+            writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_COMPONENTS);
+            writer_print_section_header(w, "parameters", SECTION_ID_STREAM_GROUP_COMPONENT);
+            print_int("nb_tiles",          tile_grid->nb_tiles);
+            print_int("coded_width",       tile_grid->coded_width);
+            print_int("coded_height",      tile_grid->coded_height);
+            print_int("horizontal_offset", tile_grid->horizontal_offset);
+            print_int("vertical_offset",   tile_grid->vertical_offset);
+            print_int("width",             tile_grid->width);
+            print_int("height",            tile_grid->height);
+            writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_PIECES);
+            for (int i = 0; i < tile_grid->nb_tiles; i++) {
+                writer_print_section_header(w, "tile_offset", SECTION_ID_STREAM_GROUP_PIECE);
+                print_int("stream_index",           tile_grid->offsets[i].idx);
+                print_int("tile_horizontal_offset", tile_grid->offsets[i].horizontal);
+                print_int("tile_vertical_offset",   tile_grid->offsets[i].vertical);
+                writer_print_section_footer(w);
+            }
+            writer_print_section_footer(w);
+            writer_print_section_footer(w);
+            writer_print_section_footer(w);
+        }
+    } else
         print_str_opt("type", unknown);
 }