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;
}