Commit 06af0fdbf7 for ffmpeg

commit 06af0fdbf72c917b80cffec4b06c49a5f882a512
Author: James Almer <jamrial@gmail.com>
Date:   Fri Oct 9 15:15:41 2026 -0300

    avformat/mov: split parsing and applying Exif item metadata

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

diff --git a/libavformat/isom.h b/libavformat/isom.h
index dad5acf961..97d41b567f 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -33,6 +33,7 @@
 #include "libavutil/spherical.h"
 #include "libavutil/stereo3d.h"

+#include "libavcodec/exif.h"
 #include "avio.h"
 #include "internal.h"
 #include "dv.h"
@@ -312,6 +313,7 @@ typedef struct HEIFItem {
     int vflip;
     int type;
     int is_idat_relative;
+    AVExifMetadata ifd;
     uint8_t *icc_profile;
     size_t icc_profile_size;
 } HEIFItem;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 6d2f34ce00..9cec6255cd 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -10836,6 +10836,7 @@ static int mov_read_close(AVFormatContext *s)
         av_freep(&mov->heif_item[i]->name);
         av_freep(&mov->heif_item[i]->iref_list);
         av_freep(&mov->heif_item[i]->icc_profile);
+        av_exif_free(&mov->heif_item[i]->ifd);
         av_freep(&mov->heif_item[i]);
     }
     av_freep(&mov->heif_item);
@@ -10986,6 +10987,38 @@ fail:
     return ret;
 }

+static int set_exif_from_item(AVFormatContext *s,
+                              AVPacketSideData **coded_side_data, int *nb_coded_side_data,
+                              const HEIFItem *item)
+{
+    AVPacketSideData *sd;
+    const AVExifMetadata *ifd = &item->ifd;
+    AVBufferRef *buf = NULL;
+    int64_t offset;
+    int err;
+
+    err = av_exif_write(s, ifd, &buf, AV_EXIF_T_OFF);
+    if (err < 0)
+        return err;
+
+    offset = AV_RB32(buf->data) + 4;
+    if (offset >= buf->size) {
+        av_buffer_unref(&buf);
+        return AVERROR_INVALIDDATA;
+    }
+    sd = av_packet_side_data_new(coded_side_data, nb_coded_side_data,
+                                 AV_PKT_DATA_EXIF, buf->size - offset, 0);
+    if (!sd) {
+        av_buffer_unref(&buf);
+        return AVERROR(ENOMEM);
+    }
+    memcpy(sd->data, buf->data + offset, buf->size - offset);
+
+    av_buffer_unref(&buf);
+
+    return 0;
+}
+
 static int set_icc_profile_from_item(AVPacketSideData **coded_side_data, int *nb_coded_side_data,
                                      const HEIFItem *item)
 {
@@ -11185,14 +11218,11 @@ fail:
     return ret;
 }

