PR #22542 opened by jermy
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22542
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22542.patch

Specifically output side data from tile groups with -show_stream_groups
which includes rotation information in HEIC images.

Signed-off-by: Jeremy James <[email protected]>

--

For more context, this data is the only place where iPhone 15 HEIC photos 
contain the "rotation" flag and therefore is important if trying to extract a 
still from such a file with a custom set of filters to reassemble them.

The following is a diff of ffprobe JSON output on such a file before and after.

```--- /tmp/heic_before 2026-03-18 12:16:58.755433526 +0000
+++ /tmp/heic_after     2026-03-18 12:17:50.824829712 +0000
@@ -240,6 +240,20 @@
                             "tile_horizontal_offset": 5120,
                             "tile_vertical_offset": 3584
                         }
+                    ],
+                    "side_data_list": [
+                        {
+                            "side_data_type": "EXIF metadata",
+                            "size": 78
+                        },
+                        {
+                            "side_data_type": "Display Matrix",
+                            "displaymatrix": "\n00000000:            0       
65536           0\n00000001:       -65536           0           0\n00000002:    
        0           0  1073741824\n",
+                            "rotation": -90
+                        },
+                        {
+                            "side_data_type": "ICC Profile"
+                        }
                     ]
                 }
             ],
```



>From f82b2f77ed3dff47d4290a93c34faa3aea9cc0b1 Mon Sep 17 00:00:00 2001
From: Jeremy James <[email protected]>
Date: Wed, 18 Mar 2026 12:35:34 +0000
Subject: [PATCH] fftools/ffprobe: Show stream group side data

Specifically output side data from tile groups with -show_stream_groups
which includes rotation information in HEIC images.

Signed-off-by: Jeremy James <[email protected]>
---
 doc/ffprobe.xsd   |  1 +
 fftools/ffprobe.c | 24 +++++++++++++++++++-----
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd
index c870db7aa0..6ad8e3eb0a 100644
--- a/doc/ffprobe.xsd
+++ b/doc/ffprobe.xsd
@@ -357,6 +357,7 @@
   <xsd:complexType name="streamGroupComponentType">
     <xsd:sequence>
       <xsd:element name="subcomponents" 
type="ffprobe:streamGroupSubComponentList" minOccurs="0" maxOccurs="1"/>
+      <xsd:element name="side_data_list" type="ffprobe:packetSideDataListType" 
minOccurs="0" maxOccurs="1"/>
       <xsd:element name="component_entry" type="ffprobe:streamGroupEntryType" 
