* [FFmpeg-devel] [PATCH] avformat: add container level Exif metadata support (PR #20321)
@ 2025-08-23 19:24 James Almer via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: James Almer via ffmpeg-devel @ 2025-08-23 19:24 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: James Almer
PR #20321 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20321
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20321.patch
Includes Exif packet side data, and starting with support in Heif.
From 962fea2d2cb56450d5325969cb655543895d4885 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 13:23:05 -0300
Subject: [PATCH 01/11] avcodec/packet: add an Exif side data type
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/avcodec.c | 1 +
libavcodec/options_table.h | 1 +
libavcodec/packet.c | 1 +
libavcodec/packet.h | 6 ++++++
4 files changed, 9 insertions(+)
diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
index 834b7ad242..2181439113 100644
--- a/libavcodec/avcodec.c
+++ b/libavcodec/avcodec.c
@@ -66,6 +66,7 @@ const SideDataMap ff_sd_global_map[] = {
{ AV_PKT_DATA_ICC_PROFILE, AV_FRAME_DATA_ICC_PROFILE },
{ AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT },
{ AV_PKT_DATA_3D_REFERENCE_DISPLAYS, AV_FRAME_DATA_3D_REFERENCE_DISPLAYS },
+ { AV_PKT_DATA_EXIF, AV_FRAME_DATA_EXIF },
{ AV_PKT_DATA_NB },
};
diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
index 25da169343..43aec402ba 100644
--- a/libavcodec/options_table.h
+++ b/libavcodec/options_table.h
@@ -406,6 +406,7 @@ static const AVOption avcodec_options[] = {
{"mastering_display_metadata", .default_val.i64 = AV_PKT_DATA_MASTERING_DISPLAY_METADATA, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" },
{"content_light_level", .default_val.i64 = AV_PKT_DATA_CONTENT_LIGHT_LEVEL, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" },
{"icc_profile", .default_val.i64 = AV_PKT_DATA_ICC_PROFILE, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" },
+ {"exif", .default_val.i64 = AV_PKT_DATA_EXIF, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" },
{NULL},
};
diff --git a/libavcodec/packet.c b/libavcodec/packet.c
index 2d1f282927..9420c10be0 100644
--- a/libavcodec/packet.c
+++ b/libavcodec/packet.c
@@ -310,6 +310,7 @@ const char *av_packet_side_data_name(enum AVPacketSideDataType type)
case AV_PKT_DATA_LCEVC: return "LCEVC NAL data";
case AV_PKT_DATA_3D_REFERENCE_DISPLAYS: return "3D Reference Displays Info";
case AV_PKT_DATA_RTCP_SR: return "RTCP Sender Report";
+ case AV_PKT_DATA_EXIF: return "EXIF metadata";
}
return NULL;
}
diff --git a/libavcodec/packet.h b/libavcodec/packet.h
index 55389a957d..5e27f9ceb5 100644
--- a/libavcodec/packet.h
+++ b/libavcodec/packet.h
@@ -362,6 +362,12 @@ enum AVPacketSideDataType {
*/
AV_PKT_DATA_RTCP_SR,
+ /**
+ * Extensible image file format metadata. The payload is a buffer containing
+ * EXIF metadata, starting with either 49 49 2a 00, or 4d 4d 00 2a.
+ */
+ AV_PKT_DATA_EXIF,
+
/**
* The number of side data types.
* This is not part of the public API/ABI in the sense that it may
--
2.49.1
From 01896153bb13272cb6e1f1fb3c95b47a6c79771c Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 22:00:33 -0300
Subject: [PATCH 02/11] ffprobe: print EXIF packet side data size
Signed-off-by: James Almer <jamrial@gmail.com>
---
fftools/ffprobe.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 018111318e..f4eb87aefa 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -1063,6 +1063,8 @@ static void print_pkt_side_data(AVTextFormatContext *tfc,
print_int("crop_right", AV_RL32(sd->data + 12));
} else if (sd->type == AV_PKT_DATA_AFD && sd->size > 0) {
print_int("active_format", *sd->data);
+ } else if (sd->type == AV_PKT_DATA_EXIF) {
+ print_int("size", sd->size);
}
}
--
2.49.1
From 3e4e14d3e80ae61cb7d7c9c8f5e60e786eefd87c Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 13:23:49 -0300
Subject: [PATCH 03/11] avformat/dump: print side data type names generically
Based on vf_showinfo behavior.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/dump.c | 45 +++++++++++++++------------------------------
1 file changed, 15 insertions(+), 30 deletions(-)
diff --git a/libavformat/dump.c b/libavformat/dump.c
index 02f69f9a3a..dfa33fd3c9 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -342,7 +342,7 @@ static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *s
{
const AVMasteringDisplayMetadata *metadata =
(const AVMasteringDisplayMetadata *)sd->data;
- av_log(ctx, log_level, "Mastering Display Metadata, "
+ av_log(ctx, log_level,
"has_primaries:%d has_luminance:%d "
"r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
"min_luminance=%f, max_luminance=%f",
@@ -362,7 +362,7 @@ static void dump_content_light_metadata(void *ctx, const AVPacketSideData *sd,
{
const AVContentLightMetadata *metadata =
(const AVContentLightMetadata *)sd->data;
- av_log(ctx, log_level, "Content Light Level Metadata, "
+ av_log(ctx, log_level,
"MaxCLL=%d, MaxFALL=%d",
metadata->MaxCLL, metadata->MaxFALL);
}
@@ -371,7 +371,7 @@ static void dump_ambient_viewing_environment_metadata(void *ctx, const AVPacketS
{
const AVAmbientViewingEnvironment *ambient =
(const AVAmbientViewingEnvironment *)sd->data;
- av_log(ctx, AV_LOG_INFO, "Ambient Viewing Environment, "
+ av_log(ctx, AV_LOG_INFO,
"ambient_illuminance=%f, ambient_light_x=%f, ambient_light_y=%f",
av_q2d(ambient->ambient_illuminance),
av_q2d(ambient->ambient_light_x),
@@ -481,81 +481,66 @@ static void dump_sidedata(void *ctx, const AVPacketSideData *side_data, int nb_s
for (i = 0; i < nb_side_data; i++) {
const AVPacketSideData *sd = &side_data[i];
- av_log(ctx, log_level, "%s ", indent);
+ const char *name = av_packet_side_data_name(sd->type);
+ av_log(ctx, log_level, "%s ", indent);
+ if (name)
+ av_log(ctx, AV_LOG_INFO, "%s: ", name);
switch (sd->type) {
- case AV_PKT_DATA_PALETTE:
- av_log(ctx, log_level, "palette");
- break;
- case AV_PKT_DATA_NEW_EXTRADATA:
- av_log(ctx, log_level, "new extradata");
- break;
case AV_PKT_DATA_PARAM_CHANGE:
- av_log(ctx, log_level, "paramchange: ");
dump_paramchange(ctx, sd, log_level);
break;
- case AV_PKT_DATA_H263_MB_INFO:
- av_log(ctx, log_level, "H.263 macroblock info");
- break;
case AV_PKT_DATA_REPLAYGAIN:
- av_log(ctx, log_level, "replaygain: ");
dump_replaygain(ctx, sd, log_level);
break;
case AV_PKT_DATA_DISPLAYMATRIX:
- av_log(ctx, log_level, "displaymatrix: rotation of %.2f degrees",
+ av_log(ctx, log_level, "rotation of %.2f degrees",
av_display_rotation_get((const int32_t *)sd->data));
break;
case AV_PKT_DATA_STEREO3D:
- av_log(ctx, log_level, "stereo3d: ");
dump_stereo3d(ctx, sd, log_level);
break;
case AV_PKT_DATA_AUDIO_SERVICE_TYPE:
- av_log(ctx, log_level, "audio service type: ");
dump_audioservicetype(ctx, sd, log_level);
break;
case AV_PKT_DATA_QUALITY_STATS:
- av_log(ctx, log_level, "quality factor: %"PRId32", pict_type: %c",
+ av_log(ctx, log_level, "%"PRId32", pict_type: %c",
AV_RL32(sd->data), av_get_picture_type_char(sd->data[4]));
break;
case AV_PKT_DATA_CPB_PROPERTIES:
- av_log(ctx, log_level, "cpb: ");
dump_cpb(ctx, sd, log_level);
break;
case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
dump_mastering_display_metadata(ctx, sd, log_level);
break;
case AV_PKT_DATA_SPHERICAL:
- av_log(ctx, log_level, "spherical: ");
dump_spherical(ctx, w, h, sd, log_level);
break;
case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
dump_content_light_metadata(ctx, sd, log_level);
break;
- case AV_PKT_DATA_ICC_PROFILE:
- av_log(ctx, log_level, "ICC Profile");
- break;
case AV_PKT_DATA_DOVI_CONF:
- av_log(ctx, log_level, "DOVI configuration record: ");
dump_dovi_conf(ctx, sd, log_level);
break;
case AV_PKT_DATA_S12M_TIMECODE:
- av_log(ctx, log_level, "SMPTE ST 12-1:2014: ");
dump_s12m_timecode(ctx, avg_frame_rate, sd, log_level);
break;
case AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT:
dump_ambient_viewing_environment_metadata(ctx, sd);
break;
case AV_PKT_DATA_FRAME_CROPPING:
- av_log(ctx, AV_LOG_INFO, "Frame cropping: ");
dump_cropping(ctx, sd);
break;
case AV_PKT_DATA_3D_REFERENCE_DISPLAYS:
- av_log(ctx, log_level, "3D Reference Displays Information: ");
dump_tdrdi(ctx, sd);
break;
default:
- av_log(ctx, log_level, "unknown side data type %d "
- "(%"SIZE_SPECIFIER" bytes)", sd->type, sd->size);
+ if (name)
+ av_log(ctx, log_level,
+ "(%"SIZE_SPECIFIER" bytes)", sd->size);
+ else
+ av_log(ctx, log_level, "unknown side data type %d "
+ "(%"SIZE_SPECIFIER" bytes)", sd->type, sd->size);
break;
}
--
2.49.1
From 7b6874c4395f69b1b4bd130b4b188a6a52a1fae2 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 21:59:25 -0300
Subject: [PATCH 04/11] tests/mov: also print stream side data in
fate-mov-heic-demux-still-image-multiple-thumb
This is in preparation for a following commit.
Signed-off-by: James Almer <jamrial@gmail.com>
---
tests/fate/mov.mak | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 421845969b..21c155ff2a 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -188,7 +188,7 @@ fate-mov-heic-demux-clap-irot-imir: CMD = stream_demux mov $(TARGET_SAMPLES)/hei
FATE_MOV_FFMPEG_FFPROBE_SAMPLES-$(call FRAMECRC, MOV, HEVC MJPEG, HEVC_PARSER) \
+= fate-mov-heic-demux-still-image-multiple-thumb
fate-mov-heic-demux-still-image-multiple-thumb: CMD = stream_demux mov $(TARGET_SAMPLES)/heif/P1001091.HIF "" "-c:v copy -map 0" \
- "-show_entries stream=index,id:stream_disposition"
+ "-show_entries stream=index,id:stream_disposition:stream_side_data_list"
# heic demuxing - still image with multiple items in a grid.
FATE_MOV_FFMPEG_FFPROBE_SAMPLES-$(call FRAMECRC, MOV, HEVC, HEVC_PARSER) \
--
2.49.1
From db113a531f1a16906888285edec82e8cbe912d89 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Sat, 23 Aug 2025 11:04:48 -0300
Subject: [PATCH 05/11] avformat/mov: reduce code duplication when setting tile
group properties
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/mov.c | 46 +++++++++++++++-------------------------------
1 file changed, 15 insertions(+), 31 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index e9a582e5aa..c846e0d4e8 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -10188,21 +10188,6 @@ static int read_image_grid(AVFormatContext *s, const HEIFGrid *grid,
tile_grid->width = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb);
tile_grid->height = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb);
- /* ICC profile */
- if (item->icc_profile_size) {
- int ret = set_icc_profile_from_item(&tile_grid->coded_side_data,
- &tile_grid->nb_coded_side_data, item);
- if (ret < 0)
- return ret;
- }
- /* rotation */
- if (item->rotation || item->hflip || item->vflip) {
- int ret = set_display_matrix_from_item(&tile_grid->coded_side_data,
- &tile_grid->nb_coded_side_data, item);
- if (ret < 0)
- return ret;
- }
-
av_log(c->fc, AV_LOG_TRACE, "grid: grid_rows %d grid_cols %d output_width %d output_height %d\n",
tile_rows, tile_cols, tile_grid->width, tile_grid->height);
@@ -10294,22 +10279,6 @@ static int read_image_iovl(AVFormatContext *s, const HEIFGrid *grid,
tile_grid->height =
tile_grid->coded_height = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb);
- /* rotation */
- if (item->rotation || item->hflip || item->vflip) {
- int ret = set_display_matrix_from_item(&tile_grid->coded_side_data,
- &tile_grid->nb_coded_side_data, item);
- if (ret < 0)
- return ret;
- }
-
- /* ICC profile */
- if (item->icc_profile_size) {
- int ret = set_icc_profile_from_item(&tile_grid->coded_side_data,
- &tile_grid->nb_coded_side_data, item);
- if (ret < 0)
- return ret;
- }
-
av_log(c->fc, AV_LOG_TRACE, "iovl: output_width %d, output_height %d\n",
tile_grid->width, tile_grid->height);
@@ -10421,6 +10390,21 @@ static int mov_parse_tiles(AVFormatContext *s)
if (err < 0)
return err;
+ /* rotation */
+ if (grid->item->rotation || grid->item->hflip || grid->item->vflip) {
+ err = set_display_matrix_from_item(&tile_grid->coded_side_data,
+ &tile_grid->nb_coded_side_data, grid->item);
+ if (err < 0)
+ return err;
+ }
+
+ /* ICC profile */
+ if (grid->item->icc_profile_size) {
+ err = set_icc_profile_from_item(&tile_grid->coded_side_data,
+ &tile_grid->nb_coded_side_data, grid->item);
+ if (err < 0)
+ return err;
+ }
if (grid->item->name)
av_dict_set(&stg->metadata, "title", grid->item->name, 0);
--
2.49.1
From a62d48c823cc7e81ff5b94caaa07db36cbf01d5f Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 17:07:50 -0300
Subject: [PATCH 06/11] avformat/mov: make items referencing items generic
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/isom.h | 9 +++++--
libavformat/mov.c | 67 +++++++++++++++++++++-------------------------
2 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 94c9c65989..1329cb9188 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -286,8 +286,15 @@ typedef struct MOVStreamContext {
int iamf_stream_offset;
} MOVStreamContext;
+typedef struct HEIFItemRef {
+ unsigned type;
+ int item_id;
+} HEIFItemRef;
+
typedef struct HEIFItem {
AVStream *st;
+ HEIFItemRef *iref_list;
+ int nb_iref_list;
char *name;
int item_id;
int64_t extent_length;
@@ -376,8 +383,6 @@ typedef struct MOVContext {
int nb_heif_item;
HEIFGrid *heif_grid;
int nb_heif_grid;
- int* thmb_item_id;
- int nb_thmb_item;
int64_t idat_offset;
int interleaved_read;
} MOVContext;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index c846e0d4e8..2944daf97c 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -188,14 +188,14 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len,
}
/**
- * Get the current item in the parsing process.
+ * Get the requested item.
*/
-static HEIFItem *heif_cur_item(MOVContext *c)
+static HEIFItem *get_heif_item(MOVContext *c, unsigned id)
{
HEIFItem *item = NULL;
for (int i = 0; i < c->nb_heif_item; i++) {
- if (!c->heif_item[i] || c->heif_item[i]->item_id != c->cur_item_id)
+ if (!c->heif_item[i] || c->heif_item[i]->item_id != id)
continue;
item = c->heif_item[i];
@@ -220,7 +220,7 @@ static AVStream *get_curr_st(MOVContext *c)
if (c->cur_item_id == -1)
return c->fc->streams[c->fc->nb_streams-1];
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
if (item)
st = item->st;
@@ -1245,7 +1245,7 @@ static int mov_read_clap(MOVContext *c, AVIOContext *pb, MOVAtom atom)
AVRational pc_x, pc_y;
uint64_t top, bottom, left, right;
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
st = get_curr_st(c);
if (!st)
return 0;
@@ -2078,7 +2078,7 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
st = get_curr_st(c);
if (!st) {
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
if (!item)
return 0;
}
@@ -9087,30 +9087,33 @@ static int mov_read_iref_dimg(MOVContext *c, AVIOContext *pb, int version)
static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version)
{
- int *thmb_item_id;
+ 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);
+ const HEIFItemRef ref = { MKTAG('t','h','m','b'), from_item_id };
+
+ from_item = get_heif_item(c, from_item_id);
+ if (!from_item) {
+ av_log(c->fc, AV_LOG_ERROR, "Missing stream referenced by thmb item\n");
+ return AVERROR_INVALIDDATA;
+ }
entries = avio_rb16(pb);
- if (entries > 1) {
- avpriv_request_sample(c->fc, "thmb in iref referencing several items");
- return AVERROR_PATCHWELCOME;
- }
/* 'to' item ids */
- to_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
+ for (int i = 0; i < entries; i++) {
+ HEIFItem *item = get_heif_item(c, version ? avio_rb32(pb) : avio_rb16(pb));
+ if (!item) {
+ av_log(c->fc, AV_LOG_WARNING, "Missing stream referenced by thmb item\n");
+ continue;
+ }
- if (to_item_id != c->primary_item_id)
- return 0;
+ if (!av_dynarray2_add((void **)&item->iref_list, &item->nb_iref_list,
+ sizeof(*item->iref_list), (const uint8_t *)&ref))
+ return AVERROR(ENOMEM);
+ }
- /* Put thumnbail IDs into an array */
- thmb_item_id = av_dynarray2_add((void **)&c->thmb_item_id, &c->nb_thmb_item,
- sizeof(*c->thmb_item_id),
- (const void *)&from_item_id);
- if (!thmb_item_id)
- return AVERROR(ENOMEM);
-
- av_log(c->fc, AV_LOG_TRACE, "thmb: from_item_id %d, entries %d, nb_thmb: %d\n",
- from_item_id, entries, c->nb_thmb_item);
+ av_log(c->fc, AV_LOG_TRACE, "thmb: from_item_id %d, entries %d\n",
+ from_item_id, entries);
return 0;
}
@@ -9166,7 +9169,7 @@ static int mov_read_ispe(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_TRACE, "ispe: item_id %d, width %u, height %u\n",
c->cur_item_id, width, height);
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
if (item) {
item->width = width;
item->height = height;
@@ -9185,7 +9188,7 @@ static int mov_read_irot(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_TRACE, "irot: item_id %d, angle %u\n",
c->cur_item_id, angle);
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
if (item) {
// angle * 90 specifies the angle (in anti-clockwise direction)
// in units of degrees.
@@ -9205,7 +9208,7 @@ static int mov_read_imir(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_TRACE, "imir: item_id %d, axis %u\n",
c->cur_item_id, axis);
- item = heif_cur_item(c);
+ item = get_heif_item(c, c->cur_item_id);
if (item) {
item->hflip = axis;
item->vflip = !axis;
@@ -9970,6 +9973,7 @@ static int mov_read_close(AVFormatContext *s)
if (!mov->heif_item[i])
continue;
av_freep(&mov->heif_item[i]->name);
+ av_freep(&mov->heif_item[i]->iref_list);
av_freep(&mov->heif_item[i]->icc_profile);
av_freep(&mov->heif_item[i]);
}
@@ -9980,7 +9984,6 @@ static int mov_read_close(AVFormatContext *s)
av_freep(&mov->heif_grid[i].tile_item_list);
}
av_freep(&mov->heif_grid);
- av_freep(&mov->thmb_item_id);
return 0;
}
@@ -10429,13 +10432,6 @@ static int mov_parse_heif_items(AVFormatContext *s)
if (!item)
continue;
if (!item->st) {
- for (int j = 0; j < mov->nb_thmb_item; j++) {
- if (item->item_id == mov->thmb_item_id[j]) {
- av_log(s, AV_LOG_ERROR, "HEIF thumbnail ID %d doesn't reference a stream\n",
- item->item_id);
- return AVERROR_INVALIDDATA;
- }
- }
continue;
}
if (item->is_idat_relative) {
@@ -10587,7 +10583,6 @@ static int mov_read_header(AVFormatContext *s)
mov->fc = s;
mov->trak_index = -1;
- mov->thmb_item_id = NULL;
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) */
--
2.49.1
From c5d13a638f77131174233348e0fd96079be95e42 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 20 Aug 2025 19:53:53 -0300
Subject: [PATCH 07/11] avformat/mov: export Exif metadata from HEIF streams
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/mov.c | 133 +++++++++++++++++-
.../mov-heic-demux-still-image-multiple-thumb | 4 +
2 files changed, 131 insertions(+), 6 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 2944daf97c..4abd7e9397 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -51,6 +51,7 @@
#include "libavutil/timecode.h"
#include "libavutil/uuid.h"
#include "libavcodec/ac3tab.h"
+#include "libavcodec/exif.h"
#include "libavcodec/flac.h"
#include "libavcodec/hevc/hevc.h"
#include "libavcodec/mpegaudiodecheader.h"
@@ -9085,12 +9086,12 @@ static int mov_read_iref_dimg(MOVContext *c, AVIOContext *pb, int version)
return 0;
}
-static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version)
+static int mov_read_iref_cdsc(MOVContext *c, AVIOContext *pb, unsigned type, int version)
{
HEIFItem *from_item = NULL;
int entries;
int from_item_id = version ? avio_rb32(pb) : avio_rb16(pb);
- const HEIFItemRef ref = { MKTAG('t','h','m','b'), from_item_id };
+ const HEIFItemRef ref = { type, from_item_id };
from_item = get_heif_item(c, from_item_id);
if (!from_item) {
@@ -9103,7 +9104,8 @@ static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version)
for (int i = 0; i < entries; i++) {
HEIFItem *item = get_heif_item(c, version ? avio_rb32(pb) : avio_rb16(pb));
if (!item) {
- av_log(c->fc, AV_LOG_WARNING, "Missing stream referenced by thmb item\n");
+ av_log(c->fc, AV_LOG_WARNING, "Missing stream referenced by %s item\n",
+ av_fourcc2str(type));
continue;
}
@@ -9112,8 +9114,8 @@ static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version)
return AVERROR(ENOMEM);
}
- av_log(c->fc, AV_LOG_TRACE, "thmb: from_item_id %d, entries %d\n",
- from_item_id, entries);
+ av_log(c->fc, AV_LOG_TRACE, "%s: from_item_id %d, entries %d\n",
+ av_fourcc2str(type), from_item_id, entries);
return 0;
}
@@ -9139,11 +9141,14 @@ static int mov_read_iref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
next += size - 4;
type = avio_rl32(pb);
switch (type) {
+ case MKTAG('c','d','s','c'):
+ mov_read_iref_cdsc(c, pb, type, version);
+ break;
case MKTAG('d','i','m','g'):
mov_read_iref_dimg(c, pb, version);
break;
case MKTAG('t','h','m','b'):
- mov_read_iref_thmb(c, pb, version);
+ mov_read_iref_cdsc(c, pb, type, version);
break;
default:
av_log(c->fc, AV_LOG_DEBUG, "Unknown iref type %s size %"PRIu32"\n",
@@ -10308,6 +10313,90 @@ fail:
return ret;
}
+static int mov_parse_exif_item(AVFormatContext *s,
+ AVPacketSideData **coded_side_data, int *nb_coded_side_data,
+ const HEIFItem *ref)
+{
+ MOVContext *c = s->priv_data;
+ AVPacketSideData *sd;
+ AVExifMetadata ifd = { 0 };
+ AVExifEntry *entry = NULL;
+ AVBufferRef *buf;
+ int64_t offset = 0, pos = avio_tell(s->pb);
+ unsigned orientation_id = av_exif_get_tag_id("Orientation");
+ int err;
+
+ if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
+ av_log(c->fc, AV_LOG_WARNING, "Exif metadata with non seekable input\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ if (ref->is_idat_relative) {
+ if (!c->idat_offset) {
+ av_log(c->fc, AV_LOG_ERROR, "missing idat box required by the Exif metadata\n");
+ return AVERROR_INVALIDDATA;
+ }
+ offset = c->idat_offset;
+ }
+
+ buf = av_buffer_alloc(ref->extent_length);
+ if (!buf)
+ return AVERROR(ENOMEM);
+
+ avio_seek(s->pb, ref->extent_offset + offset, SEEK_SET);
+ err = avio_read(s->pb, buf->data, ref->extent_length);
+ if (err != ref->extent_length) {
+ if (err > 0)
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ // 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);
+ if (err < 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to parse Exif metadata\n");
+ goto fail;
+ }
+
+ err = av_exif_get_entry(s, &ifd, orientation_id, 0, &entry);
+ if (err < 0)
+ goto fail;
+ else if (!err)
+ goto finish;
+
+ err = av_exif_remove_entry(s, &ifd, orientation_id, 0);
+ if (err < 0)
+ goto fail;
+
+ av_buffer_unref(&buf);
+ err = av_exif_write(s, &ifd, &buf, AV_EXIF_T_OFF);
+ if (err < 0)
+ goto fail;
+
+finish:
+ offset = AV_RB32(buf->data) + 4;
+ if (offset >= buf->size) {
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ 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;
+ }
+ 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);
+
+ return err;
+}
+
static int mov_parse_tiles(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
@@ -10393,6 +10482,22 @@ static int mov_parse_tiles(AVFormatContext *s)
if (err < 0)
return err;
+ for (int j = 0; j < grid->item->nb_iref_list; j++) {
+ HEIFItem *ref = get_heif_item(mov, grid->item->iref_list[j].item_id);
+
+ av_assert0(ref);
+ switch(ref->type) {
+ case MKTAG('E','x','i','f'):
+ err = mov_parse_exif_item(s, &tile_grid->coded_side_data,
+ &tile_grid->nb_coded_side_data, ref);
+ if (err < 0 && (s->error_recognition & AV_EF_EXPLODE))
+ return err;
+ break;
+ default:
+ break;
+ }
+ }
+
/* rotation */
if (grid->item->rotation || grid->item->hflip || grid->item->vflip) {
err = set_display_matrix_from_item(&tile_grid->coded_side_data,
@@ -10459,6 +10564,22 @@ static int mov_parse_heif_items(AVFormatContext *s)
if (item->item_id == mov->primary_item_id)
st->disposition |= AV_DISPOSITION_DEFAULT;
+ for (int j = 0; j < item->nb_iref_list; j++) {
+ HEIFItem *ref = get_heif_item(mov, item->iref_list[j].item_id);
+
+ av_assert0(ref);
+ switch(ref->type) {
+ case MKTAG('E','x','i','f'):
+ err = mov_parse_exif_item(s, &st->codecpar->coded_side_data,
+ &st->codecpar->nb_coded_side_data, ref);
+ if (err < 0 && (s->error_recognition & AV_EF_EXPLODE))
+ return err;
+ break;
+ default:
+ break;
+ }
+ }
+
if (item->rotation || item->hflip || item->vflip) {
err = set_display_matrix_from_item(&st->codecpar->coded_side_data,
&st->codecpar->nb_coded_side_data, item);
diff --git a/tests/ref/fate/mov-heic-demux-still-image-multiple-thumb b/tests/ref/fate/mov-heic-demux-still-image-multiple-thumb
index 88263f0d0b..e5764f6de8 100644
--- a/tests/ref/fate/mov-heic-demux-still-image-multiple-thumb
+++ b/tests/ref/fate/mov-heic-demux-still-image-multiple-thumb
@@ -39,6 +39,10 @@ DISPOSITION:metadata=0
DISPOSITION:dependent=0
DISPOSITION:still_image=0
DISPOSITION:multilayer=0
+[SIDE_DATA]
+side_data_type=EXIF metadata
+size=30308
+[/SIDE_DATA]
[/STREAM]
[STREAM]
index=1
--
2.49.1
From 6e776057f868d4ceaff41719d74c657b3c58f9a1 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Fri, 22 Aug 2025 20:30:40 -0300
Subject: [PATCH 08/11] avcodec/decode: use av_exif_get_tag_id() where useful
Removes dependency on exif_internal.h
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/decode.c | 5 +++--
libavcodec/exif.c | 7 +++++++
libavcodec/exif_internal.h | 8 --------
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index b0ce94cadd..2877010682 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -47,7 +47,7 @@
#include "codec_desc.h"
#include "codec_internal.h"
#include "decode.h"
-#include "exif_internal.h"
+#include "exif.h"
#include "hwaccel_internal.h"
#include "hwconfig.h"
#include "internal.h"
@@ -2278,7 +2278,8 @@ static int exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMe
for (size_t i = 0; i < ifd->count; i++) {
const AVExifEntry *entry = &ifd->entries[i];
- if (entry->id == ORIENTATION_TAG && entry->count > 0 && entry->type == AV_TIFF_SHORT) {
+ if (entry->id == av_exif_get_tag_id("Orientation") &&
+ entry->count > 0 && entry->type == AV_TIFF_SHORT) {
orient = entry;
break;
}
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 1332fa68bb..2359566a47 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -46,6 +46,13 @@
#define IFD_EXTRA_SIZE 6
#define EXIF_TAG_NAME_LENGTH 32
+#define MAKERNOTE_TAG 0x927c
+#define ORIENTATION_TAG 0x112
+#define EXIFIFD_TAG 0x8769
+#define IMAGE_WIDTH_TAG 0x100
+#define IMAGE_LENGTH_TAG 0x101
+#define PIXEL_X_TAG 0xa002
+#define PIXEL_Y_TAG 0xa003
struct exif_tag {
const char name[EXIF_TAG_NAME_LENGTH];
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
index 565e747353..0169fb3e00 100644
--- a/libavcodec/exif_internal.h
+++ b/libavcodec/exif_internal.h
@@ -42,14 +42,6 @@ int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
int le, int depth, AVDictionary **metadata);
#endif /* FF_API_OLD_EXIF */
-#define MAKERNOTE_TAG 0x927c
-#define ORIENTATION_TAG 0x112
-#define EXIFIFD_TAG 0x8769
-#define IMAGE_WIDTH_TAG 0x100
-#define IMAGE_LENGTH_TAG 0x101
-#define PIXEL_X_TAG 0xa002
-#define PIXEL_Y_TAG 0xa003
-
/**
* Compares values in the IFD with data in the provided AVFrame and sets the values
* in that IFD to match the ones in that AVFrame. This is mostly useful for an
--
2.49.1
From a0c32a9d6db522c46f9fe055669532576d7cb4a9 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Thu, 21 Aug 2025 20:03:03 -0300
Subject: [PATCH 09/11] avcodec/decode: use ff_frame_new_side_data() to export
Exif side data
Otherwise, the user requested priority of packet side data will be ignored.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/decode.c | 29 +++++++++++++----------------
libavcodec/decode.h | 7 +++----
libavcodec/pngdec.c | 5 +----
libavcodec/webp.c | 4 +---
4 files changed, 18 insertions(+), 27 deletions(-)
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 2877010682..fa76dba51c 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -2268,12 +2268,10 @@ static int attach_displaymatrix(AVCodecContext *avctx, AVFrame *frame, int orien
return ret;
}
-static int exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMetadata *ifd, AVBufferRef *og)
+static int exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMetadata *ifd, AVBufferRef **pbuf)
{
const AVExifEntry *orient = NULL;
- AVFrameSideData *sd;
AVExifMetadata *cloned = NULL;
- AVBufferRef *written = NULL;
int ret;
for (size_t i = 0; i < ifd->count; i++) {
@@ -2305,25 +2303,21 @@ static int exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMe
if (ret < 0)
goto end;
- if (cloned || !og) {
- ret = av_exif_write(avctx, ifd, &written, AV_EXIF_TIFF_HEADER);
+ if (cloned || !*pbuf) {
+ av_buffer_unref(pbuf);
+ ret = av_exif_write(avctx, ifd, pbuf, AV_EXIF_TIFF_HEADER);
if (ret < 0)
goto end;
}
- sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_EXIF, written ? written : og);
- if (!sd) {
- if (written)
- av_buffer_unref(&written);
- ret = AVERROR(ENOMEM);
+ ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_EXIF, pbuf);
+ if (ret < 0)
goto end;
- }
ret = 0;
end:
- if (og && written && ret >= 0)
- av_buffer_unref(&og); // as though we called new_side_data on og;
+ av_buffer_unref(pbuf);
av_exif_free(cloned);
av_free(cloned);
return ret;
@@ -2331,22 +2325,25 @@ end:
int ff_decode_exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMetadata *ifd)
{
- return exif_attach_ifd(avctx, frame, ifd, NULL);
+ AVBufferRef *dummy = NULL;
+ return exif_attach_ifd(avctx, frame, ifd, &dummy);
}
-int ff_decode_exif_attach_buffer(AVCodecContext *avctx, AVFrame *frame, AVBufferRef *data,
+int ff_decode_exif_attach_buffer(AVCodecContext *avctx, AVFrame *frame, AVBufferRef **pbuf,
enum AVExifHeaderMode header_mode)
{
int ret;
+ AVBufferRef *data = *pbuf;
AVExifMetadata ifd = { 0 };
ret = av_exif_parse_buffer(avctx, data->data, data->size, &ifd, header_mode);
if (ret < 0)
goto end;
- ret = exif_attach_ifd(avctx, frame, &ifd, data);
+ ret = exif_attach_ifd(avctx, frame, &ifd, pbuf);
end:
+ av_buffer_unref(pbuf);
av_exif_free(&ifd);
return ret;
}
diff --git a/libavcodec/decode.h b/libavcodec/decode.h
index f285da924d..561dc6a69c 100644
--- a/libavcodec/decode.h
+++ b/libavcodec/decode.h
@@ -230,11 +230,10 @@ enum AVExifHeaderMode;
* attaches that information as an AV_FRAME_DATA_DISPLAYMATRIX instead
* of including it in the AV_FRAME_DATA_EXIF side data buffer.
*
- * On a success, the caller loses ownership of the data buffer. Either it is
- * unrefed, or its ownership is transferred to the frame directly. On failure,
- * the data buffer is left owned by the caller.
+ * *buf is ALWAYS consumed by this function and NULL written in its place, even
+ * on failure.
*/
-int ff_decode_exif_attach_buffer(AVCodecContext *avctx, AVFrame *frame, AVBufferRef *data,
+int ff_decode_exif_attach_buffer(AVCodecContext *avctx, AVFrame *frame, AVBufferRef **buf,
enum AVExifHeaderMode header_mode);
struct AVExifMetadata;
diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
index 8a63f0ad90..ce817d44fa 100644
--- a/libavcodec/pngdec.c
+++ b/libavcodec/pngdec.c
@@ -1753,15 +1753,12 @@ exit_loop:
if (s->exif_data) {
// we swap because ff_decode_exif_attach_buffer adds to p->metadata
FFSWAP(AVDictionary *, p->metadata, s->frame_metadata);
- ret = ff_decode_exif_attach_buffer(avctx, p, s->exif_data, AV_EXIF_TIFF_HEADER);
+ ret = ff_decode_exif_attach_buffer(avctx, p, &s->exif_data, AV_EXIF_TIFF_HEADER);
FFSWAP(AVDictionary *, p->metadata, s->frame_metadata);
if (ret < 0) {
av_log(avctx, AV_LOG_WARNING, "unable to attach EXIF buffer\n");
return ret;
}
- // ff_decode_exif_attach_buffer takes ownership so
- // we do not want to call av_buffer_unref here
- s->exif_data = NULL;
}
if (s->color_type == PNG_COLOR_TYPE_PALETTE && avctx->codec_id == AV_CODEC_ID_APNG) {
diff --git a/libavcodec/webp.c b/libavcodec/webp.c
index 0dca130ff3..796f089437 100644
--- a/libavcodec/webp.c
+++ b/libavcodec/webp.c
@@ -1476,11 +1476,9 @@ FF_ENABLE_DEPRECATION_WARNINGS
s->has_exif = 1;
memcpy(exif_buf->data, gb.buffer, chunk_size);
- /* if this succeeds then exif_buf is either freed or transferred to the AVFrame */
- ret = ff_decode_exif_attach_buffer(avctx, p, exif_buf, AV_EXIF_TIFF_HEADER);
+ ret = ff_decode_exif_attach_buffer(avctx, p, &exif_buf, AV_EXIF_TIFF_HEADER);
if (ret < 0) {
av_log(avctx, AV_LOG_WARNING, "unable to attach EXIF buffer\n");
- av_buffer_unref(&exif_buf);
}
exif_end:
--
2.49.1
From 91b006364075467817a176c6280a528f1df6bd62 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Thu, 21 Aug 2025 21:44:27 -0300
Subject: [PATCH 10/11] avcodec/decode: parse Exif packet side data before
passing it to frames
Extract Orientation and export it as a display matrix if present, and set the
frame's metadata with the remaining Exif entries.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/decode.c | 87 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 83 insertions(+), 4 deletions(-)
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index fa76dba51c..995d92c630 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1389,6 +1389,75 @@ static int side_data_stereo3d_merge(AVFrameSideData *sd_frame,
return 0;
}
+static int side_data_exif_parse(AVFrame *dst, const AVPacketSideData *sd_pkt)
+{
+ AVExifMetadata ifd = { 0 };
+ AVExifEntry *entry = NULL;
+ AVBufferRef *buf = NULL;
+ AVFrameSideData *sd_frame;
+ int ret;
+
+ ret = av_exif_parse_buffer(NULL, sd_pkt->data, sd_pkt->size, &ifd,
+ AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ return ret;
+
+ ret = av_exif_get_entry(NULL, &ifd, av_exif_get_tag_id("Orientation"), 0, &entry);
+ if (ret < 0)
+ goto end;
+
+ if (!entry) {
+ ret = av_exif_ifd_to_dict(NULL, &ifd, &dst->metadata);
+ if (ret < 0)
+ goto end;
+
+ sd_frame = av_frame_side_data_new(&dst->side_data, &dst->nb_side_data, AV_FRAME_DATA_EXIF,
+ sd_pkt->size, 0);
+ if (sd_frame)
+ memcpy(sd_frame->data, sd_pkt->data, sd_pkt->size);
+ ret = sd_frame ? 0 : AVERROR(ENOMEM);
+
+ goto end;
+ }
+
+ // If a display matrix already exists in the frame, give it priority
+ if (av_frame_side_data_get(dst->side_data, dst->nb_side_data, AV_FRAME_DATA_DISPLAYMATRIX))
+ goto finish;
+
+ sd_frame = av_frame_side_data_new(&dst->side_data, &dst->nb_side_data, AV_FRAME_DATA_DISPLAYMATRIX,
+ sizeof(int32_t) * 9, 0);
+ if (!sd_frame) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ ret = av_exif_orientation_to_matrix((int32_t *)sd_frame->data, entry->value.uint[0]);
+ if (ret < 0)
+ goto end;
+
+finish:
+ av_exif_remove_entry(NULL, &ifd, entry->id, 0);
+
+ ret = av_exif_ifd_to_dict(NULL, &ifd, &dst->metadata);
+ if (ret < 0)
+ goto end;
+
+ ret = av_exif_write(NULL, &ifd, &buf, AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ goto end;
+
+ if (!av_frame_side_data_add(&dst->side_data, &dst->nb_side_data, AV_FRAME_DATA_EXIF, &buf, 0)) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ ret = 0;
+end:
+ av_buffer_unref(&buf);
+ av_exif_free(&ifd);
+ return ret;
+}
+
static int side_data_map(AVFrame *dst,
const AVPacketSideData *sd_src, int nb_sd_src,
const SideDataMap *map)
@@ -1415,11 +1484,21 @@ static int side_data_map(AVFrame *dst,
continue;
}
- sd_frame = av_frame_new_side_data(dst, type_frame, sd_pkt->size);
- if (!sd_frame)
- return AVERROR(ENOMEM);
+ switch (type_pkt) {
+ case AV_PKT_DATA_EXIF: {
+ int ret = side_data_exif_parse(dst, sd_pkt);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ default:
+ sd_frame = av_frame_new_side_data(dst, type_frame, sd_pkt->size);
+ if (!sd_frame)
+ return AVERROR(ENOMEM);
- memcpy(sd_frame->data, sd_pkt->data, sd_pkt->size);
+ memcpy(sd_frame->data, sd_pkt->data, sd_pkt->size);
+ break;
+ }
}
return 0;
--
2.49.1
From 533845d0ccd8368e09bacc2d48288efaa29171ed Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Thu, 21 Aug 2025 23:39:58 -0300
Subject: [PATCH 11/11] avcodec/decode: always extract display matrix from Exif
in frames
And ensure decoders don't export both Exif metadata containing an Orientation
tag as well as a display matrix from some other source.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/decode.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 995d92c630..b013f99ddb 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -2362,8 +2362,9 @@ static int exif_attach_ifd(AVCodecContext *avctx, AVFrame *frame, const AVExifMe
}
}
- if (orient && orient->value.uint[0] > 1) {
- av_log(avctx, AV_LOG_DEBUG, "found nontrivial EXIF orientation: %" PRIu64 "\n", orient->value.uint[0]);
+ if (orient) {
+ av_assert0(!av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX));
+ av_log(avctx, AV_LOG_DEBUG, "found EXIF orientation: %" PRIu64 "\n", orient->value.uint[0]);
ret = attach_displaymatrix(avctx, frame, orient->value.uint[0]);
if (ret < 0) {
av_log(avctx, AV_LOG_WARNING, "unable to attach displaymatrix from EXIF\n");
--
2.49.1
_______________________________________________
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".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-08-23 19:24 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-23 19:24 [FFmpeg-devel] [PATCH] avformat: add container level Exif metadata support (PR #20321) James Almer via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git