-static int mov_parse_exif_item(AVFormatContext *s,
-                               AVPacketSideData **coded_side_data, int *nb_coded_side_data,
-                               const HEIFItem *ref)
+static int mov_parse_exif_item(AVFormatContext *s, HEIFItem *item, const HEIFItem *ref)
 {
     MOVContext *c = s->priv_data;
-    AVPacketSideData *sd;
-    AVExifMetadata ifd = { 0 };
-    AVBufferRef *buf;
+    AVExifMetadata *ifd = &item->ifd;
+    uint8_t *buf;
     int64_t offset = 0, pos = avio_tell(s->pb);
     unsigned orientation_id = av_exif_get_tag_id("Orientation");
     int err;
@@ -11209,7 +11239,7 @@ static int mov_parse_exif_item(AVFormatContext *s,
         offset = c->idat_offset;
     }

-    buf = av_buffer_alloc(ref->extent_length);
+    buf = av_malloc(ref->extent_length);
     if (!buf)
         return AVERROR(ENOMEM);

@@ -11219,7 +11249,7 @@ static int mov_parse_exif_item(AVFormatContext *s,
     }

     avio_seek(s->pb, ref->extent_offset + offset, SEEK_SET);
-    err = avio_read(s->pb, buf->data, ref->extent_length);
+    err = avio_read(s->pb, buf, ref->extent_length);
     if (err != ref->extent_length) {
         if (err > 0)
             err = AVERROR_INVALIDDATA;
@@ -11229,44 +11259,54 @@ static int mov_parse_exif_item(AVFormatContext *s,
     // HEIF spec states that Exif metadata is informative. The irot item property is
     // the normative source of rotation information. So we remove any Orientation tag
     // present in the Exif buffer.
-    err = av_exif_parse_buffer(s, buf->data, ref->extent_length, &ifd, AV_EXIF_T_OFF);
+    err = av_exif_parse_buffer(s, buf, ref->extent_length, ifd, AV_EXIF_T_OFF);
     if (err < 0) {
         av_log(s, AV_LOG_ERROR, "Unable to parse Exif metadata\n");
         goto fail;
     }

-    err = av_exif_remove_entry(s, &ifd, orientation_id, 0);
+    err = av_exif_remove_entry(s, ifd, orientation_id, 0);
     if (err < 0)
         goto fail;
-    else if (!err)
-        goto finish;

-    av_buffer_unref(&buf);
-    err = av_exif_write(s, &ifd, &buf, AV_EXIF_T_OFF);
+    err = 0;
+fail:
+    av_free(buf);
     if (err < 0)
-        goto fail;
+        av_exif_free(ifd);
+    avio_seek(s->pb, pos, SEEK_SET);

-finish:
-    offset = AV_RB32(buf->data) + 4;
-    if (offset >= buf->size) {
-        err = AVERROR_INVALIDDATA;
-        goto fail;
+    return err;
+}
+
+static int mov_apply_heif_item_references(AVFormatContext *s, HEIFItem *item,
+                                          AVPacketSideData **coded_side_data,
+                                          int *nb_coded_side_data)
+{
+    int err;
+
+    /* exif */
+    if (item->ifd.count) {
+        err = set_exif_from_item(s, coded_side_data, nb_coded_side_data, item);
+        if (err < 0)
+            return err;
     }
-    sd = av_packet_side_data_new(coded_side_data, nb_coded_side_data,
-                                 AV_PKT_DATA_EXIF, buf->size - offset, 0);
-    if (!sd) {
-        err = AVERROR(ENOMEM);
-        goto fail;
+
+    /* rotation */
+    if (item->rotation || item->hflip || item->vflip) {
+        err = set_display_matrix_from_item(coded_side_data, nb_coded_side_data, item);
+        if (err < 0)
+            return err;
     }
-    memcpy(sd->data, buf->data + offset, buf->size - offset);

-    err = 0;
-fail:
-    av_buffer_unref(&buf);
-    av_exif_free(&ifd);
-    avio_seek(s->pb, pos, SEEK_SET);
+    /* ICC profile */
+    if (item->icc_profile_size) {
+        err = set_icc_profile_from_item(coded_side_data, nb_coded_side_data, item);
+        if (err < 0)
+            return err;
+    }

-    return err;
+    return 0;
 }

 static int mov_parse_heif_item_references(AVFormatContext *s, HEIFItem *item,
@@ -11282,7 +11322,7 @@ static int mov_parse_heif_item_references(AVFormatContext *s, HEIFItem *item,
         av_assert0(ref);
         switch(ref->type) {
         case MKTAG('E','x','i','f'):
-            err = mov_parse_exif_item(s, coded_side_data, nb_coded_side_data, ref);
+            err = mov_parse_exif_item(s, item, ref);
             if (err < 0 && (s->error_recognition & AV_EF_EXPLODE))
                 return err;
             break;
@@ -11291,19 +11331,9 @@ static int mov_parse_heif_item_references(AVFormatContext *s, HEIFItem *item,
         }
     }

-    /* rotation */
-    if (item->rotation || item->hflip || item->vflip) {
-        err = set_display_matrix_from_item(coded_side_data, nb_coded_side_data, item);
-        if (err < 0)
-            return err;
-    }
-
-    /* ICC profile */
-    if (item->icc_profile_size) {
-        err = set_icc_profile_from_item(coded_side_data, nb_coded_side_data, item);
-        if (err < 0)
-            return err;
-    }
+    err = mov_apply_heif_item_references(s, item, coded_side_data, nb_coded_side_data);
+    if (err < 0)
+        return err;

     return 0;
 }