minOccurs="0" maxOccurs="unbounded"/>
     </xsd:sequence>
   </xsd:complexType>
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index e2b58b70f7..49345d7b72 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -215,6 +215,8 @@ typedef enum {
     SECTION_ID_STREAM_GROUP_SUBPIECE,
     SECTION_ID_STREAM_GROUP_BLOCKS,
     SECTION_ID_STREAM_GROUP_BLOCK,
+    SECTION_ID_STREAM_GROUP_SIDE_DATA_LIST,
+    SECTION_ID_STREAM_GROUP_SIDE_DATA,
     SECTION_ID_STREAM_GROUP_STREAMS,
     SECTION_ID_STREAM_GROUP_STREAM,
     SECTION_ID_STREAM_GROUP_DISPOSITION,
@@ -298,7 +300,7 @@ static const AVTextFormatSection sections[] = {
     [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_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_SUBCOMPONENTS, -1 }, .unique_name = 
"stream_group_component", .element_name = "component_entry", .get_type = 
get_stream_group_type },
+    [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" },
     [SECTION_ID_STREAM_GROUP_SUBCOMPONENT] =       { 
SECTION_ID_STREAM_GROUP_SUBCOMPONENT, "subcomponent", 
AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE,
 { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .element_name = "subcomponent_entry", 
.get_type = get_raw_string_type },
     [SECTION_ID_STREAM_GROUP_PIECES] =             { 
SECTION_ID_STREAM_GROUP_PIECES, "pieces", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, 
{ SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = 
"stream_group_pieces" },
@@ -307,6 +309,8 @@ static const AVTextFormatSection sections[] = {
     [SECTION_ID_STREAM_GROUP_SUBPIECE] =           { 
SECTION_ID_STREAM_GROUP_SUBPIECE, "subpiece", 
AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE,
 { SECTION_ID_STREAM_GROUP_BLOCKS, -1 }, .element_name = "subpiece_entry", 
.get_type = get_raw_string_type },
     [SECTION_ID_STREAM_GROUP_BLOCKS] =             { 
SECTION_ID_STREAM_GROUP_BLOCKS, "blocks", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, 
{ SECTION_ID_STREAM_GROUP_BLOCK, -1 }, .element_name = "block" },
     [SECTION_ID_STREAM_GROUP_BLOCK] =              { 
SECTION_ID_STREAM_GROUP_BLOCK, "block", 
AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE,
 { -1 }, .element_name = "block_entry", .get_type = get_raw_string_type },
+    [SECTION_ID_STREAM_GROUP_SIDE_DATA_LIST] =     { 
SECTION_ID_STREAM_GROUP_SIDE_DATA_LIST, "side_data_list", 
AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SIDE_DATA, -1 }, 
.element_name = "side_data", .unique_name = "stream_group_side_data_list" },
+    [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_DISPOSITION] =        { 
SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = 
"stream_group_disposition" },
@@ -1008,7 +1012,8 @@ static void print_film_grain_params(AVTextFormatContext 
*tfc,
 }
 
 static void print_pkt_side_data(AVTextFormatContext *tfc,
-                                AVCodecParameters *par,
+                                int width,
+                                int height,
                                 const AVPacketSideData *sd,
                                 SectionID id_data)
 {
@@ -1034,7 +1039,7 @@ static void print_pkt_side_data(AVTextFormatContext *tfc,
             print_int("padding", spherical->padding);
         } else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) 
{
             size_t l, t, r, b;
-            av_spherical_tile_bounds(spherical, par->width, par->height,
+            av_spherical_tile_bounds(spherical, width, height,
                                      &l, &t, &r, &b);
             print_int("bound_left", l);
             print_int("bound_top", t);
@@ -1305,7 +1310,7 @@ static void show_packet(AVTextFormatContext *tfc, 
InputFile *ifile, AVPacket *pk
 
         avtext_print_section_header(tfc, NULL, 
SECTION_ID_PACKET_SIDE_DATA_LIST);
         for (int i = 0; i < pkt->side_data_elems; i++) {
-            print_pkt_side_data(tfc, st->codecpar, &pkt->side_data[i],
+            print_pkt_side_data(tfc, st->codecpar->width, 
st->codecpar->height, &pkt->side_data[i],
                                 SECTION_ID_PACKET_SIDE_DATA);
             avtext_print_section_footer(tfc);
         }
@@ -1975,7 +1980,7 @@ static int show_stream(AVTextFormatContext *tfc, 
AVFormatContext *fmt_ctx, int s
     if (stream->codecpar->nb_coded_side_data) {
         avtext_print_section_header(tfc, NULL, 
SECTION_ID_STREAM_SIDE_DATA_LIST);
         for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) {
-            print_pkt_side_data(tfc, stream->codecpar, 
&stream->codecpar->coded_side_data[i],
+            print_pkt_side_data(tfc, stream->codecpar->width, 
stream->codecpar->height, &stream->codecpar->coded_side_data[i],
                                 SECTION_ID_STREAM_SIDE_DATA);
             avtext_print_section_footer(tfc);
         }
@@ -2075,6 +2080,15 @@ static void print_tile_grid_params(AVTextFormatContext 
*tfc, const AVStreamGroup
         avtext_print_section_footer(tfc);
     }
     avtext_print_section_footer(tfc);
+    if (tile_grid->nb_coded_side_data) {
+        avtext_print_section_header(tfc, NULL, 
SECTION_ID_STREAM_GROUP_SIDE_DATA_LIST);
+        for (int i = 0; i < tile_grid->nb_coded_side_data; i++) {
+            print_pkt_side_data(tfc, tile_grid->width, tile_grid->height, 
&tile_grid->coded_side_data[i],
+                                SECTION_ID_STREAM_GROUP_SIDE_DATA);
+            avtext_print_section_footer(tfc);
+        }
+        avtext_print_section_footer(tfc);
+    }
     avtext_print_section_footer(tfc);
 }
 
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to