[FFmpeg-devel] [PATCH 3/4] avformat/mov: add referenced thumbnail streams to tile stream groups

James Almer jamrial at gmail.com
Thu Sep 26 17:33:48 EEST 2024


Use the reference information present in iref boxes of type thmb to include the
relevant streams into a Tile Grid stream group.
This does not yet export the relation of a thumbnail and an independent stream
(not a grid). For this, a new Stream group type would probably be needed.

Signed-off-by: James Almer <jamrial at gmail.com>
---
 libavformat/dump.c |  9 ++++--
 libavformat/isom.h |  3 +-
 libavformat/mov.c  | 71 +++++++++++++++++++++++++++++++++++-----------
 3 files changed, 64 insertions(+), 19 deletions(-)

diff --git a/libavformat/dump.c b/libavformat/dump.c
index 5178f19685..ba30b92aaf 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -784,11 +784,16 @@ static void dump_stream_group(const AVFormatContext *ic, uint8_t *printed,
         dump_disposition(stg->disposition, AV_LOG_INFO);
         av_log(NULL, AV_LOG_INFO, "\n");
         dump_metadata(NULL, stg->metadata, "    ", AV_LOG_INFO);
-        for (int i = 0; i < stg->nb_streams; i++) {
-            const AVStream *st = stg->streams[i];
+        for (int i = 0; i < tile_grid->nb_tiles; i++) {
+            const AVStream *st = stg->streams[tile_grid->offsets[i].idx];
             dump_stream_format(ic, st->index, i, index, is_output, AV_LOG_VERBOSE);
             printed[st->index] = 1;
         }
+        for (int i = 0; i < stg->nb_streams; i++) {
+            const AVStream *st = stg->streams[i];
+            if (!printed[st->index])
+                dump_stream_format(ic, st->index, i, index, is_output, AV_LOG_VERBOSE);
+        }
         break;
     }
     case AV_STREAM_GROUP_PARAMS_LCEVC: {
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 5076bc5da7..1cf69ed042 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -279,6 +279,8 @@ typedef struct HEIFItem {
     AVStream *st;
     char *name;
     int item_id;
+    struct HEIFItem **ref_item_list;
+    int nb_ref_item_list;
     int64_t extent_length;
     int64_t extent_offset;
     int width;
@@ -360,7 +362,6 @@ typedef struct MOVContext {
     int nb_heif_item;
     HEIFGrid *heif_grid;
     int nb_heif_grid;
-    int thmb_item_id;
     int64_t idat_offset;
     int interleaved_read;
 } MOVContext;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index bd502d489a..8a257ba535 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -8844,23 +8844,55 @@ static int mov_read_iref_dimg(MOVContext *c, AVIOContext *pb, int version)
     return 0;
 }
 
+static int mov_add_ref_to_item(MOVContext *c, AVIOContext *pb, int version,
+                               HEIFItem *from_item)
+{
+    HEIFItem **ref_item_list, *to_item = NULL;
+    int to_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
+
+    for (int j = 0; j < c->nb_heif_item; j++) {
+        if (c->heif_item[j].item_id != to_item_id)
+            continue;
+        to_item = &c->heif_item[j];
+    }
+    if (!to_item) {
+        av_log(c->fc, AV_LOG_ERROR, "thmb in iref references a non-existent item\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    ref_item_list = av_realloc_array(to_item->ref_item_list, to_item->nb_ref_item_list + 1U,
+                                     sizeof(*to_item->ref_item_list));
+    if (!ref_item_list)
+        return AVERROR(ENOMEM);
+    to_item->ref_item_list = ref_item_list;
+    to_item->ref_item_list[to_item->nb_ref_item_list++] = from_item;
+
+    return 0;
+}
+
 static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version)
 {
+    HEIFItem *from_item = NULL;
     int entries;
-    int to_item_id, from_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
+    int from_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
 
-    entries = avio_rb16(pb);
-    if (entries > 1) {
-        avpriv_request_sample(c->fc, "thmb in iref referencing several items");
-        return AVERROR_PATCHWELCOME;
+    for (int i = 0; i < c->nb_heif_item; i++) {
+        if (c->heif_item[i].item_id != from_item_id)
+            continue;
+        from_item = &c->heif_item[i];
+    }
+    if (!from_item) {
+        av_log(c->fc, AV_LOG_ERROR, "thmb in iref references a non-existent item\n");
+        return AVERROR_INVALIDDATA;
     }
-    /* 'to' item ids */
-    to_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
-
-    if (to_item_id != c->primary_item_id)
-        return 0;
 
-    c->thmb_item_id = from_item_id;
+    entries = avio_rb16(pb);
+    /* 'to' item ids */
+    for (int i = 0; i < entries; i++) {
+        int ret = mov_add_ref_to_item(c, pb, version, from_item);
+        if (ret < 0)
+            return ret;
+    }
 
     av_log(c->fc, AV_LOG_TRACE, "thmb: from_item_id %d, entries %d\n",
            from_item_id, entries);
@@ -9718,6 +9750,7 @@ static int mov_read_close(AVFormatContext *s)
     for (i = 0; i < mov->nb_heif_item; i++) {
         av_freep(&mov->heif_item[i].name);
         av_freep(&mov->heif_item[i].aux_type);
+        av_freep(&mov->heif_item[i].ref_item_list);
     }
     av_freep(&mov->heif_item);
     for (i = 0; i < mov->nb_heif_grid; i++) {
@@ -10074,6 +10107,17 @@ static int mov_parse_tiles(AVFormatContext *s)
         if (!loop)
             continue;
 
+        for (int j = 0; j < grid->item->nb_ref_item_list; j++) {
+            AVStream *st = grid->item->ref_item_list[j]->st;
+
+            if (!st)
+                continue;
+
+            err = avformat_stream_group_add_stream(stg, st);
+            if (err < 0 && err != AVERROR(EEXIST))
+                return err;
+        }
+
         switch (grid->item->type) {
         case MKTAG('g','r','i','d'):
             err = read_image_grid(s, grid, tile_grid);
@@ -10128,7 +10172,6 @@ static int mov_read_header(AVFormatContext *s)
 
     mov->fc = s;
     mov->trak_index = -1;
-    mov->thmb_item_id = -1;
     mov->primary_item_id = -1;
     mov->cur_item_id = -1;
     /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
@@ -10161,10 +10204,6 @@ static int mov_read_header(AVFormatContext *s)
             int64_t offset = 0;
 
             if (!item->st) {
-                if (item->item_id == mov->thmb_item_id) {
-                    av_log(s, AV_LOG_ERROR, "HEIF thumbnail doesn't reference a stream\n");
-                    return AVERROR_INVALIDDATA;
-                }
                 continue;
             }
             if (item->is_idat_relative) {
-- 
2.46.0



More information about the ffmpeg-devel mailing list