ffmpeg-cvslog
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
August 2025
- 2 participants
- 290 discussions
[ffmpeg] branch master updated. d151d3aecb avcodec/exif: don't expose a private function in an installed header
by ffmpeg-git@ffmpeg.org 20 Aug '25
by ffmpeg-git@ffmpeg.org 20 Aug '25
20 Aug '25
The branch, master has been updated
via d151d3aecbe2a478c44be7faca56caae1848ca87 (commit)
from 451e6bed436ada70a761a90f1d08f1fa505020fd (commit)
- Log -----------------------------------------------------------------
commit d151d3aecbe2a478c44be7faca56caae1848ca87
Author: James Almer <jamrial(a)gmail.com>
AuthorDate: Wed Aug 20 10:44:12 2025 -0300
Commit: James Almer <jamrial(a)gmail.com>
CommitDate: Wed Aug 20 10:44:12 2025 -0300
avcodec/exif: don't expose a private function in an installed header
And it doesn't need an entry in APIChanges, given it's not public API.
Signed-off-by: James Almer <jamrial(a)gmail.com>
diff --git a/doc/APIchanges b/doc/APIchanges
index 4b32279e35..81970f17f4 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -13,8 +13,6 @@ API changes, most recent first:
av_exif_ifd_to_dict, av_exif_clone_ifd,
av_exif_matrix_to_orientation,
av_exif_orientation_to_matrix,
- Deprecate:
- - avpriv_exif_decode_ifd (deprecated behind FF_API_OLD_EXIF)
2025-08-19 - bb90b262d6d - lavu 60.10.100 - frame.h
Add AV_FRAME_DATA_EXIF.
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index b6d1ffd91d..944d7ee666 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -223,10 +223,4 @@ int av_exif_matrix_to_orientation(const int32_t *matrix);
*/
int av_exif_orientation_to_matrix(int32_t *matrix, int orientation);
-#if FF_API_OLD_EXIF
-/* Used by the AVI demuxer */
-int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
- int le, int depth, AVDictionary **metadata);
-#endif /* FF_API_OLD_EXIF */
-
#endif /* AVCODEC_EXIF_H */
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
index 7d8e225250..de1a26e3f9 100644
--- a/libavcodec/exif_internal.h
+++ b/libavcodec/exif_internal.h
@@ -34,6 +34,13 @@
#include "libavutil/frame.h"
#include "exif.h"
+#include "version_major.h"
+
+#if FF_API_OLD_EXIF
+/* Used by the AVI demuxer */
+int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
+ int le, int depth, AVDictionary **metadata);
+#endif /* FF_API_OLD_EXIF */
/**
* Attach the data buffer to the frame. This is mostly a wrapper for
-----------------------------------------------------------------------
Summary of changes:
doc/APIchanges | 2 --
libavcodec/exif.h | 6 ------
libavcodec/exif_internal.h | 7 +++++++
3 files changed, 7 insertions(+), 8 deletions(-)
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. 451e6bed43 lavfi/bwdif_vulkan: fix typo in temp_diff assignment
by ffmpeg-git@ffmpeg.org 20 Aug '25
by ffmpeg-git@ffmpeg.org 20 Aug '25
20 Aug '25
The branch, master has been updated
via 451e6bed436ada70a761a90f1d08f1fa505020fd (commit)
from b8856c5fc51a486836983eaffc661e14b57ef49e (commit)
- Log -----------------------------------------------------------------
commit 451e6bed436ada70a761a90f1d08f1fa505020fd
Author: Lynne <dev(a)lynne.ee>
AuthorDate: Wed Aug 20 22:05:38 2025 +0900
Commit: Lynne <dev(a)lynne.ee>
CommitDate: Wed Aug 20 22:06:21 2025 +0900
lavfi/bwdif_vulkan: fix typo in temp_diff assignment
Thanks to Niklas Haas for pointing this out.
diff --git a/libavfilter/vulkan/bwdif.comp b/libavfilter/vulkan/bwdif.comp
index 5c988f472e..5152464823 100644
--- a/libavfilter/vulkan/bwdif.comp
+++ b/libavfilter/vulkan/bwdif.comp
@@ -47,7 +47,7 @@ vec4 process_line(vec4 prev2[5], vec4 prev1[2], vec4 cur[4], vec4 next1[2], vec4
vec4 temp_diff[3];
temp_diff[0] = abs(prev2[2] - next2[2]);
temp_diff[1] = (abs(prev1[0] - fc) + abs(prev1[1] - fe)) / 2;
- temp_diff[1] = (abs(next1[0] - fc) + abs(next1[1] - fe)) / 2;
+ temp_diff[2] = (abs(next1[0] - fc) + abs(next1[1] - fe)) / 2;
vec4 diff = max(temp_diff[0] / 2, max(temp_diff[1], temp_diff[2]));
bvec4 diff_mask = equal(diff, vec4(0));
-----------------------------------------------------------------------
Summary of changes:
libavfilter/vulkan/bwdif.comp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. b8856c5fc5 avcodec/hevc: Fix invalid null pointer check
by ffmpeg-git@ffmpeg.org 20 Aug '25
by ffmpeg-git@ffmpeg.org 20 Aug '25
20 Aug '25
The branch, master has been updated
via b8856c5fc51a486836983eaffc661e14b57ef49e (commit)
from 990edfee5b9ca65ef71feddb595dcda9f195120e (commit)
- Log -----------------------------------------------------------------
commit b8856c5fc51a486836983eaffc661e14b57ef49e
Author: Zhao Zhili <quinkblack(a)foxmail.com>
AuthorDate: Wed Aug 20 09:46:47 2025 +0100
Commit: James Almer <jamrial(a)gmail.com>
CommitDate: Wed Aug 20 11:52:32 2025 +0000
avcodec/hevc: Fix invalid null pointer check
c5287178b4d added a null pointer check, but checked the wrong
variable. &s->cur_frame->f->data[c_idx] cannot be null.
diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c
index f44bda8a92..d3e5e58cfc 100644
--- a/libavcodec/hevc/hevcdec.c
+++ b/libavcodec/hevc/hevcdec.c
@@ -2096,7 +2096,7 @@ static void hls_prediction_unit(HEVCLocalContext *lc,
int log2_cb_size, int partIdx, int idx)
{
#define POS(c_idx, x, y) \
- &s->cur_frame->f->data[c_idx] ? \
+ s->cur_frame->f->data[c_idx] ? \
&s->cur_frame->f->data[c_idx][((y) >> sps->vshift[c_idx]) * linesize[c_idx] + \
(((x) >> sps->hshift[c_idx]) << sps->pixel_shift)] : NULL
const HEVCContext *const s = lc->parent;
-----------------------------------------------------------------------
Summary of changes:
libavcodec/hevc/hevcdec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. 990edfee5b libavcodec/vima: reindent
by ffmpeg-git@ffmpeg.org 20 Aug '25
by ffmpeg-git@ffmpeg.org 20 Aug '25
20 Aug '25
The branch, master has been updated
via 990edfee5b9ca65ef71feddb595dcda9f195120e (commit)
via 9141fe9653dc078d81bff8308ffdd2aaaf35495c (commit)
via 1e95f1b269e25dd8907ac65f2963de54d2b73142 (commit)
from 4c3f94f2651ce4d6834fbef10e5c287f25720ac9 (commit)
- Log -----------------------------------------------------------------
commit 990edfee5b9ca65ef71feddb595dcda9f195120e
Author: Manuel Lauss <manuel.lauss(a)gmail.com>
AuthorDate: Mon Aug 18 09:46:57 2025 +0200
Commit: Manuel Lauss <manuel.lauss(a)gmail.com>
CommitDate: Wed Aug 20 11:20:14 2025 +0200
libavcodec/vima: reindent
Signed-off-by: Manuel Lauss <manuel.lauss(a)gmail.com>
diff --git a/libavcodec/vima.c b/libavcodec/vima.c
index 0423fff091..bde7033c2c 100644
--- a/libavcodec/vima.c
+++ b/libavcodec/vima.c
@@ -173,47 +173,47 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
*dest++ = cs.predictor;
}
} else {
- for (chan = 0; chan < channels; chan++) {
- uint16_t *dest = (uint16_t *)frame->data[0] + chan;
- int step_index = channel_hint[chan];
- int output = pcm_data[chan];
- int sample;
-
- for (sample = 0; sample < samples; sample++) {
- int lookup_size, lookup, highbit, lowbits;
-
- step_index = av_clip(step_index, 0, 88);
- lookup_size = size_table[step_index];
- lookup = get_bits(&gb, lookup_size);
- highbit = 1 << (lookup_size - 1);
- lowbits = highbit - 1;
-
- if (lookup & highbit)
- lookup ^= highbit;
- else
- highbit = 0;
-
- if (lookup == lowbits) {
- output = get_sbits(&gb, 16);
- } else {
- int predict_index, diff;
-
- predict_index = (lookup << (7 - lookup_size)) | (step_index << 6);
- predict_index = av_clip(predict_index, 0, 5785);
- diff = predict_table[predict_index];
- if (lookup)
- diff += ff_adpcm_step_table[step_index] >> (lookup_size - 1);
- if (highbit)
- diff = -diff;
-
- output = av_clip_int16(output + diff);
+ for (chan = 0; chan < channels; chan++) {
+ uint16_t *dest = (uint16_t *)frame->data[0] + chan;
+ int step_index = channel_hint[chan];
+ int output = pcm_data[chan];
+ int sample;
+
+ for (sample = 0; sample < samples; sample++) {
+ int lookup_size, lookup, highbit, lowbits;
+
+ step_index = av_clip(step_index, 0, 88);
+ lookup_size = size_table[step_index];
+ lookup = get_bits(&gb, lookup_size);
+ highbit = 1 << (lookup_size - 1);
+ lowbits = highbit - 1;
+
+ if (lookup & highbit)
+ lookup ^= highbit;
+ else
+ highbit = 0;
+
+ if (lookup == lowbits) {
+ output = get_sbits(&gb, 16);
+ } else {
+ int predict_index, diff;
+
+ predict_index = (lookup << (7 - lookup_size)) | (step_index << 6);
+ predict_index = av_clip(predict_index, 0, 5785);
+ diff = predict_table[predict_index];
+ if (lookup)
+ diff += ff_adpcm_step_table[step_index] >> (lookup_size - 1);
+ if (highbit)
+ diff = -diff;
+
+ output = av_clip_int16(output + diff);
+ }
+
+ *dest = output;
+ dest += channels;
+
+ step_index += step_index_tables[lookup_size - 2][lookup];
}
-
- *dest = output;
- dest += channels;
-
- step_index += step_index_tables[lookup_size - 2][lookup];
- }
}
}
commit 9141fe9653dc078d81bff8308ffdd2aaaf35495c
Author: Manuel Lauss <manuel.lauss(a)gmail.com>
AuthorDate: Sat Aug 16 10:44:06 2025 +0200
Commit: Manuel Lauss <manuel.lauss(a)gmail.com>
CommitDate: Wed Aug 20 11:20:14 2025 +0200
avcodec/vima: IMA4 subtag support
Support decoding of the IMA4 ADPCM QT scheme used in some LucasArts
SANM files.
Signed-off-by: Manuel Lauss <manuel.lauss(a)gmail.com>
diff --git a/libavcodec/vima.c b/libavcodec/vima.c
index 56cc1b7a85..0423fff091 100644
--- a/libavcodec/vima.c
+++ b/libavcodec/vima.c
@@ -28,6 +28,7 @@
#include "libavutil/channel_layout.h"
#include "libavutil/thread.h"
+#include "adpcm.h"
#include "adpcm_data.h"
#include "avcodec.h"
#include "codec_internal.h"
@@ -159,6 +160,19 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
+ if (show_bits_long(&gb, 32) == MKBETAG('I','M','A','4')) {
+ int16_t *dest = (int16_t *)frame->data[0];
+ ADPCMChannelStatus cs;
+
+ skip_bits_long(&gb, 32); /* skip the 'IMA4' tag */
+ cs.predictor = (int16_t)get_xbits_le(&gb, 16);
+ cs.step_index = av_clip(get_bits(&gb, 8), 0, 88);
+ for (int i = 0; i < samples; i++) {
+ ff_adpcm_ima_qt_expand_nibble(&cs, get_bits(&gb, 4));
+ for (int j = 0; j < channels; j++)
+ *dest++ = cs.predictor;
+ }
+ } else {
for (chan = 0; chan < channels; chan++) {
uint16_t *dest = (uint16_t *)frame->data[0] + chan;
int step_index = channel_hint[chan];
@@ -200,6 +214,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
step_index += step_index_tables[lookup_size - 2][lookup];
}
+ }
}
*got_frame_ptr = 1;
commit 1e95f1b269e25dd8907ac65f2963de54d2b73142
Author: Manuel Lauss <manuel.lauss(a)gmail.com>
AuthorDate: Sat Aug 16 10:24:34 2025 +0200
Commit: Manuel Lauss <manuel.lauss(a)gmail.com>
CommitDate: Wed Aug 20 11:20:11 2025 +0200
avcodec/adpcm: export ff_adpcm_ima_qt_expand_nibble
For use in the LucasArts VIMA decoder, where it is used as a subvariant.
Signed-off-by: Manuel Lauss <manuel.lauss(a)gmail.com>
diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c
index 83ef7c780a..4c15be6207 100644
--- a/libavcodec/adpcm.c
+++ b/libavcodec/adpcm.c
@@ -518,7 +518,7 @@ static inline int16_t adpcm_ima_wav_expand_nibble(ADPCMChannelStatus *c, GetBitC
return (int16_t)c->predictor;
}
-static inline int adpcm_ima_qt_expand_nibble(ADPCMChannelStatus *c, int nibble)
+int16_t ff_adpcm_ima_qt_expand_nibble(ADPCMChannelStatus *c, int nibble)
{
int step_index;
int predictor;
@@ -1335,8 +1335,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
for (int m = 0; m < 64; m += 2) {
int byte = bytestream2_get_byteu(&gb);
- samples[m ] = adpcm_ima_qt_expand_nibble(cs, byte & 0x0F);
- samples[m + 1] = adpcm_ima_qt_expand_nibble(cs, byte >> 4 );
+ samples[m ] = ff_adpcm_ima_qt_expand_nibble(cs, byte & 0x0F);
+ samples[m + 1] = ff_adpcm_ima_qt_expand_nibble(cs, byte >> 4 );
}
}
) /* End of CASE */
@@ -1689,16 +1689,16 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
CASE(ADPCM_IMA_SSI,
for (int n = nb_samples >> (1 - st); n > 0; n--) {
int v = bytestream2_get_byteu(&gb);
- *samples++ = adpcm_ima_qt_expand_nibble(&c->status[0], v >> 4 );
- *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0x0F);
+ *samples++ = ff_adpcm_ima_qt_expand_nibble(&c->status[0], v >> 4 );
+ *samples++ = ff_adpcm_ima_qt_expand_nibble(&c->status[st], v & 0x0F);
}
) /* End of CASE */
CASE(ADPCM_IMA_APM,
for (int n = nb_samples / 2; n > 0; n--) {
for (int channel = 0; channel < channels; channel++) {
int v = bytestream2_get_byteu(&gb);
- *samples++ = adpcm_ima_qt_expand_nibble(&c->status[channel], v >> 4 );
- samples[st] = adpcm_ima_qt_expand_nibble(&c->status[channel], v & 0x0F);
+ *samples++ = ff_adpcm_ima_qt_expand_nibble(&c->status[channel], v >> 4 );
+ samples[st] = ff_adpcm_ima_qt_expand_nibble(&c->status[channel], v & 0x0F);
}
samples += channels;
}
@@ -2154,8 +2154,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
for (int n = nb_samples >> (1 - st); n > 0; n--) {
int v = bytestream2_get_byteu(&gb);
- *samples++ = adpcm_ima_qt_expand_nibble(&c->status[0 ], v >> 4 );
- *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0xf);
+ *samples++ = ff_adpcm_ima_qt_expand_nibble(&c->status[0 ], v >> 4 );
+ *samples++ = ff_adpcm_ima_qt_expand_nibble(&c->status[st], v & 0xf);
}
) /* End of CASE */
CASE(ADPCM_CT,
diff --git a/libavcodec/adpcm.h b/libavcodec/adpcm.h
index 0ffc3da1d0..ff70e62078 100644
--- a/libavcodec/adpcm.h
+++ b/libavcodec/adpcm.h
@@ -44,5 +44,6 @@ typedef struct ADPCMChannelStatus {
} ADPCMChannelStatus;
int16_t ff_adpcm_argo_expand_nibble(ADPCMChannelStatus *cs, int nibble, int shift, int flag);
+int16_t ff_adpcm_ima_qt_expand_nibble(ADPCMChannelStatus *c, int nibble);
#endif /* AVCODEC_ADPCM_H */
-----------------------------------------------------------------------
Summary of changes:
libavcodec/adpcm.c | 18 +++++------
libavcodec/adpcm.h | 1 +
libavcodec/vima.c | 93 +++++++++++++++++++++++++++++++-----------------------
3 files changed, 64 insertions(+), 48 deletions(-)
hooks/post-receive
--
1
0
19 Aug '25
The branch, release/8.0 has been updated
via bc88c1d62e4b8f585a01e38044c9671836ae4ab5 (commit)
from 3b8fe34a3086040edd8f1d018de5af0e4af421c1 (commit)
- Log -----------------------------------------------------------------
commit bc88c1d62e4b8f585a01e38044c9671836ae4ab5
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 19 21:51:04 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 21:51:04 2025 +0200
Prepare for 8.0
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/RELEASE b/RELEASE
index 9fa1d2c9d2..cc40bca69a 100644
--- a/RELEASE
+++ b/RELEASE
@@ -1 +1 @@
-7.1.git
+8.0
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 7ac70b95c9..ef4b45afb7 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = FFmpeg
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER = 8.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
-----------------------------------------------------------------------
Summary of changes:
RELEASE | 2 +-
doc/Doxyfile | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
hooks/post-receive
--
1
0
[ffmpeg] branch release/8.0 updated. 3b8fe34a30 aacdec_usac: use RefStruct to track unfinished extension buffers
by ffmpeg-git@ffmpeg.org 19 Aug '25
by ffmpeg-git@ffmpeg.org 19 Aug '25
19 Aug '25
The branch, release/8.0 has been updated
via 3b8fe34a3086040edd8f1d018de5af0e4af421c1 (commit)
via ac8cbf2ad7bca6d8c6e5785d840c005aca21bdb1 (commit)
via 30b308f7a81a61ba826b106cea67cfa531951935 (commit)
via afc8c20946979717d5436906918f530394612f4f (commit)
via debbeb006b0afda9e3970b9c8ec2416d666fecbe (commit)
via 8c117d7fec15256a32707ea05c25c5a47c677842 (commit)
via 5f8cb575e83a05bc95b82d7f5f572d8f554f3705 (commit)
via abf123b74467cb7c124df0e83b978eee2b8a93de (commit)
via e97babf6bc2a27efe776475f879f26f356d82d98 (commit)
via 9bc89a260571d8b8226986648a8a80b5256be508 (commit)
via ac5ff0ae895351c5307ae40e2cf726a1eaed1318 (commit)
via a2e445918ef0f1ff12830b7354db232a7095d70e (commit)
via 34c39367aa8a39fb2f26f583de269e762e334787 (commit)
via 3cab0095192aa2bc51f8f8060ed4ccbf9caf3c87 (commit)
via 3e05b895900e2cbfcd2dafb77de8fbcf14c71732 (commit)
via 4a3e5ea8d2ebad2b930c503d08fbca05f6209d89 (commit)
via 263e819aa45cd3c48bf6887be02b4ec504c02048 (commit)
via 23655160ea4acb2601db6a90e1fb40fe7b4cabc4 (commit)
via f2507dba3b4c40c5d2d0135a7847db448d3c3527 (commit)
via da3f5273fce6c5dbb68077fbec346cc4b1a3e9f0 (commit)
via 6049800a106b6ff8c6a0e062c5146e554a54c21f (commit)
via e726f7af17b3ea160b6ce8482f3065e4c36c3f97 (commit)
via de76fb27a6e6da0431154ce9093933281a38a889 (commit)
via 995d329cf9213cc445a2cea31b8f2394d3475d8c (commit)
via 81dcb6781390bd3225fc2250cb0f73ee6487eb00 (commit)
via bde02336a44086d628cb8e577a915fa9a6bf6088 (commit)
via 6b1f994e43b7c0648f91fc6a14ee5c090c99e1fc (commit)
via 5051753833d5123707e58e2571d0831e3f9278be (commit)
via a676267a2c29581346c62dbfdfc9268aa2fd2658 (commit)
via bd55bf8300ef2b97c9316d7b2674b07142163e70 (commit)
via 64c71cbe4eefcd59559238758bf760b3197469ef (commit)
via 2feaad5cb977362610305941c689c8d76a8e80f3 (commit)
from 048f6f4bd57b1f18af5da40d28d506a22daa1338 (commit)
- Log -----------------------------------------------------------------
commit 3b8fe34a3086040edd8f1d018de5af0e4af421c1
Author: Lynne <dev(a)lynne.ee>
AuthorDate: Mon Aug 11 22:26:35 2025 +0900
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:52 2025 +0200
aacdec_usac: use RefStruct to track unfinished extension buffers
Extensions in AAC USAC can be stored across multiple frames (mainly to keep CBR compliance).
This means that we need to reallocate a buffer when new data is received, accumulate the bitstream data,
and so on until the end of extension flag is signalled and the extension can be decoded.
This is made more complicated by the way in which the AAC channel layout switching is performed.
After decades of evolution, our AAC decoder evolved to double-buffer its entire configuration.
All changes are buffered, verified, and applied, on a per-frame basis if required, in often
random order.
Since we allocate the extension data on heap, this means that if configuration is applied,
in order to avoid double-freeing, we have to keep track of what we've allocated.
It should be noted that extensions which are spread in multiple frames are generally rare,
so an optimization to introduce av_refstruct_realloc() wouldn't generally be useful across the codebase.
Therefore, a copy is good enough for now.
Thanks to Michael Niedermayer for additional fixing.
Fixes: double free
Fixes: 393523547/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_AAC_LATM_fuzzer-6740617236905984
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
(cherry picked from commit c05fc27dd33b361eb0105157ab7d3a01c2ffa782)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c
index 947079bc3d..9b42014ee8 100644
--- a/libavcodec/aac/aacdec.c
+++ b/libavcodec/aac/aacdec.c
@@ -62,6 +62,7 @@
#include "libavutil/opt.h"
#include "libavutil/tx.h"
#include "libavutil/version.h"
+#include "libavutil/refstruct.h"
/*
* supported tools
@@ -421,6 +422,26 @@ static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags)
return layout;
}
+static void copy_oc(OutputConfiguration *dst, OutputConfiguration *src)
+{
+ int i;
+
+ for (i = 0; i < src->usac.nb_elems; i++) {
+ AACUsacElemConfig *src_e = &src->usac.elems[i];
+ AACUsacElemConfig *dst_e = &dst->usac.elems[i];
+ /* dst_e->ext.pl_buf is guaranteed to be set to src_e->ext.pl_buf
+ * upon this function's return */
+ av_refstruct_replace(&dst_e->ext.pl_buf, src_e->ext.pl_buf);
+ }
+
+ /* Unref all additional buffers to close leaks */
+ for (; i < dst->usac.nb_elems; i++)
+ av_refstruct_unref(&dst->usac.elems[i].ext.pl_buf);
+
+ /* Set all other properties */
+ *dst = *src;
+}
+
/**
* Save current output configuration if and only if it has been locked.
*/
@@ -429,7 +450,7 @@ static int push_output_configuration(AACDecContext *ac)
int pushed = 0;
if (ac->oc[1].status == OC_LOCKED || ac->oc[0].status == OC_NONE) {
- ac->oc[0] = ac->oc[1];
+ copy_oc(&ac->oc[0], &ac->oc[1]);
pushed = 1;
}
ac->oc[1].status = OC_NONE;
@@ -443,7 +464,8 @@ static int push_output_configuration(AACDecContext *ac)
static void pop_output_configuration(AACDecContext *ac)
{
if (ac->oc[1].status != OC_LOCKED && ac->oc[0].status != OC_NONE) {
- ac->oc[1] = ac->oc[0];
+ copy_oc(&ac->oc[1], &ac->oc[0]);
+
ac->avctx->ch_layout = ac->oc[1].ch_layout;
ff_aac_output_configure(ac, ac->oc[1].layout_map, ac->oc[1].layout_map_tags,
ac->oc[1].status, 0);
@@ -1110,7 +1132,7 @@ static av_cold int decode_close(AVCodecContext *avctx)
AACUSACConfig *usac = &oc->usac;
for (int j = 0; j < usac->nb_elems; j++) {
AACUsacElemConfig *ec = &usac->elems[j];
- av_freep(&ec->ext.pl_data);
+ av_refstruct_unref(&ec->ext.pl_buf);
}
av_channel_layout_uninit(&ac->oc[i].ch_layout);
diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h
index e5a79a7139..b3763fdccc 100644
--- a/libavcodec/aac/aacdec.h
+++ b/libavcodec/aac/aacdec.h
@@ -344,7 +344,7 @@ typedef struct AACUsacElemConfig {
uint8_t payload_frag;
uint32_t default_len;
uint32_t pl_data_offset;
- uint8_t *pl_data;
+ uint8_t *pl_buf;
} ext;
} AACUsacElemConfig;
@@ -353,7 +353,7 @@ typedef struct AACUSACConfig {
uint16_t core_frame_len;
uint16_t stream_identifier;
- AACUsacElemConfig elems[64];
+ AACUsacElemConfig elems[MAX_ELEM_ID];
int nb_elems;
struct {
diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c
index e03e6e015f..c4b821bbba 100644
--- a/libavcodec/aac/aacdec_usac.c
+++ b/libavcodec/aac/aacdec_usac.c
@@ -24,12 +24,13 @@
#include "aacdec_ac.h"
#include "libavcodec/aacsbr.h"
-
#include "libavcodec/aactab.h"
-#include "libavutil/mem.h"
#include "libavcodec/mpeg4audio.h"
#include "libavcodec/unary.h"
+#include "libavutil/mem.h"
+#include "libavutil/refstruct.h"
+
/* Number of scalefactor bands per complex prediction band, equal to 2. */
#define SFB_PER_PRED_BAND 2
@@ -1574,7 +1575,6 @@ static int parse_audio_preroll(AACDecContext *ac, GetBitContext *gb)
static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
GetBitContext *gb)
{
- uint8_t *tmp;
uint8_t pl_frag_start = 1;
uint8_t pl_frag_end = 1;
uint32_t len;
@@ -1601,18 +1601,26 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
if (pl_frag_start)
e->ext.pl_data_offset = 0;
- /* If an extension starts and ends this packet, we can directly use it */
+ /* If an extension starts and ends this packet, we can directly use it below.
+ * Otherwise, we have to copy it to a buffer and accumulate it. */
if (!(pl_frag_start && pl_frag_end)) {
- tmp = av_realloc(e->ext.pl_data, e->ext.pl_data_offset + len);
- if (!tmp) {
- av_free(e->ext.pl_data);
+ /* Reallocate the data */
+ uint8_t *tmp_buf = av_refstruct_alloc_ext(e->ext.pl_data_offset + len,
+ AV_REFSTRUCT_FLAG_NO_ZEROING,
+ NULL, NULL);
+ if (!tmp_buf)
return AVERROR(ENOMEM);
- }
- e->ext.pl_data = tmp;
+
+ /* Copy the data over only if we had saved data to begin with */
+ if (e->ext.pl_buf)
+ memcpy(tmp_buf, e->ext.pl_buf, e->ext.pl_data_offset);
+
+ av_refstruct_unref(&e->ext.pl_buf);
+ e->ext.pl_buf = tmp_buf;
/* Readout data to a buffer */
for (int i = 0; i < len; i++)
- e->ext.pl_data[e->ext.pl_data_offset + i] = get_bits(gb, 8);
+ e->ext.pl_buf[e->ext.pl_data_offset + i] = get_bits(gb, 8);
}
e->ext.pl_data_offset += len;
@@ -1624,7 +1632,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
GetBitContext *gb2 = gb;
GetBitContext gbc;
if (!(pl_frag_start && pl_frag_end)) {
- ret = init_get_bits8(&gbc, e->ext.pl_data, pl_len);
+ ret = init_get_bits8(&gbc, e->ext.pl_buf, pl_len);
if (ret < 0)
return ret;
@@ -1642,7 +1650,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
/* This should never happen */
av_assert0(0);
}
- av_freep(&e->ext.pl_data);
+ av_refstruct_unref(&e->ext.pl_buf);
if (ret < 0)
return ret;
commit ac8cbf2ad7bca6d8c6e5785d840c005aca21bdb1
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 19 03:09:14 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:52 2025 +0200
avcode: Use av_fast_realloc() in ff_lzf_uncompress()
Fixes: 438961582/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5850827739955200
Fixes: mixed up realloc() functions
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
Co-Authored-by: James Almer <jamrial(a)gmail.com>
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit 0a5046c09996262d0f8b1802a4b34816f72fff06)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index 01e5657075..07eee253e7 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -42,7 +42,7 @@ typedef struct DXVContext {
uint8_t *ctex_data; // Compressed chroma texture
unsigned ctex_data_size;
- int64_t tex_size; // Texture size
+ size_t tex_size; // Texture size
int64_t ctex_size; // Chroma texture size
uint8_t *op_data[4]; // Opcodes
@@ -828,7 +828,7 @@ static int dxv_decompress_dxt5(AVCodecContext *avctx)
static int dxv_decompress_lzf(AVCodecContext *avctx)
{
DXVContext *ctx = avctx->priv_data;
- return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size);
+ return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size, &ctx->tex_data_size);
}
static int dxv_decompress_raw(AVCodecContext *avctx)
diff --git a/libavcodec/lzf.c b/libavcodec/lzf.c
index 94b369dd59..8f223b1f42 100644
--- a/libavcodec/lzf.c
+++ b/libavcodec/lzf.c
@@ -37,7 +37,22 @@
#define LZF_LITERAL_MAX (1 << 5)
#define LZF_LONG_BACKREF 7 + 2
-int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
+
+static inline int lzf_realloc(uint8_t **buf, size_t *size, int addition, unsigned *allocated_size)
+{
+ void *ptr = av_fast_realloc(*buf, allocated_size, *size + addition);
+
+ if (!ptr) {
+ av_freep(buf); //probably not needed
+ return AVERROR(ENOMEM);
+ }
+ *buf = ptr;
+ *size += addition;
+
+ return 0;
+}
+
+int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, size_t *size, unsigned *allocated_size)
{
int ret = 0;
uint8_t *p = *buf;
@@ -49,8 +64,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
if (s < LZF_LITERAL_MAX) {
s++;
if (s > *size - len) {
- *size += s + *size /2;
- ret = av_reallocp(buf, *size);
+ ret = lzf_realloc(buf, size, s, allocated_size);
if (ret < 0)
return ret;
p = *buf + len;
@@ -75,8 +89,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
return AVERROR_INVALIDDATA;
if (l > *size - len) {
- *size += l + *size / 2;
- ret = av_reallocp(buf, *size);
+ ret = lzf_realloc(buf, size, l, allocated_size);
if (ret < 0)
return ret;
p = *buf + len;
diff --git a/libavcodec/lzf.h b/libavcodec/lzf.h
index 0ad73d9f79..e61ebff727 100644
--- a/libavcodec/lzf.h
+++ b/libavcodec/lzf.h
@@ -24,6 +24,6 @@
#include "bytestream.h"
-int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size);
+int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, size_t *size, unsigned *allocated_size);
#endif /* AVCODEC_LZF_H */
diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c
index 246a3e0174..d99de1810e 100644
--- a/libavcodec/notchlc.c
+++ b/libavcodec/notchlc.c
@@ -40,7 +40,8 @@ typedef struct NotchLCContext {
unsigned uncompressed_size;
uint8_t *lzf_buffer;
- int64_t lzf_size;
+ size_t lzf_size;
+ unsigned lzf_alloc_size;
unsigned texture_size_x;
unsigned texture_size_y;
@@ -490,7 +491,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p,
return AVERROR_PATCHWELCOME;
if (s->format == 0) {
- ret = ff_lzf_uncompress(gb, &s->lzf_buffer, &s->lzf_size);
+ ret = ff_lzf_uncompress(gb, &s->lzf_buffer, &s->lzf_size, &s->lzf_alloc_size);
if (ret < 0)
return ret;
commit 30b308f7a81a61ba826b106cea67cfa531951935
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 19 03:12:37 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:51 2025 +0200
avcodec/dxv: Check coded_height, to avoid invalid av_clip()
Fixes: assertion failure
Fixes: 438961582/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5850827739955200
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit cdee519d40e61bd65ba5b3fbec00acd50a08d0d9)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index dd82e450b1..01e5657075 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -940,6 +940,8 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame,
}
break;
}
+ if (avctx->coded_height / 2 / TEXTURE_BLOCK_H < 1)
+ return AVERROR_INVALIDDATA;
texdsp_ctx.slice_count = av_clip(avctx->thread_count, 1,
avctx->coded_height / TEXTURE_BLOCK_H);
commit afc8c20946979717d5436906918f530394612f4f
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Wed Aug 13 13:11:23 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:51 2025 +0200
avcodec/aac/aacdec: dont allow ff_aac_output_configure() allocating a new frame if it has no frame
Fixes: null pointer dereference
Fixes: crash_test.mp4
Found-by: Intel PSIRT
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit fcf180d9ea27b7dc29658c9dc3488ae6fac3ebd9)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c
index 6a2aa9dc8e..947079bc3d 100644
--- a/libavcodec/aac/aacdec.c
+++ b/libavcodec/aac/aacdec.c
@@ -466,6 +466,9 @@ int ff_aac_output_configure(AACDecContext *ac,
uint8_t id_map[TYPE_END][MAX_ELEM_ID] = {{ 0 }};
uint8_t type_counts[TYPE_END] = { 0 };
+ if (get_new_frame && !ac->frame)
+ return AVERROR_INVALIDDATA;
+
if (ac->oc[1].layout_map != layout_map) {
memcpy(ac->oc[1].layout_map, layout_map, tags * sizeof(layout_map[0]));
ac->oc[1].layout_map_tags = tags;
commit debbeb006b0afda9e3970b9c8ec2416d666fecbe
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Mon Aug 18 17:20:49 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:51 2025 +0200
avformat/lrcdec: Fix fate-sub-lrc-ms-remux on x86-32
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 0243cf89b137b093b02a5c61a76e28cec1d69ae9)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c
index e3e091a61a..f4a879954e 100644
--- a/libavformat/lrcdec.c
+++ b/libavformat/lrcdec.c
@@ -92,7 +92,7 @@ static int64_t read_ts(const char *p, int64_t *start)
if (ret != 3 || prefix[0] != '[' || ss < 0 || ss > 60) {
return 0;
}
- *start = (mm * 60 + ss) * AV_TIME_BASE;
+ *start = llrint((mm * 60 + ss) * AV_TIME_BASE);
if (prefix[1] == '-') {
*start = - *start;
}
commit 8c117d7fec15256a32707ea05c25c5a47c677842
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Mon Aug 18 16:31:05 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:50 2025 +0200
swscale/swscale_internal: Use more precisse gamma
Avoids failure of xyz12 fate tests on mingw and linux x86-32
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit ca20d42cd79b12b1f39cc80c758e5492337f2f25)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index 32428c56fb..5dd65a8d71 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -545,8 +545,8 @@ struct SwsInternal {
int use_mmx_vfilter;
/* pre defined color-spaces gamma */
-#define XYZ_GAMMA (2.6f)
-#define RGB_GAMMA (2.2f)
+#define XYZ_GAMMA (2.6)
+#define RGB_GAMMA (2.2)
uint16_t *xyzgamma;
uint16_t *rgbgamma;
uint16_t *xyzgammainv;
diff --git a/tests/ref/fate/filter-pixdesc-xyz12be b/tests/ref/fate/filter-pixdesc-xyz12be
index 6610444e76..3cef4083c7 100644
--- a/tests/ref/fate/filter-pixdesc-xyz12be
+++ b/tests/ref/fate/filter-pixdesc-xyz12be
@@ -1 +1 @@
-pixdesc-xyz12be 1508a33dea936c45d9ee13f7743af00d
+pixdesc-xyz12be 198f43f452bc55f4ca1e0e0171de5c4c
diff --git a/tests/ref/fate/filter-pixdesc-xyz12le b/tests/ref/fate/filter-pixdesc-xyz12le
index 41b4907923..6193c3193a 100644
--- a/tests/ref/fate/filter-pixdesc-xyz12le
+++ b/tests/ref/fate/filter-pixdesc-xyz12le
@@ -1 +1 @@
-pixdesc-xyz12le da2d1326fa5747a7f6ce5ac1e1494aea
+pixdesc-xyz12le 5ca7d9ab5e01cc1bdc906520926721eb
diff --git a/tests/ref/fate/filter-pixfmts-copy b/tests/ref/fate/filter-pixfmts-copy
index 84a407c8fa..d42e2f6b33 100644
--- a/tests/ref/fate/filter-pixfmts-copy
+++ b/tests/ref/fate/filter-pixfmts-copy
@@ -117,8 +117,8 @@ xv36be 9f556ee59a672fd8725f0bb36ce3e4b0
xv36le e08dcbde02f1c28a3554f372ad1278e2
xv48be ce34993b4b4411bba1d852b9b86aa39e
xv48le df913a7e61b162aa98303e5393e60c63
-xyz12be f257f86373207af8aed0a1a05171df3b
-xyz12le 7922f99edc44a2c26a25becbea9914cc
+xyz12be d4562ab725e3ab7e5573dd03218bb03c
+xyz12le ce17d867c3fea4c42324e40353b72de4
y210le 04e9487b6cce38e7531437e946cdd586
y212le 825768be8fe92708ae80be84855066ed
y216le 0e99aeddfee304e72d525d72998d9e9b
diff --git a/tests/ref/fate/filter-pixfmts-crop b/tests/ref/fate/filter-pixfmts-crop
index 05bb5b1b19..a86113c631 100644
--- a/tests/ref/fate/filter-pixfmts-crop
+++ b/tests/ref/fate/filter-pixfmts-crop
@@ -114,8 +114,8 @@ xv36be 23b6f253fcb375e4145cfcb562268c5f
xv36le 778286003497f92b84d0bd8258d6b85d
xv48be c90889b2cf54cc78bd58e8c47d4eb791
xv48le 2c15c1254449ec5f9135ae61bdf4e1d5
-xyz12be e2f9f6a1ec205ab675a5a1c9521dfa6c
-xyz12le fea1da11c07736303b139bc52b7d4759
+xyz12be 05d6fc3fbe70403160ff4e1b8a4aa969
+xyz12le 3c7328a2f8497f0b4635b84df381a2b8
ya16be 071add03126a11dc6a06209e9b409f8d
ya16le b723211dc0647c944768c6e45e066b36
ya8 51a8dd297e35d40b06d3ebe8f4717895
diff --git a/tests/ref/fate/filter-pixfmts-field b/tests/ref/fate/filter-pixfmts-field
index d8a0d5234a..c05dba55f6 100644
--- a/tests/ref/fate/filter-pixfmts-field
+++ b/tests/ref/fate/filter-pixfmts-field
@@ -117,8 +117,8 @@ xv36be bcc7bda2d0a5d43db4464af6a4cb5d65
xv36le ba99f258370f2a56993e8760e6b30194
xv48be 2abcd986a34789ba4310be3969020d0d
xv48le f6f2e33f260f48334197538f3331f7bc
-xyz12be 3b6eb75517263b9e54b9bfa869de394f
-xyz12le 27d1d6a488cbc5d53e8d12fa0e162ddb
+xyz12be a17930e991ca4ff86de5c2dc3b1e9b51
+xyz12le b134b258a9593bc2019f0493f079f3a1
y210le 4c2fba1dc40322584977d15dd07c9146
y212le ac2a47c45187dd54d0f55293cbffd954
y216le e65b5bfae1b40edbbed2012e9cd45e31
diff --git a/tests/ref/fate/filter-pixfmts-fieldorder b/tests/ref/fate/filter-pixfmts-fieldorder
index df48d3af34..4c8cffca08 100644
--- a/tests/ref/fate/filter-pixfmts-fieldorder
+++ b/tests/ref/fate/filter-pixfmts-fieldorder
@@ -106,8 +106,8 @@ xv36be 962386c88268f4382004c3a7a82c5eb8
xv36le bcceffc985aaa8414c4b8072aa0889bd
xv48be 4d6e4004b03767f12df8bb4e76c98ddf
xv48le 9e94d82461a2131063157ac0dbe9467b
-xyz12be ba6928f85c202cd77e216934f6bf0698
-xyz12le 964680cd3f3db8a7ef5510f90196961a
+xyz12be 65fe8da4ad44c83855360144eba41853
+xyz12le f13851b3ba4e511ab1436786ec0727bd
y210le 22b1a02a39c4b325726bf8793bf1e8f2
y212le 2f08fb195b948056c844acb1eee8d649
y216le 360cb98ac80b13d3a8ec61c9f1ff3bac
diff --git a/tests/ref/fate/filter-pixfmts-hflip b/tests/ref/fate/filter-pixfmts-hflip
index cd5dda5ccf..b47087d1f6 100644
--- a/tests/ref/fate/filter-pixfmts-hflip
+++ b/tests/ref/fate/filter-pixfmts-hflip
@@ -114,8 +114,8 @@ xv36be 98f578df965eed369f46cb135e2d1345
xv36le e478b4b54698beb3ce1b9a2dd691d544
xv48be e030a2c7b1b600cfacb691b6e90c2e3d
xv48le fbd7f8c65cd6fc9f9108dc9a1f977dc3
-xyz12be 3c50a51a3c486a0c6853e4bbbcf3f244
-xyz12le e020897d826ea20ded16f30ea1eb018d
+xyz12be c678c7f19c610063f352a0253121441f
+xyz12le 2320702cb1c8ccaad27d7004d4f3a195
ya16be 70fa41c32ecaf3370edc38add6096db2
ya16le 3b2c20f9e80717628ced6c6468507f63
ya8 4ad5920716de3d2fbbc49f95adb60345
diff --git a/tests/ref/fate/filter-pixfmts-il b/tests/ref/fate/filter-pixfmts-il
index 768bf8c06c..490995bbf0 100644
--- a/tests/ref/fate/filter-pixfmts-il
+++ b/tests/ref/fate/filter-pixfmts-il
@@ -116,8 +116,8 @@ xv36be 3bbb949278ea55cc947ee03bd9c27c2d
xv36le 102c0e817d375ddd6b2cfbb4262dec95
xv48be 4d7376651fb7b3e84d00abad6c785aad
xv48le a1a8ff16d9a864568e5e557734bf3d6d
-xyz12be b7d50e283360bf69fd661369110b26ef
-xyz12le d5b1d45c3a136bb3d04f70a619c86c8d
+xyz12be 3d9ae42c65eb645bb8e99dc708716f2d
+xyz12le b27ec91dcff02d2b94b17c136be55001
y210le d4cf9b53cd7ff22f087743d483e88480
y212le d5a2b4677ddb4a3bc3e5cd5cbb20f426
y216le 9e44c6d76b09bcbe71738423b4b3d67a
diff --git a/tests/ref/fate/filter-pixfmts-null b/tests/ref/fate/filter-pixfmts-null
index 84a407c8fa..d42e2f6b33 100644
--- a/tests/ref/fate/filter-pixfmts-null
+++ b/tests/ref/fate/filter-pixfmts-null
@@ -117,8 +117,8 @@ xv36be 9f556ee59a672fd8725f0bb36ce3e4b0
xv36le e08dcbde02f1c28a3554f372ad1278e2
xv48be ce34993b4b4411bba1d852b9b86aa39e
xv48le df913a7e61b162aa98303e5393e60c63
-xyz12be f257f86373207af8aed0a1a05171df3b
-xyz12le 7922f99edc44a2c26a25becbea9914cc
+xyz12be d4562ab725e3ab7e5573dd03218bb03c
+xyz12le ce17d867c3fea4c42324e40353b72de4
y210le 04e9487b6cce38e7531437e946cdd586
y212le 825768be8fe92708ae80be84855066ed
y216le 0e99aeddfee304e72d525d72998d9e9b
diff --git a/tests/ref/fate/filter-pixfmts-scale b/tests/ref/fate/filter-pixfmts-scale
index e4ab07104c..66d22e0484 100644
--- a/tests/ref/fate/filter-pixfmts-scale
+++ b/tests/ref/fate/filter-pixfmts-scale
@@ -117,8 +117,8 @@ xv36be 4d084adca0228d7750d1e2e877e0d79b
xv36le de9c74e94dc19c828e1572aa283d8aca
xv48be 9e58d1a045df100b0dec116e13be5b4e
xv48le fd873d53609b2fbdfe99470f515a234c
-xyz12be f1905012d9b845306d9bef68d0fc81d5
-xyz12le cfe1a3bbe391d83d381f590a00e1a16d
+xyz12be 94ed06895d7030b97c36ccb9aae3722a
+xyz12le fcb73d6b6192ab64ca9ff668fe6562f6
y210le 7c2aef142d88ab343ec01acd45f38466
y212le 39a3c0c843041ad4501b3107dd91ef17
y216le 17be2999e97d36b8ed903f07ef428c09
diff --git a/tests/ref/fate/filter-pixfmts-transpose b/tests/ref/fate/filter-pixfmts-transpose
index 64b45945c7..80b05dbb05 100644
--- a/tests/ref/fate/filter-pixfmts-transpose
+++ b/tests/ref/fate/filter-pixfmts-transpose
@@ -104,8 +104,8 @@ xv36be 2261a0e3db5ee607d37f68d19704ae15
xv36le 9202133de91bf64c76ca27d5cd0c816a
xv48be 14373b7fe123225689e76fe2ce43fb93
xv48le 319df9724a067c7b5efa215f9f54d127
-xyz12be 69737aceb508a73365664d04c340dd3b
-xyz12le 70dd5fab9d8383b0d2e772b3b6569df4
+xyz12be 38ce311e8734b535484c2e2ba70626d0
+xyz12le 24a0e779fce6eaed687cb2c416a61eb9
ya16be 6098f7d2ede0aab6b2d93d2b4f4d915a
ya16le 1fae63e3e320ba9e6c12c29a48c44eff
ya8 d4b7a62f80681fa44c977ff3a64f4ce4
diff --git a/tests/ref/fate/filter-pixfmts-vflip b/tests/ref/fate/filter-pixfmts-vflip
index 590717399d..6b8c888b9a 100644
--- a/tests/ref/fate/filter-pixfmts-vflip
+++ b/tests/ref/fate/filter-pixfmts-vflip
@@ -117,8 +117,8 @@ xv36be c0272372d3e1a59adb3931ee433a5d5b
xv36le ffe6ab75ebc09134c3451f8f6ef0d501
xv48be bdfc3217ae456b370dbdcf4d52606a3f
xv48le 53dbebab73a66539c04644ef56dc6bbb
-xyz12be 1bffa153a4a3ae61fd18e370f95161d9
-xyz12le a0e93443826621a9d6c48354d949898a
+xyz12be b9b83d36d58f4ad129894207888b3b3c
+xyz12le b9764e43bacbbfc410e4b6558486115f
y210le f8847bedd3ae6e1c0cf84a823f275e31
y212le c801725ae31e3b8f5be269359d49f191
y216le 985db498aedf3fb1c547ad07442b7258
diff --git a/tests/ref/pixfmt/xyz12le b/tests/ref/pixfmt/xyz12le
index 870d5ce683..312bc9ed57 100644
--- a/tests/ref/pixfmt/xyz12le
+++ b/tests/ref/pixfmt/xyz12le
@@ -1,2 +1,2 @@
-47b890678bb9195107c2f7afb2344bbf *tests/data/pixfmt/xyz12le.yuv
+f14b14a12c864408a0115f55eaef9934 *tests/data/pixfmt/xyz12le.yuv
304128 tests/data/pixfmt/xyz12le.yuv
commit 5f8cb575e83a05bc95b82d7f5f572d8f554f3705
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Sun Aug 17 15:31:48 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:50 2025 +0200
avcodec/sanm: Check w,h,left,top
The setup code fow w,h,left,top is complex, the code using it also falls in
at least 2 different classes, one using left/top the other not.
To ensure no out of array access happens we add this clear check.
Fixes: out of array access
Fixes: 439261995/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SANM_fuzzer-5383455572819968
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 134fbfd1dcb59441e38d870ddd231772f4e8e127)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index a066a864eb..9e99aa9dd9 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -1757,6 +1757,11 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb)
memset(ctx->fbuf, 0, ctx->frm0_size);
}
+ if (w + FFMAX(left, 0) > ctx->avctx->width || h + FFMAX(top, 0) > ctx->avctx->height) {
+ avpriv_request_sample(ctx->avctx, "overly large frame\n");
+ return AVERROR_PATCHWELCOME;
+ }
+
switch (codec) {
case 1:
case 3:
commit abf123b74467cb7c124df0e83b978eee2b8a93de
Author: James Almer <jamrial(a)gmail.com>
AuthorDate: Sat Aug 16 14:16:38 2025 -0300
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:50 2025 +0200
avformat/mov: don't use an allocated array for sample_size with HEIF images
The array is only ever needed for streams where each sample entry may have a
different value. Given that for non animated HEIF there's a single value that
applies to the image, use the field defined for that.
Fixes: NULL pointer dereference
Fixes: 437528618/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-6537287645331456
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: James Almer <jamrial(a)gmail.com>
(cherry picked from commit a28e01a6c16430da689340d0af6eec094020b719)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 86037c6712..b29c41a6b6 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -5456,10 +5456,6 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
if (!sc->chunk_offsets)
goto fail;
sc->chunk_count = 1;
- sc->sample_sizes = av_malloc_array(1, sizeof(*sc->sample_sizes));
- if (!sc->sample_sizes)
- goto fail;
- sc->sample_count = 1;
sc->stts_data = av_malloc_array(1, sizeof(*sc->stts_data));
if (!sc->stts_data)
goto fail;
@@ -10471,11 +10467,13 @@ static int mov_parse_heif_items(AVFormatContext *s)
st->codecpar->width = item->width;
st->codecpar->height = item->height;
+ sc->sample_size = sc->stsz_sample_size = item->extent_length;
+ sc->sample_count = 1;
+
err = sanity_checks(s, sc, item->item_id);
- if (err || !sc->sample_count)
+ if (err)
return AVERROR_INVALIDDATA;
- sc->sample_sizes[0] = item->extent_length;
sc->chunk_offsets[0] = item->extent_offset + offset;
if (item->item_id == mov->primary_item_id)
commit e97babf6bc2a27efe776475f879f26f356d82d98
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 15 19:49:19 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:49 2025 +0200
avcodec/rv60dec: clear pu_info
pu_info is read uninitialized on damaged input and at that point the following codepath is dependant
on the uninitialized data. In one of these pathes out of array accesses happen.
None of this is replicatable
Less uninitialized data also should result in more reproducable reports
Fixes: Use of uninitialized memory
Fixes: 418335931/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_RV60_fuzzer-5103986067963904
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 50affd2b09ca7ebf6beb287a087947be887b2417)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/rv60dec.c b/libavcodec/rv60dec.c
index 4a3d9067db..208fbc68f7 100644
--- a/libavcodec/rv60dec.c
+++ b/libavcodec/rv60dec.c
@@ -308,6 +308,8 @@ static int update_dimensions_clear_info(RV60Context *s, int width, int height)
if ((ret = av_reallocp_array(&s->blk_info, s->blk_stride * (s->cu_height << 4), sizeof(s->blk_info[0]))) < 0)
return ret;
+ memset(s->pu_info, 0, s->pu_stride * (s->cu_height << 3) * sizeof(s->pu_info[0]));
+
for (int j = 0; j < s->cu_height << 4; j++)
for (int i = 0; i < s->cu_width << 4; i++)
s->blk_info[j*s->blk_stride + i].mv.mvref = MVREF_NONE;
commit 9bc89a260571d8b8226986648a8a80b5256be508
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Mon Jul 28 19:07:32 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:49 2025 +0200
avformat/tls_openssl: use ascii - (0x2D) instead of 0x2010 hyphen
Too much AI is bad for you...
Fixes: 167e343bbe75515a80db8ee72ffa0c607c944a00
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit 3a8b3dfeca2ddbee9999262f3240bfe05b55c66a)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index 9f7b46c3ca..75229ea671 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -34,8 +34,8 @@
#include <openssl/x509v3.h>
/**
- * Returns a heap‐allocated null‐terminated string containing
- * the PEM‐encoded public key. Caller must free.
+ * Returns a heap-allocated null-terminated string containing
+ * the PEM-encoded public key. Caller must free.
*/
static char *pkey_to_pem_string(EVP_PKEY *pkey) {
BIO *mem = NULL;
@@ -60,7 +60,7 @@ static char *pkey_to_pem_string(EVP_PKEY *pkey) {
if (!pem_str)
goto err;
- // Copy data & NUL‐terminate
+ // Copy data & NUL-terminate
memcpy(pem_str, bptr->data, bptr->length);
pem_str[bptr->length] = '\0';
@@ -425,7 +425,7 @@ error:
/**
- * Deserialize a PEM‐encoded private or public key from a NUL-terminated C string.
+ * Deserialize a PEM-encoded private or public key from a NUL-terminated C string.
*
* @param pem_str The PEM text, e.g.
* "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n"
@@ -456,7 +456,7 @@ static EVP_PKEY *pkey_from_pem_string(const char *pem_str, int is_priv)
}
/**
- * Deserialize a PEM‐encoded certificate from a NUL-terminated C string.
+ * Deserialize a PEM-encoded certificate from a NUL-terminated C string.
*
* @param pem_str The PEM text, e.g.
* "-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----\n"
commit ac5ff0ae895351c5307ae40e2cf726a1eaed1318
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 15 17:55:05 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:48 2025 +0200
avcodec/utvideodec: Clear plane_start array
in pack mode the array is passed into decode_plane() without being initialized or used
Fixes: use of uninitialized memory
Fixes: 438780119/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_UTVIDEO_DEC_fuzzer-5464037027807232
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 2a22972db3b390d82dedbdbb5f44cc09a43912b5)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c
index 934945c1be..bc02ac44d5 100644
--- a/libavcodec/utvideodec.c
+++ b/libavcodec/utvideodec.c
@@ -585,7 +585,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
int buf_size = avpkt->size;
UtvideoContext *c = avctx->priv_data;
int i, j;
- const uint8_t *plane_start[5];
+ const uint8_t *plane_start[5] = {NULL};
int plane_size, max_slice_size = 0, slice_start, slice_end, slice_size;
int ret;
GetByteContext gb;
commit a2e445918ef0f1ff12830b7354db232a7095d70e
Author: Oliver Chang <ochang(a)google.com>
AuthorDate: Thu Aug 14 22:11:41 2025 -0700
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:48 2025 +0200
avcodec/prores_raw: Fix heap buffer overflow
When dimensions differ from context, those were updated using
ff_set_dimensions, however this overwrote the aligned coded_width and
coded_height that were set before, leading to a buffer overflow when
writing the frame data.
Fixes: OssFuzz 438771336
Fixes: Heap-buffer-overflow
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Marvin Scholz <epirat07(a)gmail.com>
Reviewed-by: Marvin Scholz <epirat07(a)gmail.com>
(cherry picked from commit c9e93df4eed93fe0044c52d953688c4180de1d48)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c
index 748e176815..b2aa97ddda 100644
--- a/libavcodec/prores_raw.c
+++ b/libavcodec/prores_raw.c
@@ -367,9 +367,6 @@ static int decode_frame(AVCodecContext *avctx,
if ((w & 1) || (h & 1))
return AVERROR_INVALIDDATA;
- avctx->coded_width = FFALIGN(w, 16);
- avctx->coded_height = FFALIGN(h, 16);
-
if (w != avctx->width || h != avctx->height) {
av_log(avctx, AV_LOG_WARNING, "picture resolution change: %ix%i -> %ix%i\n",
avctx->width, avctx->height, w, h);
@@ -377,6 +374,9 @@ static int decode_frame(AVCodecContext *avctx,
return ret;
}
+ avctx->coded_width = FFALIGN(w, 16);
+ avctx->coded_height = FFALIGN(h, 16);
+
enum AVPixelFormat pix_fmt = AV_PIX_FMT_BAYER_RGGB16;
if (pix_fmt != s->pix_fmt) {
s->pix_fmt = pix_fmt;
commit 34c39367aa8a39fb2f26f583de269e762e334787
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 15 00:48:26 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:48 2025 +0200
.forgejo/CODEOWNERS: remove reference to secret apparently uncommited code
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 7d606ef0ccf2946a4a21ab1ec23486cadc21864b)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index c7d41e5485..a4ea6dd50f 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -53,7 +53,6 @@ libavutil/.*d3d12va.* @jianhuaw
libavutil/eval.* @michaelni
libavutil/iamf.* @jamrial
libavutil/integer.* @michaelni
-libavutil/kiss99.* @michaelni
libavutil/lfg.* @michaelni
libavutil/lls.* @michaelni
libavutil/md5.* @michaelni
commit 3cab0095192aa2bc51f8f8060ed4ccbf9caf3c87
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Wed Aug 13 00:59:20 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:47 2025 +0200
fftools/ffmpeg_mux_init: Use 64bit for score computation in map_auto_video()
Fixes: signed integer overflow: 10 * 1952737655 cannot be represented in type 'int'
Fixes: PoC_avi_demux
Found-by: 2ourc3 (Salim LARGO)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit cdbb5f1b93352f9e7eceb1562ad283a78b546091)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 4a973e5286..17977eb07f 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -1593,7 +1593,7 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
{
AVFormatContext *oc = mux->fc;
InputStream *best_ist = NULL;
- int best_score = 0;
+ int64_t best_score = 0;
int qcr;
/* video: highest resolution */
@@ -1604,16 +1604,16 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
for (int j = 0; j < nb_input_files; j++) {
InputFile *ifile = input_files[j];
InputStream *file_best_ist = NULL;
- int file_best_score = 0;
+ int64_t file_best_score = 0;
for (int i = 0; i < ifile->nb_streams; i++) {
InputStream *ist = ifile->streams[i];
- int score;
+ int64_t score;
if (ist->user_set_discard == AVDISCARD_ALL ||
ist->st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
continue;
- score = ist->st->codecpar->width * ist->st->codecpar->height
+ score = ist->st->codecpar->width * (int64_t)ist->st->codecpar->height
+ 100000000 * !!(ist->st->event_flags & AVSTREAM_EVENT_FLAG_NEW_PACKETS)
+ 5000000*!!(ist->st->disposition & AV_DISPOSITION_DEFAULT);
if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
commit 3e05b895900e2cbfcd2dafb77de8fbcf14c71732
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Mon Aug 11 20:06:25 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:47 2025 +0200
tools/merge-all-source-plugins: Fix merge_internal() return code
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit b5b306ca31ba4cc035f4cea3fd82ae43dccd38f3)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/tools/merge-all-source-plugins b/tools/merge-all-source-plugins
index 911f984624..7af8d45ed7 100755
--- a/tools/merge-all-source-plugins
+++ b/tools/merge-all-source-plugins
@@ -2,8 +2,11 @@
#If a version is set then we only try merging a source plugin with matching version as a generic one could change the ABI to master HEAD
merge_internal(){ # $1=repository, $2=refspec
- [ -n "$version" ] && git pull --no-rebase --log --stat --commit --no-edit $1 sourceplugin-$2-$version
- [ -z "$version" ] && git pull --no-rebase --log --stat --commit --no-edit $1 sourceplugin-$2
+ branch="sourceplugin-$2"
+ if [ -n "$version" ] ; then
+ branch="$branch-$version"
+ fi
+ git pull --no-rebase --log --stat --commit --no-edit "$1" "$branch"
}
unset succeeded failed version
commit 4a3e5ea8d2ebad2b930c503d08fbca05f6209d89
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 12 12:43:06 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:47 2025 +0200
tools: Split the list of source plugins out of "merge-all-source-plugins"
(cherry picked from commit fd31df43067fe21cc1e4fabd07ee9d6a26cb7264)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/tools/merge-all-source-plugins b/tools/merge-all-source-plugins
index a3812a90a6..911f984624 100755
--- a/tools/merge-all-source-plugins
+++ b/tools/merge-all-source-plugins
@@ -31,7 +31,16 @@ git diff --cached --exit-code >/dev/null ||\
version="8.0"
-merge "https://github.com/michaelni/FFmpeg.git" "libpostproc"
+[ $# -ne 1 ] &&\
+ error "Usage: $0 source-plugins.txt"
+
+while IFS=' ' read -r a b; do
+ case "$a" in
+ ''|'#'*) continue ;;
+ esac
+
+ merge "$a" "$b"
+done < "$1"
[ -n "$version" ] && echo version: $version
[ -n "$succeeded" ] && echo Succeeded merging: $succeeded
diff --git a/tools/source-plugins.txt b/tools/source-plugins.txt
new file mode 100644
index 0000000000..6940140ccc
--- /dev/null
+++ b/tools/source-plugins.txt
@@ -0,0 +1,4 @@
+# List of source plugins
+
+# libpostproc, GPL, maintained by Michael Niedermayer
+https://github.com/michaelni/FFmpeg.git libpostproc
commit 263e819aa45cd3c48bf6887be02b4ec504c02048
Author: Jiasheng Jiang <jiashengjiangcool(a)gmail.com>
AuthorDate: Thu Aug 7 14:50:10 2025 +0000
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:46 2025 +0200
libavfilter/dnn/dnn_backend_tf: Remove redundant av_freep() to avoid double free
Remove redundant av_freep() to avoid double free since task will be freed in dnn_free_model_tf() after the success of ff_queue_push_back().
Fixes: af052f9066 ("lavfi/dnn: fix mem leak in TF backend error handle")
Signed-off-by: Jiasheng Jiang <jiashengjiangcool(a)gmail.com>
(cherry picked from commit b8d5f65b9e89d893f27cf00799dbc15fc0ca2f8e)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavfilter/dnn/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c
index 6ca7fb6910..2c33691f96 100644
--- a/libavfilter/dnn/dnn_backend_tf.c
+++ b/libavfilter/dnn/dnn_backend_tf.c
@@ -833,14 +833,12 @@ static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_p
ret = extract_lltask_from_task(task, tf_model->lltask_queue);
if (ret != 0) {
- av_freep(&task);
av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
return ret;
}
request = ff_safe_queue_pop_front(tf_model->request_queue);
if (!request) {
- av_freep(&task);
av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
return AVERROR(EINVAL);
}
commit 23655160ea4acb2601db6a90e1fb40fe7b4cabc4
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 8 12:43:46 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:46 2025 +0200
avcodec/dxv: Use av_fast_realloc() for op_data
makes things consistent
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 373bd80b16643e349d229e2479fad565dba129a5)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index 052fe0ac6c..dd82e450b1 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -46,6 +46,7 @@ typedef struct DXVContext {
int64_t ctex_size; // Chroma texture size
uint8_t *op_data[4]; // Opcodes
+ unsigned op_data_size[4];
int64_t op_size[4]; // Opcodes size
} DXVContext;
@@ -1003,9 +1004,11 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame,
memset(ctx->ctex_data + old_size, 0, ctx->ctex_data_size - old_size);
for (i = 0; i < 4; i++) {
- ret = av_reallocp(&ctx->op_data[i], ctx->op_size[i]);
- if (ret < 0)
- return ret;
+ old_size = ctx->op_data_size[i];
+ ptr = av_fast_realloc(ctx->op_data[i], &ctx->op_data_size[i], ctx->op_size[i]);
+ if (!ptr)
+ return AVERROR(ENOMEM);
+ ctx->op_data[i] = ptr;
}
}
@@ -1101,6 +1104,7 @@ static av_cold int dxv_close(AVCodecContext *avctx)
av_freep(&ctx->op_data[1]);
av_freep(&ctx->op_data[2]);
av_freep(&ctx->op_data[3]);
+ memset(ctx->op_data_size, 0, sizeof(ctx->op_data_size));
return 0;
}
commit f2507dba3b4c40c5d2d0135a7847db448d3c3527
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Thu Aug 7 19:56:53 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:46 2025 +0200
avcodec/dxv: Use av_fast_realloc() and clear all new space
The code writing in the buffer has a wide range of error checks
which simply leave it partly uninitialized.
Initializing it on allocation ensures no sensitive data leaks and that
bugs are more reliably reproduceable
Fixes: use of uninitialized memory
Fixes: 435225510/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-4521918634196992
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 4a0b793737ec1a118d2119a677fa17926def01bc)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index 0f8de13c25..052fe0ac6c 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -38,6 +38,7 @@ typedef struct DXVContext {
GetByteContext gbc;
uint8_t *tex_data; // Compressed texture
+ unsigned tex_data_size;
uint8_t *ctex_data; // Compressed chroma texture
unsigned ctex_data_size;
@@ -972,9 +973,14 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame,
ctx->tex_size = avctx->coded_width / (texdsp_ctx.raw_ratio / (avctx->pix_fmt == AV_PIX_FMT_RGBA ? 4 : 1)) *
avctx->coded_height / TEXTURE_BLOCK_H *
texdsp_ctx.tex_ratio;
- ret = av_reallocp(&ctx->tex_data, ctx->tex_size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (ret < 0)
- return ret;
+ unsigned old_size = ctx->tex_data_size;
+ void *ptr = av_fast_realloc(ctx->tex_data, &ctx->tex_data_size, ctx->tex_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!ptr)
+ return AVERROR(ENOMEM);
+ ctx->tex_data = ptr;
+
+ if (ctx->tex_data_size > old_size)
+ memset(ctx->tex_data + old_size, 0, ctx->tex_data_size - old_size);
if (avctx->pix_fmt != AV_PIX_FMT_RGBA) {
int i;
@@ -1086,6 +1092,8 @@ static av_cold int dxv_close(AVCodecContext *avctx)
DXVContext *ctx = avctx->priv_data;
av_freep(&ctx->tex_data);
+ ctx->tex_data_size = 0;
+
av_freep(&ctx->ctex_data);
ctx->ctex_data_size = 0;
commit da3f5273fce6c5dbb68077fbec346cc4b1a3e9f0
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 8 12:25:55 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:45 2025 +0200
avcodec/dxv: Clear ctex
same issue as with tex
Fixes: 431665305/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5339599339847680
Fixes: use of uninitialized memory
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 4e5f25c0a50ac17e20ddc3549dbff0976a5826b9)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index 20d353b29b..0f8de13c25 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -39,6 +39,7 @@ typedef struct DXVContext {
uint8_t *tex_data; // Compressed texture
uint8_t *ctex_data; // Compressed chroma texture
+ unsigned ctex_data_size;
int64_t tex_size; // Texture size
int64_t ctex_size; // Chroma texture size
@@ -987,9 +988,14 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame,
ctx->op_size[2] = avctx->coded_width * avctx->coded_height / 32;
ctx->op_size[3] = avctx->coded_width * avctx->coded_height / 16;
- ret = av_reallocp(&ctx->ctex_data, ctx->ctex_size + AV_INPUT_BUFFER_PADDING_SIZE);
- if (ret < 0)
- return ret;
+ old_size = ctx->ctex_data_size;
+ ptr = av_fast_realloc(ctx->ctex_data, &ctx->ctex_data_size, ctx->ctex_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!ptr)
+ return AVERROR(ENOMEM);
+ ctx->ctex_data = ptr;
+ if (old_size < ctx->ctex_data_size)
+ memset(ctx->ctex_data + old_size, 0, ctx->ctex_data_size - old_size);
+
for (i = 0; i < 4; i++) {
ret = av_reallocp(&ctx->op_data[i], ctx->op_size[i]);
if (ret < 0)
@@ -1081,6 +1087,8 @@ static av_cold int dxv_close(AVCodecContext *avctx)
av_freep(&ctx->tex_data);
av_freep(&ctx->ctex_data);
+ ctx->ctex_data_size = 0;
+
av_freep(&ctx->op_data[0]);
av_freep(&ctx->op_data[1]);
av_freep(&ctx->op_data[2]);
commit 6049800a106b6ff8c6a0e062c5146e554a54c21f
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 8 12:25:55 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:45 2025 +0200
avcodec/dxv: Check that we initialize op_data
Fixes: 431665305/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5339599339847680
Fixes: use of uninitialized memory
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 6a8c41dcacbba011e553fbf35518577321d1aadb)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index 0b8e077ad6..20d353b29b 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -274,7 +274,9 @@ static int dxv_decompress_opcodes(GetByteContext *gb, void *dstp, size_t op_size
if ((flag & 3) == 0) {
bytestream2_skip(gb, 1);
- bytestream2_get_buffer(gb, dstp, op_size);
+ int read_size = bytestream2_get_buffer(gb, dstp, op_size);
+ if (read_size != op_size)
+ return AVERROR_INVALIDDATA;
} else if ((flag & 3) == 1) {
bytestream2_skip(gb, 1);
memset(dstp, bytestream2_get_byte(gb), op_size);
commit e726f7af17b3ea160b6ce8482f3065e4c36c3f97
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Thu Aug 7 19:38:30 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:45 2025 +0200
avcodec/sanm: Check mv in codec48_block()
Fixes: out of array read
Fixes: 436943287/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SANM_fuzzer-5011037029203968
This issue did oddly enough, not replicate
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit d5bdb0b705ce96739e812ca5317361674359369c)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c
index d345f58846..a066a864eb 100644
--- a/libavcodec/sanm.c
+++ b/libavcodec/sanm.c
@@ -1427,8 +1427,18 @@ static void c48_4to8(uint8_t *dst, const uint8_t *src, const uint16_t w)
}
}
-static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
- const uint16_t w)
+static int check_mv(int x, int y, const uint16_t w, int h, int blocksize, int mvofs) {
+ if (mvofs < -x + -y*w)
+ return AVERROR_INVALIDDATA;
+
+ if (mvofs > w-x-blocksize + w*(h-y-blocksize))
+ return AVERROR_INVALIDDATA;
+
+ return 0;
+}
+
+static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db, int x, int y,
+ const uint16_t w, int h)
{
uint8_t opc, sb[16];
int i, j, k, l;
@@ -1453,6 +1463,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
if (bytestream2_get_bytes_left(&ctx->gb) < 2)
return 1;
mvofs = bytestream2_get_le16(&ctx->gb);
+ if (check_mv(x, y, w, h, 8, mvofs))
+ return 1;
for (i = 0; i < 8; i++) {
ofs = w * i;
for (k = 0; k < 8; k++)
@@ -1480,6 +1492,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
for (k = 0; k < 8; k += 4) {
opc = bytestream2_get_byteu(&ctx->gb);
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
+ if (check_mv(x+k, y+i, w, h, 4, mvofs))
+ return 1;
for (j = 0; j < 4; j++) {
ofs = (w * (j + i)) + k;
for (l = 0; l < 4; l++)
@@ -1494,6 +1508,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
for (i = 0; i < 8; i += 4) {
for (k = 0; k < 8; k += 4) {
mvofs = bytestream2_get_le16(&ctx->gb);
+ if (check_mv(x+k, y+i, w, h, 4, mvofs))
+ return 1;
for (j = 0; j < 4; j++) {
ofs = (w * (j + i)) + k;
for (l = 0; l < 4; l++)
@@ -1516,6 +1532,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
ofs = (w * i) + j;
opc = bytestream2_get_byteu(&ctx->gb);
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
+ if (check_mv(x+j, y+i, w, h, 2, mvofs))
+ return 1;
for (l = 0; l < 2; l++) {
*(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
*(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
@@ -1530,6 +1548,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
for (j = 0; j < 8; j += 2) {
ofs = w * i + j;
mvofs = bytestream2_get_le16(&ctx->gb);
+ if (check_mv(x+j, y+i, w, h, 2, mvofs))
+ return 1;
for (l = 0; l < 2; l++) {
*(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
*(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
@@ -1548,6 +1568,8 @@ static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
break;
default: // copy 8x8 block from prev, c37_mv from source
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
+ if (check_mv(x, y, w, h, 8, mvofs))
+ return 1;
for (i = 0; i < 8; i++) {
ofs = i * w;
for (l = 0; l < 8; l++)
@@ -1613,7 +1635,7 @@ static int old_codec48(SANMVideoContext *ctx, int width, int height)
if (seq == ctx->prev_seq + 1) {
for (j = 0; j < height; j += 8) {
for (i = 0; i < width; i += 8) {
- if (codec48_block(ctx, dst + i, prev + i, width))
+ if (codec48_block(ctx, dst + i, prev + i, i, j, width, height))
return AVERROR_INVALIDDATA;
}
dst += width * 8;
commit de76fb27a6e6da0431154ce9093933281a38a889
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Sat Aug 9 14:05:19 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:44 2025 +0200
avcodec/exr: Check for pixel type consistency in DWA
Fixes: out of array access
Fixes: BIGSLEEP-436511754/testcase.exr
Found-by: Google Big Sleep
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 0469d68acb52081ca8385b844b9650398242be0f)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/exr.c b/libavcodec/exr.c
index dea612a42b..67f971ff35 100644
--- a/libavcodec/exr.c
+++ b/libavcodec/exr.c
@@ -2086,6 +2086,17 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture,
if ((ret = decode_header(s, picture)) < 0)
return ret;
+ if (s->compression == EXR_DWAA ||
+ s->compression == EXR_DWAB) {
+ for (int i = 0; i<s->nb_channels; i++) {
+ EXRChannel *channel = &s->channels[i];
+ if (channel->pixel_type != s->pixel_type) {
+ avpriv_request_sample(s->avctx, "mixed pixel type DWA");
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+ }
+
switch (s->pixel_type) {
case EXR_HALF:
if (s->channel_offsets[3] >= 0) {
commit 995d329cf9213cc445a2cea31b8f2394d3475d8c
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Sun Aug 3 16:46:10 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:44 2025 +0200
avcodec/d3d12va_encode: fix label followed by a declaration warning
Fixes: d3d12va_encode.c: warning: label followed by a declaration is a
C23 extension
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit ac6db22e372460f3462d16eacdf9b7611cc3c0af)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c
index 880002ce55..1ecaee3b6d 100644
--- a/libavcodec/d3d12va_encode.c
+++ b/libavcodec/d3d12va_encode.c
@@ -973,8 +973,7 @@ rc_mode_found:
case RC_MODE_CQP:
// cqp ConfigParams will be updated in ctx->codec->configure.
break;
-
- case RC_MODE_CBR:
+ case RC_MODE_CBR: {
D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR *cbr_ctl;
ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_CBR);
@@ -995,8 +994,8 @@ rc_mode_found:
ctx->rc.ConfigParams.pConfiguration_CBR = cbr_ctl;
break;
-
- case RC_MODE_VBR:
+ }
+ case RC_MODE_VBR: {
D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR *vbr_ctl;
ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_VBR);
@@ -1018,8 +1017,8 @@ rc_mode_found:
ctx->rc.ConfigParams.pConfiguration_VBR = vbr_ctl;
break;
-
- case RC_MODE_QVBR:
+ }
+ case RC_MODE_QVBR: {
D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR *qvbr_ctl;
ctx->rc.ConfigParams.DataSize = sizeof(D3D12_VIDEO_ENCODER_RATE_CONTROL_QVBR);
@@ -1039,7 +1038,7 @@ rc_mode_found:
ctx->rc.ConfigParams.pConfiguration_QVBR = qvbr_ctl;
break;
-
+ }
default:
break;
}
commit 81dcb6781390bd3225fc2250cb0f73ee6487eb00
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Sat Aug 9 17:15:51 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:44 2025 +0200
avcodec/libvorbisdec: avoid overflow when assinging sample rate from long to int
Fixes: 416134551/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_LIBVORBIS_DEC_fuzzer-6096101407260672
Found-by: OSS-Fuzz
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit 2287a19abbd80d25b411a3028969c55c4b0b8c88)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/libvorbisdec.c b/libavcodec/libvorbisdec.c
index 7922261b2f..326ed4b4fe 100644
--- a/libavcodec/libvorbisdec.c
+++ b/libavcodec/libvorbisdec.c
@@ -114,6 +114,12 @@ static av_cold int oggvorbis_decode_init(AVCodecContext *avccontext)
}
}
+ if (context->vi.rate <= 0 || context->vi.rate > INT_MAX) {
+ av_log(avccontext, AV_LOG_ERROR, "vorbis rate is invalid\n");
+ ret = AVERROR_INVALIDDATA;
+ goto error;
+ }
+
av_channel_layout_uninit(&avccontext->ch_layout);
avccontext->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;
avccontext->ch_layout.nb_channels = context->vi.channels;
commit bde02336a44086d628cb8e577a915fa9a6bf6088
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Sat Aug 9 17:09:57 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:43 2025 +0200
avcodec/g726: init missing sample rate
Fixes: 416134551/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_ADPCM_G726_DEC_fuzzer-5695764455292928
Found-by: OSS-Fuzz
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit c2f7dae70d27a8f5ca1e3fa43d96ff5c8bf032fa)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/g726.c b/libavcodec/g726.c
index 5491b7eb7a..f41df3073f 100644
--- a/libavcodec/g726.c
+++ b/libavcodec/g726.c
@@ -455,6 +455,8 @@ static av_cold int g726_decode_init(AVCodecContext *avctx)
g726_reset(c);
avctx->sample_fmt = AV_SAMPLE_FMT_S16;
+ if (!avctx->sample_rate)
+ avctx->sample_rate = 8000;
return 0;
}
commit 6b1f994e43b7c0648f91fc6a14ee5c090c99e1fc
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Sat Aug 9 16:49:17 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:43 2025 +0200
avformat/lrcdec: limit input timestamp range to avoid overflows
Fixes: clusterfuzz-testcase-ffmpeg_dem_LRC_fuzzer-5226140131459072
Found-by: OSS-Fuzz
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit c74bc74398e7a1e235fdf51d0dd2dfb942626c82)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c
index 7941c02c5d..e3e091a61a 100644
--- a/libavformat/lrcdec.c
+++ b/libavformat/lrcdec.c
@@ -78,7 +78,7 @@ static int64_t count_ts(const char *p)
static int64_t read_ts(const char *p, int64_t *start)
{
int64_t offset = 0;
- uint64_t mm;
+ uint32_t mm;
double ss;
char prefix[3];
@@ -88,8 +88,8 @@ static int64_t read_ts(const char *p, int64_t *start)
if(p[offset] != '[') {
return 0;
}
- int ret = sscanf(p, "%2[[-]%"SCNu64":%lf]", prefix, &mm, &ss);
- if (ret != 3 || prefix[0] != '[') {
+ int ret = sscanf(p, "%2[[-]%"SCNu32":%lf]", prefix, &mm, &ss);
+ if (ret != 3 || prefix[0] != '[' || ss < 0 || ss > 60) {
return 0;
}
*start = (mm * 60 + ss) * AV_TIME_BASE;
commit 5051753833d5123707e58e2571d0831e3f9278be
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 8 23:19:03 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:42 2025 +0200
avcodec/scpr3: Clear clr
clr is passing into decode_run_p() its not used when not set
but this possibly triggers msan (it doesnt locally)
Fixes?: use of uninintialized memory
Fixes?: 436997807/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_SCPR_fuzzer-6253316466606080
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 354226037646d44701f0f2a84749fb2ea303f043)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/scpr3.c b/libavcodec/scpr3.c
index e91c198308..369d2653c2 100644
--- a/libavcodec/scpr3.c
+++ b/libavcodec/scpr3.c
@@ -1167,7 +1167,7 @@ static int decompress_p3(AVCodecContext *avctx,
}
} else {
int run, bx = x * 16 + sx1, by = y * 16 + sy1;
- uint32_t clr, ptype = 0, r, g, b;
+ uint32_t clr = 0, ptype = 0, r, g, b;
if (bx >= avctx->width)
return AVERROR_INVALIDDATA;
commit a676267a2c29581346c62dbfdfc9268aa2fd2658
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Fri Aug 8 15:03:56 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:42 2025 +0200
avcodec/ilbcdec: Clear cbvec when used with create_augmented_vector()
Fixes: use of uninitialized memory
Fixes: 42538134/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_ILBC_fuzzer-6322020827070464
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
(cherry picked from commit 9686fdd729a9caeeac0dc84dca2a65e4c9e5460b)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/ilbcdec.c b/libavcodec/ilbcdec.c
index a9dfa857a2..8b495a2f8e 100644
--- a/libavcodec/ilbcdec.c
+++ b/libavcodec/ilbcdec.c
@@ -675,6 +675,7 @@ static void get_codebook(int16_t * cbvec, /* (o) Constructed codebook vector *
/* get vector */
memcpy(cbvec, mem + lMem - k, cbveclen * 2);
} else if (index < base_size) {
+ memset(cbvec, 0, cbveclen * 2);
/* Calculate lag */
@@ -701,6 +702,7 @@ static void get_codebook(int16_t * cbvec, /* (o) Constructed codebook vector *
filter_mafq12(&mem[memIndTest + 4], cbvec, kCbFiltersRev, CB_FILTERLEN, cbveclen);
} else {
+ memset(cbvec, 0, cbveclen * 2);
/* interpolated vectors */
/* Stuff zeros outside memory buffer */
memIndTest = lMem - cbveclen - CB_FILTERLEN;
commit bd55bf8300ef2b97c9316d7b2674b07142163e70
Author: Kacper Michajłow <kasper93(a)gmail.com>
AuthorDate: Wed Aug 6 00:36:10 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:42 2025 +0200
avformat/mov: clear old name from infe
heif_items are reused and to avoid leaking memory or using stale name,
clear it first.
Fixes: 432505829/clusterfuzz-testcase-minimized-ffmpeg_dem_MOV_fuzzer-6654363487764480
Found-by: OSS-Fuzz
Signed-off-by: Kacper Michajłow <kasper93(a)gmail.com>
(cherry picked from commit 3bf8bf965fb69f873e52d34a85d1ecb722a9fe7f)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 94b741f056..86037c6712 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -8957,6 +8957,7 @@ static int mov_read_infe(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return AVERROR(ENOMEM);
}
+ av_freep(&item->name);
av_bprint_finalize(&item_name, ret ? &item->name : NULL);
item->item_id = item_id;
item->type = item_type;
commit 64c71cbe4eefcd59559238758bf760b3197469ef
Author: Leon Grutters <gruttersleonbot2(a)gmail.com>
AuthorDate: Sat Aug 9 18:43:13 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:41 2025 +0200
doc/community.texi: fix spelling error
Fixes: 262d41c804 ("all: fix typos found by codespell")
Signed-off-by: Leon Grutters <gruttersleonbot2(a)gmail.com>
(cherry picked from commit 777408d149b120a730045a71fbed2b8e0ef7807c)
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/doc/community.texi b/doc/community.texi
index 4274da5d63..97a49f15ed 100644
--- a/doc/community.texi
+++ b/doc/community.texi
@@ -123,7 +123,7 @@ Internally, the TC should take decisions with a majority, or using ranked-choice
Each TC member must vote on such decision according to what is, in their view, best for the project.
-If a TC member feels they are affected by a conflict of interest with regards to the case, they should announce it and recurse themselves from the TC
+If a TC member feels they are affected by a conflict of interest with regards to the case, they should announce it and recuse themselves from the TC
discussion and vote.
A conflict of interest is presumed to occur when a TC member has a personal interest (e.g. financial) in a specific outcome of the case.
commit 2feaad5cb977362610305941c689c8d76a8e80f3
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Mon Aug 11 19:48:33 2025 +0200
Commit: Michael Niedermayer <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 17:56:41 2025 +0200
tools/merge-all-source-plugins: set version
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/tools/merge-all-source-plugins b/tools/merge-all-source-plugins
index cd030cdabe..a3812a90a6 100755
--- a/tools/merge-all-source-plugins
+++ b/tools/merge-all-source-plugins
@@ -29,7 +29,7 @@ git diff --exit-code >/dev/null ||\
git diff --cached --exit-code >/dev/null ||\
error "Please commit local changes first"
-#version="12.34"
+version="8.0"
merge "https://github.com/michaelni/FFmpeg.git" "libpostproc"
-----------------------------------------------------------------------
Summary of changes:
.forgejo/CODEOWNERS | 1 -
doc/community.texi | 2 +-
fftools/ffmpeg_mux_init.c | 8 +++---
libavcodec/aac/aacdec.c | 31 +++++++++++++++++++--
libavcodec/aac/aacdec.h | 4 +--
libavcodec/aac/aacdec_usac.c | 32 +++++++++++++--------
libavcodec/d3d12va_encode.c | 13 ++++-----
libavcodec/dxv.c | 48 ++++++++++++++++++++++++--------
libavcodec/exr.c | 11 ++++++++
libavcodec/g726.c | 2 ++
libavcodec/ilbcdec.c | 2 ++
libavcodec/libvorbisdec.c | 6 ++++
libavcodec/lzf.c | 23 +++++++++++----
libavcodec/lzf.h | 2 +-
libavcodec/notchlc.c | 5 ++--
libavcodec/prores_raw.c | 6 ++--
libavcodec/rv60dec.c | 2 ++
libavcodec/sanm.c | 33 ++++++++++++++++++++--
libavcodec/scpr3.c | 2 +-
libavcodec/utvideodec.c | 2 +-
libavfilter/dnn/dnn_backend_tf.c | 2 --
libavformat/lrcdec.c | 8 +++---
libavformat/mov.c | 11 ++++----
libavformat/tls_openssl.c | 10 +++----
libswscale/swscale_internal.h | 4 +--
tests/ref/fate/filter-pixdesc-xyz12be | 2 +-
tests/ref/fate/filter-pixdesc-xyz12le | 2 +-
tests/ref/fate/filter-pixfmts-copy | 4 +--
tests/ref/fate/filter-pixfmts-crop | 4 +--
tests/ref/fate/filter-pixfmts-field | 4 +--
tests/ref/fate/filter-pixfmts-fieldorder | 4 +--
tests/ref/fate/filter-pixfmts-hflip | 4 +--
tests/ref/fate/filter-pixfmts-il | 4 +--
tests/ref/fate/filter-pixfmts-null | 4 +--
tests/ref/fate/filter-pixfmts-scale | 4 +--
tests/ref/fate/filter-pixfmts-transpose | 4 +--
tests/ref/fate/filter-pixfmts-vflip | 4 +--
tests/ref/pixfmt/xyz12le | 2 +-
tools/merge-all-source-plugins | 20 ++++++++++---
tools/source-plugins.txt | 4 +++
40 files changed, 236 insertions(+), 104 deletions(-)
create mode 100644 tools/source-plugins.txt
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. 4c3f94f265 doc/APIChanges: document EXIF API additions
by ffmpeg-git@ffmpeg.org 19 Aug '25
by ffmpeg-git@ffmpeg.org 19 Aug '25
19 Aug '25
The branch, master has been updated
via 4c3f94f2651ce4d6834fbef10e5c287f25720ac9 (commit)
via fe496b0308f1911138698b92bbafac582fa455d7 (commit)
via 535a07d14e16eb2930d2b1e16605eafa95959139 (commit)
via 8132ee046d1a03c5913759c50fce7beda8d3d627 (commit)
via 5d4f873ff31f86f9ae1927f37ca0fcb78da70512 (commit)
via 1e816ebefee0ef479fa76bd2a1fb9ec5d5e807df (commit)
via 93a8091015978c44462626409af71789080bbef4 (commit)
via 303f60684f25ce63951480ef0f759acf41aac938 (commit)
via 5caaadee7954f2014d9e0a17d1edfb961893bcf1 (commit)
via d3190a64c366a79091fe47fddf93c09a7d988802 (commit)
via 44af3829796427b89c4b76b56652cc5932ada616 (commit)
via f5ad1c910c168d05cb01315773ab0bd094c9372f (commit)
via e3aa1154aab2656c91ce61915f79516d9b563b61 (commit)
via c6cc2115f45ac26ae42442f8796996eb410f4028 (commit)
via 52dba25661305e3c4a6209d46aea43cd327c960e (commit)
via bfb17d26306592c85cf0c4e909099c621177b062 (commit)
via ba2ea285e0f270a0a885b414cafaace6a89b9a91 (commit)
via ad77345a5d14862f4701e5ad422b03b14934a5b9 (commit)
via bb90b262d6d23f1bca3587a48abc15b951cbbf05 (commit)
via a99fff4e2d4058c57599ba0b968862af82bdad5b (commit)
from a6b5a382dd7ecdd27c5d0ebba688e1db409d18fd (commit)
- Log -----------------------------------------------------------------
commit 4c3f94f2651ce4d6834fbef10e5c287f25720ac9
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Fri Jul 25 10:14:37 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:32:06 2025 -0400
doc/APIChanges: document EXIF API additions
Many of these additions are in separate commits in one set, so in the
interest of clarity, the API changes are all documented in one commmit
here.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/doc/APIchanges b/doc/APIchanges
index 1ed8337d04..4b32279e35 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,23 @@ The last version increases of all libraries were on 2025-03-28
API changes, most recent first:
+2025-08-19 - ad77345a5d1..fe496b0308f - lavc 62.13.100 - exif.h
+ Add:
+ - enum AVTiffDataType, enum AVExifHeaderMode
+ - struct AVExifMetadata, struct AVExifEntry
+ - av_exif_get_tag_name, av_exif_get_tag_id,
+ av_exif_set_entry, av_exif_get_entry,
+ av_exif_remove_entry, av_exif_parse_buffer,
+ av_exif_write, av_exif_free,
+ av_exif_ifd_to_dict, av_exif_clone_ifd,
+ av_exif_matrix_to_orientation,
+ av_exif_orientation_to_matrix,
+ Deprecate:
+ - avpriv_exif_decode_ifd (deprecated behind FF_API_OLD_EXIF)
+
+2025-08-19 - bb90b262d6d - lavu 60.10.100 - frame.h
+ Add AV_FRAME_DATA_EXIF.
+
2025-07-29 - 1c85a3832af - lavc 62.10.100 - smpte_436m.h
Add a new public header smpte_436m.h with API for
manipulating AV_CODEC_ID_SMPTE_436M_ANC data.
commit fe496b0308f1911138698b92bbafac582fa455d7
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Sun Apr 13 05:47:54 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/libjxlenc: apply displaymatrix side data orientation to output
If the output is tagged with AV_FRAME_DATA_DISPLAYMATRIX we will read
it and apply it to the output file.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c
index 40d5f760b1..4f05f015e2 100644
--- a/libavcodec/libjxlenc.c
+++ b/libavcodec/libjxlenc.c
@@ -28,6 +28,7 @@
#include "libavutil/avutil.h"
#include "libavutil/csp.h"
+#include "libavutil/display.h"
#include "libavutil/error.h"
#include "libavutil/frame.h"
#include "libavutil/libm.h"
@@ -40,6 +41,7 @@
#include "avcodec.h"
#include "encode.h"
#include "codec_internal.h"
+#include "exif_internal.h"
#include <jxl/encode.h>
#include <jxl/thread_parallel_runner.h>
@@ -322,10 +324,14 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame,
{
LibJxlEncodeContext *ctx = avctx->priv_data;
AVFrameSideData *sd;
+ int32_t *matrix = (int32_t[9]){ 0 };
+ int ret = 0;
const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format);
JxlBasicInfo info;
JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt;
int bits_per_sample;
+ int orientation;
+ AVBufferRef *exif_buffer = NULL;
#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0)
JxlBitDepth jxl_bit_depth;
#endif
@@ -368,8 +374,51 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame,
/* bitexact lossless requires there to be no XYB transform */
info.uses_original_profile = ctx->distance == 0.0 || !ctx->xyb;
- /* libjxl doesn't support negative linesizes so we use orientation to work around this */
- info.orientation = frame->linesize[0] >= 0 ? JXL_ORIENT_IDENTITY : JXL_ORIENT_FLIP_VERTICAL;
+ sd = av_frame_get_side_data(frame, AV_FRAME_DATA_EXIF);
+ if (sd) {
+ AVExifMetadata ifd = { 0 };
+ AVExifEntry *orient = NULL;
+ uint16_t tag = av_exif_get_tag_id("Orientation");
+ ret = av_exif_parse_buffer(avctx, sd->data, sd->size, &ifd, AV_EXIF_TIFF_HEADER);
+ if (ret >= 0)
+ ret = ff_exif_sanitize_ifd(avctx, frame, &ifd);
+ if (ret >= 0)
+ ret = av_exif_get_entry(avctx, &ifd, tag, 0, &orient);
+ if (ret >= 0 && orient && orient->value.uint[0] >= 1 && orient->value.uint[0] <= 8) {
+ av_exif_orientation_to_matrix(matrix, orient->value.uint[0]);
+ ret = av_exif_remove_entry(avctx, &ifd, tag, 0);
+ } else {
+ av_exif_orientation_to_matrix(matrix, 1);
+ }
+ if (ret >= 0)
+ ret = av_exif_write(avctx, &ifd, &exif_buffer, AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_WARNING, "unable to process EXIF frame data\n");
+ } else {
+ sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
+ if (sd)
+ matrix = (int32_t *) sd->data;
+ else
+ av_exif_orientation_to_matrix(matrix, 1);
+ }
+
+ /* av_display_matrix_flip is a right-multipilcation */
+ /* i.e. flip is applied before the previous matrix */
+ if (frame->linesize < 0)
+ av_display_matrix_flip(matrix, 0, 1);
+
+ orientation = av_exif_matrix_to_orientation(matrix);
+ /* JPEG XL orientation flag agrees with EXIF for values 1-8 */
+ if (orientation) {
+ info.orientation = orientation;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "singular displaymatrix data\n");
+ info.orientation = frame->linesize[0] >= 0 ? JXL_ORIENT_IDENTITY : JXL_ORIENT_FLIP_VERTICAL;
+ }
+
+ /* restore the previous value */
+ if (frame->linesize < 0)
+ av_display_matrix_flip(matrix, 0, 1);
if (animated) {
info.have_animation = 1;
@@ -382,7 +431,8 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame,
if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n");
- return AVERROR_EXTERNAL;
+ ret = AVERROR_EXTERNAL;
+ goto end;
}
sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
@@ -399,6 +449,11 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame,
av_log(avctx, AV_LOG_WARNING, "Failed to set JxlBitDepth\n");
#endif
+ if (exif_buffer) {
+ if (JxlEncoderUseBoxes(ctx->encoder) != JXL_ENC_SUCCESS)
+ av_log(avctx, AV_LOG_WARNING, "Couldn't enable UseBoxes\n");
+ }
+
/* depending on basic info, level 10 might
* be required instead of level 5 */
if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) {
@@ -406,7 +461,9 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame,
av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n");
}
- return 0;
+end:
+ av_buffer_unref(&exif_buffer);
+ return ret;
}
/**
commit 535a07d14e16eb2930d2b1e16605eafa95959139
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Fri Jul 25 11:56:36 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/exif: add ff_exif_sanitize_ifd
This commit takes some of the sanitize code used by ff_exif_get_buffer
and exposes it as an ff_ API, so encoders who wish to modify the
sanitized IFD before calling av_exif_write can do so.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 15bbe7749b..48959eb9b3 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -1307,13 +1307,10 @@ int av_exif_orientation_to_matrix(int32_t *matrix, int orientation)
return 0;
}
-int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_ptr, enum AVExifHeaderMode header_mode)
+int ff_exif_sanitize_ifd(void *logctx, const AVFrame *frame, AVExifMetadata *ifd)
{
int ret = 0;
- AVBufferRef *buffer = NULL;
- AVFrameSideData *sd_exif = NULL;
AVFrameSideData *sd_orient = NULL;
- AVExifMetadata ifd = { 0 };
AVExifEntry *or = NULL;
AVExifEntry *iw = NULL;
AVExifEntry *ih = NULL;
@@ -1324,28 +1321,15 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
uint64_t h = frame->height;
int rewrite = 0;
- if (!buffer_ptr || *buffer_ptr)
- return AVERROR(EINVAL);
-
- sd_exif = av_frame_get_side_data(frame, AV_FRAME_DATA_EXIF);
sd_orient = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
- if (!sd_exif && !sd_orient)
- return 0;
-
if (sd_orient)
orientation = av_exif_matrix_to_orientation((int32_t *) sd_orient->data);
if (orientation != 1)
av_log(logctx, AV_LOG_DEBUG, "matrix contains nontrivial EXIF orientation: %" PRIu64 "\n", orientation);
- if (sd_exif) {
- ret = av_exif_parse_buffer(logctx, sd_exif->data, sd_exif->size, &ifd, AV_EXIF_TIFF_HEADER);
- if (ret < 0)
- goto end;
- }
-
- for (size_t i = 0; i < ifd.count; i++) {
- AVExifEntry *entry = &ifd.entries[i];
+ for (size_t i = 0; i < ifd->count; i++) {
+ AVExifEntry *entry = &ifd->entries[i];
if (entry->id == ORIENTATION_TAG && entry->count > 0 && entry->type == AV_TIFF_SHORT) {
or = entry;
continue;
@@ -1396,26 +1380,26 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
}
if (!or && orientation != 1) {
rewrite = 1;
- ret = av_exif_set_entry(logctx, &ifd, ORIENTATION_TAG, AV_TIFF_SHORT, 1, NULL, 0, &orientation);
+ ret = av_exif_set_entry(logctx, ifd, ORIENTATION_TAG, AV_TIFF_SHORT, 1, NULL, 0, &orientation);
if (ret < 0)
goto end;
}
if (!iw && w) {
rewrite = 1;
- ret = av_exif_set_entry(logctx, &ifd, IMAGE_WIDTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &w);
+ ret = av_exif_set_entry(logctx, ifd, IMAGE_WIDTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &w);
if (ret < 0)
goto end;
}
if (!ih && h) {
rewrite = 1;
- ret = av_exif_set_entry(logctx, &ifd, IMAGE_LENGTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &h);
+ ret = av_exif_set_entry(logctx, ifd, IMAGE_LENGTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &h);
if (ret < 0)
goto end;
}
if (!pw && w && w < 0xFFFFu || !ph && h && h < 0xFFFFu) {
AVExifMetadata *exif;
AVExifEntry *exif_entry;
- int exif_found = av_exif_get_entry(logctx, &ifd, EXIFIFD_TAG, 0, &exif_entry);
+ int exif_found = av_exif_get_entry(logctx, ifd, EXIFIFD_TAG, 0, &exif_entry);
rewrite = 1;
if (exif_found < 0)
goto end;
@@ -1423,12 +1407,12 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
exif = &exif_entry->value.ifd;
} else {
AVExifMetadata exif_new = { 0 };
- ret = av_exif_set_entry(logctx, &ifd, EXIFIFD_TAG, AV_TIFF_IFD, 1, NULL, 0, &exif_new);
+ ret = av_exif_set_entry(logctx, ifd, EXIFIFD_TAG, AV_TIFF_IFD, 1, NULL, 0, &exif_new);
if (ret < 0) {
av_exif_free(&exif_new);
goto end;
}
- exif = &ifd.entries[ifd.count - 1].value.ifd;
+ exif = &ifd->entries[ifd->count - 1].value.ifd;
}
if (!pw && w && w < 0xFFFFu) {
ret = av_exif_set_entry(logctx, exif, PIXEL_X_TAG, AV_TIFF_SHORT, 1, NULL, 0, &w);
@@ -1442,6 +1426,37 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
}
}
+ return rewrite;
+
+end:
+ return ret;
+}
+
+int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_ptr, enum AVExifHeaderMode header_mode)
+{
+ AVFrameSideData *sd_exif = NULL;
+ AVBufferRef *buffer = NULL;
+ AVExifMetadata ifd = { 0 };
+ int ret = 0;
+ int rewrite = 0;
+
+ if (!buffer_ptr || *buffer_ptr)
+ return AVERROR(EINVAL);
+
+ sd_exif = av_frame_get_side_data(frame, AV_FRAME_DATA_EXIF);
+ if (!sd_exif)
+ return 0;
+
+ ret = av_exif_parse_buffer(logctx, sd_exif->data, sd_exif->size, &ifd, AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ goto end;
+
+ rewrite = ff_exif_sanitize_ifd(logctx, frame, &ifd);
+ if (rewrite < 0) {
+ ret = rewrite;
+ goto end;
+ }
+
if (rewrite) {
ret = av_exif_write(logctx, &ifd, &buffer, header_mode);
if (ret < 0)
@@ -1456,6 +1471,9 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
}
}
+ av_exif_free(&ifd);
+ return rewrite;
+
end:
av_exif_free(&ifd);
return ret;
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
index 4fd147fb99..7d8e225250 100644
--- a/libavcodec/exif_internal.h
+++ b/libavcodec/exif_internal.h
@@ -59,6 +59,14 @@ int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum
*/
int ff_exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd);
+/**
+ * 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
+ * encoder that wishes to use ff_exif_get_buffer, but would prefer to modify the
+ * IFD after it is sanitized and call av_exif_write afterward.
+ */
+int ff_exif_sanitize_ifd(void *logctx, const AVFrame *frame, AVExifMetadata *ifd);
+
/**
* Gets all relevant side data, collects it into an IFD, and writes it into the
* corresponding buffer pointer. This includes both AV_FRAME_DATA_EXIF and other
commit 8132ee046d1a03c5913759c50fce7beda8d3d627
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Sat Apr 12 23:44:42 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/libjxldec: read EXIF metadata from container JPEG XL files
libjxl provides the ability to get EXIF metadata from an ISO18181-2
JPEG XL container file. This commit enables reading it and attaching
it to an AVFrame using the new EXIF API.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c
index 96c338d1b4..7ea5fc4787 100644
--- a/libavcodec/libjxldec.c
+++ b/libavcodec/libjxldec.c
@@ -29,6 +29,7 @@
#include "libavutil/common.h"
#include "libavutil/csp.h"
#include "libavutil/error.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixfmt.h"
@@ -37,6 +38,7 @@
#include "avcodec.h"
#include "codec_internal.h"
#include "decode.h"
+#include "exif_internal.h"
#include "internal.h"
#include <jxl/decode.h>
@@ -59,6 +61,8 @@ typedef struct LibJxlDecodeContext {
int prev_is_last;
AVRational anim_timebase;
AVFrame *frame;
+ AVBufferRef *exif;
+ size_t exif_pos;
} LibJxlDecodeContext;
static int libjxl_init_jxl_decoder(AVCodecContext *avctx)
@@ -66,17 +70,24 @@ static int libjxl_init_jxl_decoder(AVCodecContext *avctx)
LibJxlDecodeContext *ctx = avctx->priv_data;
ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE
- | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME;
+ | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME
+ | JXL_DEC_BOX | JXL_DEC_BOX_COMPLETE;
if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events\n");
return AVERROR_EXTERNAL;
}
+ if (JxlDecoderSetDecompressBoxes(ctx->decoder, JXL_TRUE) != JXL_DEC_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Error setting compress box mode\n");
+ return AVERROR_EXTERNAL;
+ }
+
if (JxlDecoderSetParallelRunner(ctx->decoder, JxlThreadParallelRunner, ctx->runner) != JXL_DEC_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner\n");
return AVERROR_EXTERNAL;
}
+ av_buffer_unref(&ctx->exif);
memset(&ctx->basic_info, 0, sizeof(JxlBasicInfo));
memset(&ctx->jxl_pixfmt, 0, sizeof(JxlPixelFormat));
ctx->prev_is_last = 1;
@@ -487,6 +498,28 @@ static int libjxl_receive_frame(AVCodecContext *avctx, AVFrame *frame)
if (ret < 0)
return ret;
}
+ if (ctx->exif) {
+ AVExifMetadata ifd = { 0 };
+ /* size may be larger than exif_pos due to the realloc loop */
+ ret = av_exif_parse_buffer(avctx, ctx->exif->data, ctx->exif_pos, &ifd, AV_EXIF_T_OFF);
+ av_buffer_unref(&ctx->exif);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Unable to parse EXIF buffer\n");
+ continue;
+ }
+ /*
+ * JPEG XL Codestream orientation overrides EXIF orientation in all cases.
+ * As a result, we remove the EXIF Orientation tag rather than just zeroing it
+ * in order to prevent any ambiguity. libjxl autorotates the image for us so we
+ * do not need to worry about that.
+ */
+ ret = av_exif_remove_entry(avctx, &ifd, av_exif_get_tag_id("Orientation"), 0);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_WARNING, "Unable to remove orientation from EXIF buffer\n");
+ ret = ff_exif_attach_ifd(avctx, ctx->frame, &ifd);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_ERROR, "Unable to attach EXIF ifd\n");
+ }
if (ctx->basic_info.have_animation) {
ctx->frame->pts = av_rescale_q(ctx->accumulated_pts, ctx->anim_timebase, avctx->pkt_timebase);
ctx->frame->duration = av_rescale_q(ctx->frame_duration, ctx->anim_timebase, avctx->pkt_timebase);
@@ -511,6 +544,48 @@ static int libjxl_receive_frame(AVCodecContext *avctx, AVFrame *frame)
JxlDecoderReset(ctx->decoder);
libjxl_init_jxl_decoder(avctx);
continue;
+ case JXL_DEC_BOX: {
+ char type[4];
+ av_log(avctx, AV_LOG_DEBUG, "BOX event emitted\n");
+ if (JxlDecoderGetBoxType(ctx->decoder, type, JXL_TRUE) != JXL_DEC_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Error getting box type\n");
+ return AVERROR_EXTERNAL;
+ }
+ if (AV_RL32(type) != MKTAG('E','x','i','f'))
+ continue;
+ av_buffer_unref(&ctx->exif);
+ ctx->exif_pos = 0;
+ // 4k buffer should usually be enough
+ ret = av_buffer_realloc(&ctx->exif, 4096);
+ if (ret < 0)
+ return AVERROR(ENOMEM);
+ if (JxlDecoderSetBoxBuffer(ctx->decoder, ctx->exif->data, ctx->exif->size) != JXL_DEC_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Error setting box buffer\n");
+ return AVERROR_EXTERNAL;
+ }
+ continue;
+ }
+ case JXL_DEC_BOX_NEED_MORE_OUTPUT: {
+ av_log(avctx, AV_LOG_DEBUG, "BOX_NEED_MORE_OUTPUT event emitted\n");
+ size_t remainder = JxlDecoderReleaseBoxBuffer(ctx->decoder);
+ ctx->exif_pos = ctx->exif->size - remainder;
+ size_t new_size = ctx->exif->size << 1;
+ ret = av_buffer_realloc(&ctx->exif, new_size);
+ if (ret < 0)
+ return AVERROR(ENOMEM);
+ if (JxlDecoderSetBoxBuffer(ctx->decoder, ctx->exif->data + ctx->exif_pos,
+ ctx->exif->size - ctx->exif_pos) != JXL_DEC_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Error setting box buffer\n");
+ return AVERROR_EXTERNAL;
+ }
+ continue;
+ }
+ case JXL_DEC_BOX_COMPLETE: {
+ av_log(avctx, AV_LOG_DEBUG, "BOX_COMPLETE event emitted\n");
+ size_t remainder = JxlDecoderReleaseBoxBuffer(ctx->decoder);
+ ctx->exif_pos = ctx->exif->size - remainder;
+ continue;
+ }
default:
av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret);
return AVERROR_EXTERNAL;
@@ -528,6 +603,7 @@ static av_cold int libjxl_decode_close(AVCodecContext *avctx)
if (ctx->decoder)
JxlDecoderDestroy(ctx->decoder);
ctx->decoder = NULL;
+ av_buffer_unref(&ctx->exif);
av_buffer_unref(&ctx->iccp);
av_frame_free(&ctx->frame);
commit 5d4f873ff31f86f9ae1927f37ca0fcb78da70512
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Sat Apr 12 12:00:52 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
fftools/ffprobe: print EXIF side data size
We don't need to print the tags here because they're added as dict
elements to AVFrame->metadata and are printed elsewhere with ffprobe
-show_frames.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index b8d51071e6..018111318e 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -1342,6 +1342,8 @@ static void print_frame_side_data(AVTextFormatContext *tfc,
print_film_grain_params(tfc, fgp);
} else if (sd->type == AV_FRAME_DATA_VIEW_ID) {
print_int("view_id", *(int*)sd->data);
+ } else if (sd->type == AV_FRAME_DATA_EXIF) {
+ print_int("size", sd->size);
}
avtext_print_section_footer(tfc);
}
diff --git a/tests/ref/fate/exif-image-embedded b/tests/ref/fate/exif-image-embedded
index a6617bd89f..22d4b03d1c 100644
--- a/tests/ref/fate/exif-image-embedded
+++ b/tests/ref/fate/exif-image-embedded
@@ -34,6 +34,7 @@ TAG:ExifIFD/UserComment=AppleMark
[SIDE_DATA]
side_data_type=EXIF metadata
+size=55
[/SIDE_DATA]
[/FRAME]
[FRAME]
diff --git a/tests/ref/fate/exif-image-jpg b/tests/ref/fate/exif-image-jpg
index 5135ae32f8..d847ae7c7f 100644
--- a/tests/ref/fate/exif-image-jpg
+++ b/tests/ref/fate/exif-image-jpg
@@ -199,5 +199,6 @@ TAG:ExifIFD/DigitalZoomRatio= 4000:4000
TAG:ExifIFD/SceneCaptureType= 0
[SIDE_DATA]
side_data_type=EXIF metadata
+size=3297
[/SIDE_DATA]
[/FRAME]
diff --git a/tests/ref/fate/exif-image-tiff b/tests/ref/fate/exif-image-tiff
index 81737cb983..f0c0946c76 100644
--- a/tests/ref/fate/exif-image-tiff
+++ b/tests/ref/fate/exif-image-tiff
@@ -54,5 +54,6 @@ TAG:ResolutionUnit= 3
TAG:Software=ImageMagick 6.5.8-0 2010-02-09 Q16 http://www.imagemagick.org
[SIDE_DATA]
side_data_type=EXIF metadata
+size=403
[/SIDE_DATA]
[/FRAME]
diff --git a/tests/ref/fate/exif-image-webp b/tests/ref/fate/exif-image-webp
index 7c01ae3d66..54c8294362 100644
--- a/tests/ref/fate/exif-image-webp
+++ b/tests/ref/fate/exif-image-webp
@@ -199,5 +199,6 @@ TAG:ExifIFD/DigitalZoomRatio= 4000:4000
TAG:ExifIFD/SceneCaptureType= 0
[SIDE_DATA]
side_data_type=EXIF metadata
+size=9718
[/SIDE_DATA]
[/FRAME]
commit 1e816ebefee0ef479fa76bd2a1fb9ec5d5e807df
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Apr 9 13:07:49 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/tiff: decode TIFF non-image-data tags into EXIF metadata struct
This commit will cause TIFF files to store their tags in the EXIF
struct so tags such as orientation can be transfered to other formats
(such as PNG) in a way that doesn't corrupt the IFD.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index b468598fbb..d15bc497ec 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -47,6 +47,7 @@
#include "bytestream.h"
#include "codec_internal.h"
#include "decode.h"
+#include "exif_internal.h"
#include "faxcompr.h"
#include "lzw.h"
#include "tiff.h"
@@ -124,6 +125,8 @@ typedef struct TiffContext {
int geotag_count;
TiffGeoTag *geotags;
+
+ AVExifMetadata exif_meta;
} TiffContext;
static const float d65_white[3] = { 0.950456f, 1.f, 1.088754f };
@@ -1937,6 +1940,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p,
int is_dng;
int has_tile_bits, has_strip_bits;
+ av_exif_free(&s->exif_meta);
+ /* this will not parse the image data */
+ ret = av_exif_parse_buffer(avctx, avpkt->data, avpkt->size, &s->exif_meta, AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_ERROR, "could not parse EXIF data: %s\n", av_err2str(ret));
+
bytestream2_init(&s->gb, avpkt->data, avpkt->size);
// parse image header
@@ -2402,6 +2411,10 @@ again:
}
}
+ ret = ff_exif_attach_ifd(avctx, p, &s->exif_meta);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_ERROR, "error attaching EXIF ifd: %s\n", av_err2str(ret));
+
*got_frame = 1;
return avpkt->size;
@@ -2450,6 +2463,7 @@ static av_cold int tiff_end(AVCodecContext *avctx)
TiffContext *const s = avctx->priv_data;
free_geotags(s);
+ av_exif_free(&s->exif_meta);
ff_lzw_decode_close(&s->lzw);
av_freep(&s->deinvert_buf);
diff --git a/tests/ref/fate/exif-image-tiff b/tests/ref/fate/exif-image-tiff
index 0e604d4271..81737cb983 100644
--- a/tests/ref/fate/exif-image-tiff
+++ b/tests/ref/fate/exif-image-tiff
@@ -32,5 +32,27 @@ color_transfer=unknown
chroma_location=unspecified
TAG:document_name=image_small.tiff
TAG:page_number= 0 / 1
-TAG:software=ImageMagick 6.5.8-0 2010-02-09 Q16 http://www.imagemagick.org
+TAG:0x0129= 0, 1
+TAG:ImageWidth= 200
+TAG:ImageLength= 112
+TAG:BitsPerSample= 8, 8, 8
+TAG:Compression= 1
+TAG:PhotometricInterpretation= 2
+TAG:0x010A= 1
+TAG:0x010D=image_small.tiff
+TAG:StripOffsets= 8, 7808, 15608, 23408, 31208, 39008, 46808, 54608
+ 62408
+TAG:Orientation= 1
+TAG:SamplesPerPixel= 3
+TAG:RowsPerStrip= 13
+TAG:StripByteCounts= 7800, 7800, 7800, 7800, 7800, 7800, 7800, 7800
+ 4800
+TAG:XResolution=1188833536:16777216
+TAG:YResolution=1188833536:16777216
+TAG:PlanarConfiguration= 1
+TAG:ResolutionUnit= 3
+TAG:Software=ImageMagick 6.5.8-0 2010-02-09 Q16 http://www.imagemagick.org
+[SIDE_DATA]
+side_data_type=EXIF metadata
+[/SIDE_DATA]
[/FRAME]
commit 93a8091015978c44462626409af71789080bbef4
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Apr 16 10:33:04 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/exif: add av_exif_get_entry
Add an API function to retrieve am AVExifEntry struct with a given TIFF
tag ID from the AVExifMetadata ifd struct.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 993a931d42..15bbe7749b 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -1080,6 +1080,36 @@ end:
return ret;
}
+static int exif_get_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int depth, AVExifEntry **value)
+{
+ int offset = 1;
+
+ if (!ifd || ifd->entries && !ifd->count || ifd->count && !ifd->entries || !value)
+ return AVERROR(EINVAL);
+
+ for (size_t i = 0; i < ifd->count; i++) {
+ if (ifd->entries[i].id == id) {
+ *value = &ifd->entries[i];
+ return i + offset;
+ }
+ if (ifd->entries[i].type == AV_TIFF_IFD) {
+ if (depth < 3) {
+ int ret = exif_get_entry(logctx, &ifd->entries[i].value.ifd, id, depth + 1, value);
+ if (ret)
+ return ret < 0 ? ret : ret + offset;
+ }
+ offset += ifd->entries[i].value.ifd.count;
+ }
+ }
+
+ return 0;
+}
+
+int av_exif_get_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int recursive, AVExifEntry **value)
+{
+ return exif_get_entry(logctx, ifd, id, recursive ? 0 : INT_MAX, value);
+}
+
int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTiffDataType type,
uint32_t count, const uint8_t *ifd_lead, uint32_t ifd_offset, const void *value)
{
@@ -1093,12 +1123,9 @@ int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTif
|| !value || ifd->count == 0xFFFFu)
return AVERROR(EINVAL);
- for (size_t i = 0; i < ifd->count; i++) {
- if (ifd->entries[i].id == id) {
- entry = &ifd->entries[i];
- break;
- }
- }
+ ret = av_exif_get_entry(logctx, ifd, id, 0, &entry);
+ if (ret < 0)
+ return ret;
if (entry) {
exif_free_entry(entry);
@@ -1292,7 +1319,6 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
AVExifEntry *ih = NULL;
AVExifEntry *pw = NULL;
AVExifEntry *ph = NULL;
- AVExifMetadata *exif = NULL;
uint64_t orientation = 1;
uint64_t w = frame->width;
uint64_t h = frame->height;
@@ -1333,7 +1359,7 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
continue;
}
if (entry->id == EXIFIFD_TAG && entry->type == AV_TIFF_IFD) {
- exif = &entry->value.ifd;
+ AVExifMetadata *exif = &entry->value.ifd;
for (size_t j = 0; j < exif->count; j++) {
AVExifEntry *exifentry = &exif->entries[j];
if (exifentry->id == PIXEL_X_TAG && exifentry->count > 0 && exifentry->type == AV_TIFF_SHORT) {
@@ -1387,15 +1413,15 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
goto end;
}
if (!pw && w && w < 0xFFFFu || !ph && h && h < 0xFFFFu) {
+ AVExifMetadata *exif;
+ AVExifEntry *exif_entry;
+ int exif_found = av_exif_get_entry(logctx, &ifd, EXIFIFD_TAG, 0, &exif_entry);
rewrite = 1;
- exif = NULL;
- for (size_t i = 0; i < ifd.count; i++) {
- if (ifd.entries[i].id == EXIFIFD_TAG && ifd.entries[i].type == AV_TIFF_IFD) {
- exif = &ifd.entries[i].value.ifd;
- break;
- }
- }
- if (!exif) {
+ if (exif_found < 0)
+ goto end;
+ if (exif_found > 0) {
+ exif = &exif_entry->value.ifd;
+ } else {
AVExifMetadata exif_new = { 0 };
ret = av_exif_set_entry(logctx, &ifd, EXIFIFD_TAG, AV_TIFF_IFD, 1, NULL, 0, &exif_new);
if (ret < 0) {
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index 9f9f707495..b6d1ffd91d 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -144,6 +144,17 @@ int32_t av_exif_get_tag_id(const char *name);
int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTiffDataType type,
uint32_t count, const uint8_t *ifd_lead, uint32_t ifd_offset, const void *value);
+/**
+ * Get an entry with the tagged ID from the EXIF metadata struct. A pointer to the entry
+ * will be written into *value. If the recursive flag is set to true, this function will check
+ * subdirectories as well.
+ *
+ * If the entry was present and returned successfully, a positive number is returned.
+ * If the entry was not found, *value is left untouched and zero is returned.
+ * If an error occurred, a negative number is returned.
+ */
+int av_exif_get_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int recursive, AVExifEntry **value);
+
/**
* Remove an entry from the provided EXIF metadata struct. If the recursive flag is set
* to true, then this function will check subdirectories as well.
commit 303f60684f25ce63951480ef0f759acf41aac938
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 25 17:25:40 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:48 2025 -0400
avcodec/exif: add av_exif_remove_entry
Add an API function that allows popping an exif entry out of the struct
entirely rather than requiring it be replaced with a default value.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index d174d16ddc..993a931d42 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -1132,6 +1132,46 @@ int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTif
return ret;
}
+static int exif_remove_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int depth)
+{
+ int32_t index = -1;
+ int ret = 0;
+
+ if (!ifd || ifd->entries && !ifd->count || ifd->count && !ifd->entries)
+ return AVERROR(EINVAL);
+
+ for (size_t i = 0; i < ifd->count; i++) {
+ if (ifd->entries[i].id == id) {
+ index = i;
+ break;
+ }
+ if (ifd->entries[i].type == AV_TIFF_IFD && depth < 3) {
+ ret = exif_remove_entry(logctx, &ifd->entries[i].value.ifd, id, depth + 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (index < 0)
+ return 0;
+ exif_free_entry(&ifd->entries[index]);
+
+ if (index == --ifd->count) {
+ if (!index)
+ av_freep(&ifd->entries);
+ return 1;
+ }
+
+ memmove(&ifd->entries[index], &ifd->entries[index + 1], (ifd->count - index) * sizeof(*ifd->entries));
+
+ return 1 + (ifd->count - index);
+}
+
+int av_exif_remove_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int recursive)
+{
+ return exif_remove_entry(logctx, ifd, id, recursive ? 0 : INT_MAX);
+}
+
AVExifMetadata *av_exif_clone_ifd(const AVExifMetadata *ifd)
{
AVExifMetadata *ret = av_mallocz(sizeof(*ret));
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index 23c1b37c5d..9f9f707495 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -144,6 +144,16 @@ int32_t av_exif_get_tag_id(const char *name);
int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTiffDataType type,
uint32_t count, const uint8_t *ifd_lead, uint32_t ifd_offset, const void *value);
+/**
+ * Remove an entry from the provided EXIF metadata struct. If the recursive flag is set
+ * to true, then this function will check subdirectories as well.
+ *
+ * If the entry was present and removed successfully, a positive number is returned.
+ * If the entry was not found, zero is returned.
+ * If an error occurred, a negative number is returned.
+ */
+int av_exif_remove_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, int recursive);
+
/**
* Decodes the EXIF data provided in the buffer and writes it into the
* struct *ifd. If this function succeeds, the IFD is owned by the caller
commit 5caaadee7954f2014d9e0a17d1edfb961893bcf1
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 25 17:09:07 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/exif: add orientation ID and matrix conversion routines
Takes existing code that makes display matricies work with EXIF
orientation tags and expose the conversion as a public API.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index fa46202874..d174d16ddc 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -811,7 +811,7 @@ static int attach_displaymatrix(void *logctx, AVFrame *frame, int orientation)
int32_t *matrix;
/* invalid orientation */
if (orientation < 2 || orientation > 8)
- return 0;
+ return AVERROR_INVALIDDATA;
sd = av_frame_new_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9);
if (!sd) {
av_log(logctx, AV_LOG_ERROR, "Could not allocate frame side data\n");
@@ -819,37 +819,7 @@ static int attach_displaymatrix(void *logctx, AVFrame *frame, int orientation)
}
matrix = (int32_t *) sd->data;
- switch (orientation) {
- case 2:
- av_display_rotation_set(matrix, 0.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 3:
- av_display_rotation_set(matrix, 180.0);
- break;
- case 4:
- av_display_rotation_set(matrix, 180.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 5:
- av_display_rotation_set(matrix, 90.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 6:
- av_display_rotation_set(matrix, 90.0);
- break;
- case 7:
- av_display_rotation_set(matrix, -90.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 8:
- av_display_rotation_set(matrix, -90.0);
- break;
- default:
- av_assert0(0);
- }
-
- return 0;
+ return av_exif_orientation_to_matrix(matrix, orientation);
}
#define COLUMN_SEP(i, c) ((i) ? ((i) % (c) ? ", " : "\n") : "")
@@ -1215,31 +1185,59 @@ end:
return ret;
}
-static int exif_get_orientation(const int32_t *display_matrix)
+static const int rotation_lut[2][4] = {
+ {1, 8, 3, 6}, {4, 7, 2, 5},
+};
+
+int av_exif_matrix_to_orientation(const int32_t *matrix)
{
- double rotation = av_display_rotation_get(display_matrix);
+ double rotation = av_display_rotation_get(matrix);
// determinant
- int vflip = ((int64_t)display_matrix[0] * (int64_t)display_matrix[4]
- - (int64_t)display_matrix[1] * (int64_t)display_matrix[3]) < 0;
+ int vflip = ((int64_t)matrix[0] * (int64_t)matrix[4]
+ - (int64_t)matrix[1] * (int64_t)matrix[3]) < 0;
if (!isfinite(rotation))
- return 1;
- if (vflip) {
- if (rotation > 181.0 || rotation > -179.0 && rotation < -89.0)
- return 5;
- if (rotation > 91.0 || rotation < -179.0 && rotation > -269.0)
- return 2;
- if (rotation > 1.0 || rotation < -269.0)
- return 7;
- return 4;
- } else {
- if (rotation > 181.0 || rotation > -179.0 && rotation < -89.0)
- return 6;
- if (rotation > 91.0 || rotation < -179.0 && rotation > -269.0)
- return 3;
- if (rotation > 1.0 || rotation < -269.0)
- return 8;
- return 1;
+ return 0;
+ int rot = (int)(rotation + 0.5);
+ rot = (((rot % 360) + 360) % 360) / 90;
+ return rotation_lut[vflip][rot];
+}
+
+int av_exif_orientation_to_matrix(int32_t *matrix, int orientation)
+{
+ switch (orientation) {
+ case 1:
+ av_display_rotation_set(matrix, 0.0);
+ break;
+ case 2:
+ av_display_rotation_set(matrix, 0.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 3:
+ av_display_rotation_set(matrix, 180.0);
+ break;
+ case 4:
+ av_display_rotation_set(matrix, 180.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 5:
+ av_display_rotation_set(matrix, 90.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 6:
+ av_display_rotation_set(matrix, 90.0);
+ break;
+ case 7:
+ av_display_rotation_set(matrix, -90.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 8:
+ av_display_rotation_set(matrix, -90.0);
+ break;
+ default:
+ return AVERROR(EINVAL);
}
+
+ return 0;
}
int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_ptr, enum AVExifHeaderMode header_mode)
@@ -1270,7 +1268,7 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
return 0;
if (sd_orient)
- orientation = exif_get_orientation((int32_t *) sd_orient->data);
+ orientation = av_exif_matrix_to_orientation((int32_t *) sd_orient->data);
if (orientation != 1)
av_log(logctx, AV_LOG_DEBUG, "matrix contains nontrivial EXIF orientation: %" PRIu64 "\n", orientation);
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index 2661f86582..23c1b37c5d 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -184,6 +184,24 @@ int av_exif_ifd_to_dict(void *logctx, const AVExifMetadata *ifd, AVDictionary **
*/
AVExifMetadata *av_exif_clone_ifd(const AVExifMetadata *ifd);
+/**
+ * Convert a display matrix used by AV_FRAME_DATA_DISPLAYMATRIX
+ * into an orientation constant used by EXIF's orientation tag.
+ *
+ * Returns an EXIF orientation between 1 and 8 (inclusive) depending
+ * on the rotation and flip factors. Returns 0 if the matrix is singular.
+ */
+int av_exif_matrix_to_orientation(const int32_t *matrix);
+
+/**
+ * Convert an orientation constant used by EXIF's orientation tag
+ * into a display matrix used by AV_FRAME_DATA_DISPLAYMATRIX.
+ *
+ * Returns 0 on success and negative if the orientation is invalid,
+ * i.e. not between 1 and 8 (inclusive).
+ */
+int av_exif_orientation_to_matrix(int32_t *matrix, int orientation);
+
#if FF_API_OLD_EXIF
/* Used by the AVI demuxer */
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
commit d3190a64c366a79091fe47fddf93c09a7d988802
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 25 12:14:32 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/pngenc: support writing EXIF profiles
Add support to write EXIF profiles using the new EXIF framework, namely
ff_exif_get_buffer, and writing them into eXIf chunks.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index c48eb5daa6..fa46202874 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -1350,6 +1350,13 @@ int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_
}
if (!pw && w && w < 0xFFFFu || !ph && h && h < 0xFFFFu) {
rewrite = 1;
+ exif = NULL;
+ for (size_t i = 0; i < ifd.count; i++) {
+ if (ifd.entries[i].id == EXIFIFD_TAG && ifd.entries[i].type == AV_TIFF_IFD) {
+ exif = &ifd.entries[i].value.ifd;
+ break;
+ }
+ }
if (!exif) {
AVExifMetadata exif_new = { 0 };
ret = av_exif_set_entry(logctx, &ifd, EXIFIFD_TAG, AV_TIFF_IFD, 1, NULL, 0, &exif_new);
diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c
index 9bbb8267cf..635c89e87e 100644
--- a/libavcodec/pngenc.c
+++ b/libavcodec/pngenc.c
@@ -22,6 +22,7 @@
#include "avcodec.h"
#include "codec_internal.h"
#include "encode.h"
+#include "exif_internal.h"
#include "bytestream.h"
#include "lossless_videoencdsp.h"
#include "png.h"
@@ -29,6 +30,7 @@
#include "zlib_wrapper.h"
#include "libavutil/avassert.h"
+#include "libavutil/buffer.h"
#include "libavutil/crc.h"
#include "libavutil/csp.h"
#include "libavutil/libm.h"
@@ -373,6 +375,7 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict)
{
AVFrameSideData *side_data;
PNGEncContext *s = avctx->priv_data;
+ AVBufferRef *exif_data = NULL;
int ret;
/* write png header */
@@ -414,6 +417,19 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict)
}
}
+ ret = ff_exif_get_buffer(avctx, pict, &exif_data, AV_EXIF_TIFF_HEADER);
+ if (exif_data) {
+ // png_write_chunk accepts an int, not a size_t, so we have to check overflow
+ if (exif_data->size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
+ // that's a very big exif chunk, probably a bug
+ av_log(avctx, AV_LOG_ERROR, "extremely large EXIF buffer detected, not writing\n");
+ else
+ png_write_chunk(&s->bytestream, MKTAG('e','X','I','f'), exif_data->data, exif_data->size);
+ av_buffer_unref(&exif_data);
+ } else if (ret < 0) {
+ av_log(avctx, AV_LOG_WARNING, "unable to attach EXIF metadata: %s\n", av_err2str(ret));
+ }
+
side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE);
if ((ret = png_write_iccp(s, side_data)))
return ret;
diff --git a/tests/ref/fate/cover-art-mp3-id3v2-remux b/tests/ref/fate/cover-art-mp3-id3v2-remux
index 011c123a70..42fe704470 100644
--- a/tests/ref/fate/cover-art-mp3-id3v2-remux
+++ b/tests/ref/fate/cover-art-mp3-id3v2-remux
@@ -1,5 +1,5 @@
-a8dc078d174d5c4ae9769090b8404bae *tests/data/fate/cover-art-mp3-id3v2-remux.mp3
-346394 tests/data/fate/cover-art-mp3-id3v2-remux.mp3
+070ed1d95dc1bd365d68ae795966589f *tests/data/fate/cover-art-mp3-id3v2-remux.mp3
+346509 tests/data/fate/cover-art-mp3-id3v2-remux.mp3
#tb 0: 1/14112000
#media_type 0: audio
#codec_id 0: mp3
@@ -23,7 +23,7 @@ a8dc078d174d5c4ae9769090b8404bae *tests/data/fate/cover-art-mp3-id3v2-remux.mp3
0, -353590, -353590, 368640, 417, 0x15848290, S=1, Skip Samples, 10, 0x034e0055
1, 0, 0, 0, 208350, 0x291b44d1
2, 0, 0, 0, 15760, 0x71d5c418
-3, 0, 0, 0, 112719, 0x117b6853
+3, 0, 0, 0, 112834, 0x7da17554
0, 15050, 15050, 368640, 418, 0x46f684a4
0, 383690, 383690, 368640, 418, 0x46f684a4
0, 752330, 752330, 368640, 418, 0x46f684a4
diff --git a/tests/ref/fate/mov-cover-image b/tests/ref/fate/mov-cover-image
index 0607d56a0c..305a851d98 100644
--- a/tests/ref/fate/mov-cover-image
+++ b/tests/ref/fate/mov-cover-image
@@ -1,5 +1,5 @@
-9a5143f063aedb39fb1f080444cc19a0 *tests/data/fate/mov-cover-image.mp4
-1023905 tests/data/fate/mov-cover-image.mp4
+ea8251f5922d663b174855af325d689f *tests/data/fate/mov-cover-image.mp4
+1024065 tests/data/fate/mov-cover-image.mp4
#extradata 0: 2, 0x00340022
#tb 0: 1/44100
#media_type 0: audio
@@ -20,7 +20,7 @@
0, -1088, -1088, 1024, 6, 0x027e00e8, F=0x5
0, -64, -64, 1024, 6, 0x027e00e8
1, 0, 0, 0, 25441, 0xe82503b0
-2, 0, 0, 0, 44643, 0xdb75ffb5
+2, 0, 0, 0, 44803, 0x1e4a0e8f
0, 960, 960, 1024, 6, 0x027e00e8
0, 1984, 1984, 1024, 6, 0x027e00e8
0, 3008, 3008, 1024, 6, 0x027e00e8
commit 44af3829796427b89c4b76b56652cc5932ada616
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Mon Mar 17 23:04:31 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/pngdec: support reading EXIF profiles
Add support to parse eXIf chunks using the new EXIF framework, as well
as EXIF profiles in zTXt and tEXt with Raw profile type exif.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c
index b9c997ab0e..5e138a4f49 100644
--- a/libavcodec/pngdec.c
+++ b/libavcodec/pngdec.c
@@ -39,6 +39,7 @@
#include "bytestream.h"
#include "codec_internal.h"
#include "decode.h"
+#include "exif_internal.h"
#include "apng.h"
#include "png.h"
#include "pngdsp.h"
@@ -125,6 +126,8 @@ typedef struct PNGDecContext {
int pass_row_size; /* decompress row size of the current pass */
int y;
FFZStream zstream;
+
+ AVBufferRef *exif_data;
} PNGDecContext;
/* Mask to determine which pixels are valid in a pass */
@@ -540,6 +543,98 @@ static char *iso88591_to_utf8(const char *in, size_t size_in)
return out;
}
+static int decode_text_to_exif(PNGDecContext *s, const char *txt_utf8)
+{
+ size_t len = strlen(txt_utf8);
+ const char *ptr = txt_utf8;
+ const char *end = txt_utf8 + len;
+ size_t exif_len = 0;
+ uint8_t *exif_ptr;
+ const uint8_t *exif_end;
+
+ // first we find a newline
+ while (*ptr++ != '\n') {
+ if (ptr >= end)
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
+
+ // we check for "exif" and skip over it
+ if (end - ptr < 4 || strncmp("exif", ptr, 4))
+ return AVERROR_INVALIDDATA;
+ ptr += 3;
+
+ // then we find the next printable non-space character
+ while (!av_isgraph(*++ptr)) {
+ if (ptr >= end)
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
+
+ // parse the length
+ while (av_isdigit(*ptr)) {
+ size_t nlen = exif_len * 10 + (*ptr - '0');
+ if (nlen < exif_len) // overflow
+ return AVERROR_INVALIDDATA;
+ exif_len = nlen;
+ if (++ptr >= end)
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
+
+ // then we find the next printable non-space character
+ while (!av_isgraph(*ptr)) {
+ if (++ptr >= end)
+ return AVERROR_BUFFER_TOO_SMALL;
+ }
+
+ // first condition checks for overflow in 2 * exif_len
+ if ((exif_len & ~SIZE_MAX) || end - ptr < 2 * exif_len)
+ return AVERROR_INVALIDDATA;
+ if (exif_len < 10)
+ return AVERROR_INVALIDDATA;
+
+ av_buffer_unref(&s->exif_data);
+ // the buffer starts with "Exif " which we skip over
+ // we don't use AV_EXIF_EXIF00 because that disagrees
+ // with the eXIf chunk format
+ s->exif_data = av_buffer_alloc(exif_len - 6);
+ if (!s->exif_data)
+ return AVERROR(ENOMEM);
+
+ // we subtract one because we call ++ptr later
+ // compiler will optimize out the call
+ ptr += strlen("Exif ") * 2 - 1;
+
+ exif_ptr = s->exif_data->data;
+ exif_end = exif_ptr + s->exif_data->size;
+
+ while (exif_ptr < exif_end) {
+ while (++ptr < end) {
+ if (*ptr >= '0' && *ptr <= '9') {
+ *exif_ptr = (*ptr - '0') << 4;
+ break;
+ }
+ if (*ptr >= 'a' && *ptr <= 'f') {
+ *exif_ptr = (*ptr - 'a' + 10) << 4;
+ break;
+ }
+ }
+ while (++ptr < end) {
+ if (*ptr >= '0' && *ptr <= '9') {
+ *exif_ptr += *ptr - '0';
+ break;
+ }
+ if (*ptr >= 'a' && *ptr <= 'f') {
+ *exif_ptr += *ptr - 'a' + 10;
+ break;
+ }
+ }
+ if (ptr > end)
+ return AVERROR_INVALIDDATA;
+ exif_ptr++;
+ }
+
+ return 0;
+}
+
static int decode_text_chunk(PNGDecContext *s, GetByteContext *gb, int compressed)
{
int ret, method;
@@ -582,6 +677,17 @@ static int decode_text_chunk(PNGDecContext *s, GetByteContext *gb, int compresse
return AVERROR(ENOMEM);
}
+ if (!strcmp(kw_utf8, "Raw profile type exif")) {
+ ret = decode_text_to_exif(s, txt_utf8);
+ if (ret < 0) {;
+ av_buffer_unref(&s->exif_data);
+ } else {
+ av_freep(&kw_utf8);
+ av_freep(&txt_utf8);
+ return ret;
+ }
+ }
+
av_dict_set(&s->frame_metadata, kw_utf8, txt_utf8,
AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
return 0;
@@ -654,6 +760,23 @@ static int decode_phys_chunk(AVCodecContext *avctx, PNGDecContext *s,
return 0;
}
+static int decode_exif_chunk(AVCodecContext *avctx, PNGDecContext *s,
+ GetByteContext *gb)
+{
+ if (!(s->hdr_state & PNG_IHDR)) {
+ av_log(avctx, AV_LOG_ERROR, "eXIf before IHDR\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_buffer_unref(&s->exif_data);
+ s->exif_data = av_buffer_alloc(bytestream2_get_bytes_left(gb));
+ if (!s->exif_data)
+ return AVERROR(ENOMEM);
+ bytestream2_get_buffer(gb, s->exif_data->data, s->exif_data->size);
+
+ return 0;
+}
+
/*
* This populates AVCodecContext fields so it must be called before
* ff_thread_finish_setup() to avoid a race condition with respect to the
@@ -1594,6 +1717,11 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s,
s->mdcv_max_lum = bytestream2_get_be32u(&gb_chunk);
s->mdcv_min_lum = bytestream2_get_be32u(&gb_chunk);
break;
+ case MKTAG('e', 'X', 'I', 'f'):
+ ret = decode_exif_chunk(avctx, s, &gb_chunk);
+ if (ret < 0)
+ goto fail;
+ break;
case MKTAG('I', 'E', 'N', 'D'):
if (!(s->pic_state & PNG_ALLIMAGE))
av_log(avctx, AV_LOG_ERROR, "IEND without all image\n");
@@ -1622,6 +1750,20 @@ exit_loop:
if (s->bits_per_pixel <= 4)
handle_small_bpp(s, p);
+ if (s->exif_data) {
+ // we swap because ff_exif_attach_buffer adds to p->metadata
+ FFSWAP(AVDictionary *, p->metadata, s->frame_metadata);
+ ret = ff_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_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) {
for (int y = 0; y < s->height; y++) {
uint8_t *row = &p->data[0][p->linesize[0] * y];
@@ -1914,6 +2056,7 @@ static av_cold int png_dec_end(AVCodecContext *avctx)
s->tmp_row_size = 0;
av_freep(&s->iccp_data);
+ av_buffer_unref(&s->exif_data);
av_dict_free(&s->frame_metadata);
ff_inflate_end(&s->zstream);
commit f5ad1c910c168d05cb01315773ab0bd094c9372f
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Mon Mar 17 11:51:33 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/exif: add ff_exif_get_buffer
Add a function to allow encoders to get a self-contained EXIF
buffer to write into an EXIF chunk.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index aa2246ae9a..c48eb5daa6 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -29,6 +29,7 @@
#include <inttypes.h>
+#include "libavutil/avconfig.h"
#include "libavutil/bprint.h"
#include "libavutil/display.h"
#include "libavutil/intreadwrite.h"
@@ -47,6 +48,11 @@
#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];
@@ -616,15 +622,19 @@ static size_t exif_get_ifd_size(const AVExifMetadata *ifd)
return total_size;
}
-static int exif_write_ifd(void *logctx, PutByteContext *pb, int le, const AVExifMetadata *ifd)
+static int exif_write_ifd(void *logctx, PutByteContext *pb, int le, int depth, const AVExifMetadata *ifd)
{
- int offset, ret, tell;
- tell = bytestream2_get_bytes_left_p(pb);
+ int offset, ret, tell, tell2;
+ tell = bytestream2_tell_p(pb);
tput16(pb, le, ifd->count);
offset = tell + IFD_EXTRA_SIZE + BASE_TAG_SIZE * (uint32_t) ifd->count;
av_log(logctx, AV_LOG_DEBUG, "writing IFD with %u entries and initial offset %d\n", ifd->count, offset);
for (size_t i = 0; i < ifd->count; i++) {
const AVExifEntry *entry = &ifd->entries[i];
+ av_log(logctx, AV_LOG_DEBUG, "writing TIFF entry: id: 0x%04" PRIx16 ", type: %d, count: %"
+ PRIu32 ", offset: %d, offset value: %d\n",
+ entry->id, entry->type, entry->count,
+ bytestream2_tell_p(pb), offset);
tput16(pb, le, entry->id);
if (entry->id == MAKERNOTE_TAG && entry->type == AV_TIFF_IFD) {
size_t ifd_size = exif_get_ifd_size(&entry->value.ifd);
@@ -635,29 +645,30 @@ static int exif_write_ifd(void *logctx, PutByteContext *pb, int le, const AVExif
tput32(pb, le, entry->count);
}
if (entry->type == AV_TIFF_IFD) {
- int tell = bytestream2_tell_p(pb);
tput32(pb, le, offset);
+ tell2 = bytestream2_tell_p(pb);
bytestream2_seek_p(pb, offset, SEEK_SET);
if (entry->ifd_offset)
bytestream2_put_buffer(pb, entry->ifd_lead, entry->ifd_offset);
- ret = exif_write_ifd(logctx, pb, le, &entry->value.ifd);
+ ret = exif_write_ifd(logctx, pb, le, depth + 1, &entry->value.ifd);
if (ret < 0)
return ret;
offset += ret + entry->ifd_offset;
- bytestream2_seek_p(pb, tell + 4, SEEK_SET);
+ bytestream2_seek_p(pb, tell2, SEEK_SET);
} else {
size_t payload_size = entry->count * exif_sizes[entry->type];
if (payload_size > 4) {
- int tell = bytestream2_tell_p(pb);
tput32(pb, le, offset);
+ tell2 = bytestream2_tell_p(pb);
bytestream2_seek_p(pb, offset, SEEK_SET);
exif_write_values(pb, le, entry);
offset += payload_size;
- bytestream2_seek_p(pb, tell + 4, SEEK_SET);
+ bytestream2_seek_p(pb, tell2, SEEK_SET);
} else {
/* zero uninitialized excess payload values */
AV_WN32(pb->buffer, 0);
exif_write_values(pb, le, entry);
+ bytestream2_seek_p(pb, 4 - payload_size, SEEK_CUR);
}
}
}
@@ -666,7 +677,7 @@ static int exif_write_ifd(void *logctx, PutByteContext *pb, int le, const AVExif
* we write 0 if this is the top-level exif IFD
* indicating that there are no more IFD pointers
*/
- tput32(pb, le, 0);
+ tput32(pb, le, depth ? offset : 0);
return offset - tell;
}
@@ -722,7 +733,7 @@ int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer,
tput32(&pb, le, 8);
}
- ret = exif_write_ifd(logctx, &pb, le, ifd);
+ ret = exif_write_ifd(logctx, &pb, le, 0, ifd);
if (ret < 0) {
av_buffer_unref(&buf);
av_log(logctx, AV_LOG_ERROR, "error writing EXIF data: %s\n", av_err2str(ret));
@@ -1203,3 +1214,178 @@ end:
av_exif_free(&ifd);
return ret;
}
+
+static int exif_get_orientation(const int32_t *display_matrix)
+{
+ double rotation = av_display_rotation_get(display_matrix);
+ // determinant
+ int vflip = ((int64_t)display_matrix[0] * (int64_t)display_matrix[4]
+ - (int64_t)display_matrix[1] * (int64_t)display_matrix[3]) < 0;
+ if (!isfinite(rotation))
+ return 1;
+ if (vflip) {
+ if (rotation > 181.0 || rotation > -179.0 && rotation < -89.0)
+ return 5;
+ if (rotation > 91.0 || rotation < -179.0 && rotation > -269.0)
+ return 2;
+ if (rotation > 1.0 || rotation < -269.0)
+ return 7;
+ return 4;
+ } else {
+ if (rotation > 181.0 || rotation > -179.0 && rotation < -89.0)
+ return 6;
+ if (rotation > 91.0 || rotation < -179.0 && rotation > -269.0)
+ return 3;
+ if (rotation > 1.0 || rotation < -269.0)
+ return 8;
+ return 1;
+ }
+}
+
+int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer_ptr, enum AVExifHeaderMode header_mode)
+{
+ int ret = 0;
+ AVBufferRef *buffer = NULL;
+ AVFrameSideData *sd_exif = NULL;
+ AVFrameSideData *sd_orient = NULL;
+ AVExifMetadata ifd = { 0 };
+ AVExifEntry *or = NULL;
+ AVExifEntry *iw = NULL;
+ AVExifEntry *ih = NULL;
+ AVExifEntry *pw = NULL;
+ AVExifEntry *ph = NULL;
+ AVExifMetadata *exif = NULL;
+ uint64_t orientation = 1;
+ uint64_t w = frame->width;
+ uint64_t h = frame->height;
+ int rewrite = 0;
+
+ if (!buffer_ptr || *buffer_ptr)
+ return AVERROR(EINVAL);
+
+ sd_exif = av_frame_get_side_data(frame, AV_FRAME_DATA_EXIF);
+ sd_orient = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX);
+
+ if (!sd_exif && !sd_orient)
+ return 0;
+
+ if (sd_orient)
+ orientation = exif_get_orientation((int32_t *) sd_orient->data);
+ if (orientation != 1)
+ av_log(logctx, AV_LOG_DEBUG, "matrix contains nontrivial EXIF orientation: %" PRIu64 "\n", orientation);
+
+ if (sd_exif) {
+ ret = av_exif_parse_buffer(logctx, sd_exif->data, sd_exif->size, &ifd, AV_EXIF_TIFF_HEADER);
+ if (ret < 0)
+ goto end;
+ }
+
+ for (size_t i = 0; i < ifd.count; i++) {
+ AVExifEntry *entry = &ifd.entries[i];
+ if (entry->id == ORIENTATION_TAG && entry->count > 0 && entry->type == AV_TIFF_SHORT) {
+ or = entry;
+ continue;
+ }
+ if (entry->id == IMAGE_WIDTH_TAG && entry->count > 0 && entry->type == AV_TIFF_LONG) {
+ iw = entry;
+ continue;
+ }
+ if (entry->id == IMAGE_LENGTH_TAG && entry->count > 0 && entry->type == AV_TIFF_LONG) {
+ ih = entry;
+ continue;
+ }
+ if (entry->id == EXIFIFD_TAG && entry->type == AV_TIFF_IFD) {
+ exif = &entry->value.ifd;
+ for (size_t j = 0; j < exif->count; j++) {
+ AVExifEntry *exifentry = &exif->entries[j];
+ if (exifentry->id == PIXEL_X_TAG && exifentry->count > 0 && exifentry->type == AV_TIFF_SHORT) {
+ pw = exifentry;
+ continue;
+ }
+ if (exifentry->id == PIXEL_Y_TAG && exifentry->count > 0 && exifentry->type == AV_TIFF_SHORT) {
+ ph = exifentry;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (or && or->value.uint[0] != orientation) {
+ rewrite = 1;
+ or->value.uint[0] = orientation;
+ }
+ if (iw && iw->value.uint[0] != w) {
+ rewrite = 1;
+ iw->value.uint[0] = w;
+ }
+ if (ih && ih->value.uint[0] != h) {
+ rewrite = 1;
+ ih->value.uint[0] = h;
+ }
+ if (pw && pw->value.uint[0] != w) {
+ rewrite = 1;
+ pw->value.uint[0] = w;
+ }
+ if (ph && ph->value.uint[0] != h) {
+ rewrite = 1;
+ ph->value.uint[0] = h;
+ }
+ if (!or && orientation != 1) {
+ rewrite = 1;
+ ret = av_exif_set_entry(logctx, &ifd, ORIENTATION_TAG, AV_TIFF_SHORT, 1, NULL, 0, &orientation);
+ if (ret < 0)
+ goto end;
+ }
+ if (!iw && w) {
+ rewrite = 1;
+ ret = av_exif_set_entry(logctx, &ifd, IMAGE_WIDTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &w);
+ if (ret < 0)
+ goto end;
+ }
+ if (!ih && h) {
+ rewrite = 1;
+ ret = av_exif_set_entry(logctx, &ifd, IMAGE_LENGTH_TAG, AV_TIFF_LONG, 1, NULL, 0, &h);
+ if (ret < 0)
+ goto end;
+ }
+ if (!pw && w && w < 0xFFFFu || !ph && h && h < 0xFFFFu) {
+ rewrite = 1;
+ if (!exif) {
+ AVExifMetadata exif_new = { 0 };
+ ret = av_exif_set_entry(logctx, &ifd, EXIFIFD_TAG, AV_TIFF_IFD, 1, NULL, 0, &exif_new);
+ if (ret < 0) {
+ av_exif_free(&exif_new);
+ goto end;
+ }
+ exif = &ifd.entries[ifd.count - 1].value.ifd;
+ }
+ if (!pw && w && w < 0xFFFFu) {
+ ret = av_exif_set_entry(logctx, exif, PIXEL_X_TAG, AV_TIFF_SHORT, 1, NULL, 0, &w);
+ if (ret < 0)
+ goto end;
+ }
+ if (!ph && h && h < 0xFFFFu) {
+ ret = av_exif_set_entry(logctx, exif, PIXEL_Y_TAG, AV_TIFF_SHORT, 1, NULL, 0, &h);
+ if (ret < 0)
+ goto end;
+ }
+ }
+
+ if (rewrite) {
+ ret = av_exif_write(logctx, &ifd, &buffer, header_mode);
+ if (ret < 0)
+ goto end;
+
+ *buffer_ptr = buffer;
+ } else {
+ *buffer_ptr = av_buffer_ref(sd_exif->buf);
+ if (!*buffer_ptr) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ }
+
+end:
+ av_exif_free(&ifd);
+ return ret;
+}
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
index 88e5701daa..4fd147fb99 100644
--- a/libavcodec/exif_internal.h
+++ b/libavcodec/exif_internal.h
@@ -43,7 +43,7 @@
* 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 transfered to the frame directly. On failure,
+ * unrefed, or its ownership is transferred to the frame directly. On failure,
* the data buffer is left owned by the caller.
*/
int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum AVExifHeaderMode header_mode);
@@ -59,4 +59,18 @@ int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum
*/
int ff_exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd);
+/**
+ * Gets all relevant side data, collects it into an IFD, and writes it into the
+ * corresponding buffer pointer. This includes both AV_FRAME_DATA_EXIF and other
+ * side data types that are included in the frame data, such as possibly an
+ * instance of AV_FRAME_DATA_DISPLAYMATRIX. It also sets width and height tags
+ * to match those of the AVFrame if they are different.
+ *
+ * Upon error, *buffer will be NULL. The buffer becomes owned by the caller upon
+ * success. The *buffer argument must be NULL before calling. If *buffer is NULL
+ * upon return then a negative return value indicates an error, and a zero return
+ * value indicates that there was no EXIF data to write.
+ */
+int ff_exif_get_buffer(void *logctx, const AVFrame *frame, AVBufferRef **buffer, enum AVExifHeaderMode header_mode);
+
#endif /* AVCODEC_EXIF_INTERNAL_H */
commit e3aa1154aab2656c91ce61915f79516d9b563b61
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Mar 12 12:21:21 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/exif: remove ff_exif_decode_ifd
This function is no longer called and its functionality
can be accessed using the new API in exif.h as well as the
various internal attach functions in exif_internal.h.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 66afe11908..aa2246ae9a 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -936,27 +936,21 @@ int av_exif_ifd_to_dict(void *logctx, const AVExifMetadata *ifd, AVDictionary **
return exif_ifd_to_dict(logctx, "", ifd, metadata);
}
-int ff_exif_decode_ifd(void *logctx, GetByteContext *gb, int le, int depth, AVDictionary **metadata)
+#if FF_API_OLD_EXIF
+int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
+ int le, int depth, AVDictionary **metadata)
{
AVExifMetadata ifd = { 0 };
- int ret = exif_parse_ifd_list(logctx, gb, le, depth, &ifd);
+ GetByteContext gb;
+ int ret;
+ bytestream2_init(&gb, buf, size);
+ ret = exif_parse_ifd_list(logctx, &gb, le, depth, &ifd);
if (ret < 0)
return ret;
-
ret = av_exif_ifd_to_dict(logctx, &ifd, metadata);
av_exif_free(&ifd);
-
return ret;
}
-
-#if FF_API_OLD_EXIF
-int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
- int le, int depth, AVDictionary **metadata)
-{
- GetByteContext gb;
- bytestream2_init(&gb, buf, size);
- return ff_exif_decode_ifd(logctx, &gb, le, depth, metadata);
-}
#endif /* FF_API_OLD_EXIF */
static int exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd, AVBufferRef *og)
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
index bd0bd22015..88e5701daa 100644
--- a/libavcodec/exif_internal.h
+++ b/libavcodec/exif_internal.h
@@ -31,10 +31,8 @@
#define AVCODEC_EXIF_INTERNAL_H
#include "libavutil/buffer.h"
-#include "libavutil/dict.h"
#include "libavutil/frame.h"
-#include "bytestream.h"
#include "exif.h"
/**
@@ -61,8 +59,4 @@ int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum
*/
int ff_exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd);
-/* Used by mjpeg decoder */
-int ff_exif_decode_ifd(void *logctx, GetByteContext *gb,
- int le, int depth, AVDictionary **metadata);
-
#endif /* AVCODEC_EXIF_INTERNAL_H */
commit c6cc2115f45ac26ae42442f8796996eb410f4028
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Mar 12 11:27:13 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/webp: use new EXIF parse API
Switch over to the new API to parse EXIF metadata.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/webp.c b/libavcodec/webp.c
index 489cf6f016..56c3ec6d28 100644
--- a/libavcodec/webp.c
+++ b/libavcodec/webp.c
@@ -1456,38 +1456,34 @@ FF_ENABLE_DEPRECATION_WARNINGS
break;
}
case MKTAG('E', 'X', 'I', 'F'): {
- int le, ifd_offset, exif_offset = bytestream2_tell(&gb);
- AVDictionary *exif_metadata = NULL;
- GetByteContext exif_gb;
+ AVBufferRef *exif_buf = NULL;
if (s->has_exif) {
av_log(avctx, AV_LOG_VERBOSE, "Ignoring extra EXIF chunk\n");
goto exif_end;
}
+
if (!(vp8x_flags & VP8X_FLAG_EXIF_METADATA))
av_log(avctx, AV_LOG_WARNING,
"EXIF chunk present, but Exif bit not set in the "
"VP8X header\n");
- s->has_exif = 1;
- bytestream2_init(&exif_gb, avpkt->data + exif_offset,
- avpkt->size - exif_offset);
- if (ff_tdecode_header(&exif_gb, &le, &ifd_offset) < 0) {
- av_log(avctx, AV_LOG_ERROR, "invalid TIFF header "
- "in Exif data\n");
+ exif_buf = av_buffer_alloc(chunk_size);
+ if (!exif_buf) {
+ av_log(avctx, AV_LOG_WARNING, "unable to allocate EXIF buffer\n");
goto exif_end;
}
+ s->has_exif = 1;
+ memcpy(exif_buf->data, gb.buffer, chunk_size);
- bytestream2_seek(&exif_gb, ifd_offset, SEEK_SET);
- if (ff_exif_decode_ifd(avctx, &exif_gb, le, 0, &exif_metadata) < 0) {
- av_log(avctx, AV_LOG_ERROR, "error decoding Exif data\n");
- goto exif_end;
+ /* if this succeeds then exif_buf is either freed or transferred to the AVFrame */
+ ret = ff_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);
}
- av_dict_copy(&p->metadata, exif_metadata, 0);
-
exif_end:
- av_dict_free(&exif_metadata);
bytestream2_skip(&gb, chunk_size);
break;
}
diff --git a/tests/ref/fate/exif-image-webp b/tests/ref/fate/exif-image-webp
index 3a0e6e2c24..7c01ae3d66 100644
--- a/tests/ref/fate/exif-image-webp
+++ b/tests/ref/fate/exif-image-webp
@@ -197,4 +197,7 @@ TAG:ExifIFD/ExposureMode= 0
TAG:ExifIFD/WhiteBalance= 0
TAG:ExifIFD/DigitalZoomRatio= 4000:4000
TAG:ExifIFD/SceneCaptureType= 0
+[SIDE_DATA]
+side_data_type=EXIF metadata
+[/SIDE_DATA]
[/FRAME]
commit 52dba25661305e3c4a6209d46aea43cd327c960e
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Mar 12 09:54:09 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/mjpegdec: use new EXIF parse API
Switch over to the new API to parse EXIF metadata.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index 983a30cb59..46ec3eb938 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -55,7 +55,6 @@
#include "put_bits.h"
#include "exif_internal.h"
#include "bytestream.h"
-#include "tiff_common.h"
static int init_default_huffman_tables(MJpegDecodeContext *s)
@@ -2043,8 +2042,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s)
/* EXIF metadata */
if (s->start_code == APP1 && id == AV_RB32("Exif") && len >= 2) {
- GetByteContext gbytes;
- int ret, le, ifd_offset, bytes_read;
+ int ret;
const uint8_t *aligned;
skip_bits(&s->gb, 16); // skip padding
@@ -2052,26 +2050,15 @@ static int mjpeg_decode_app(MJpegDecodeContext *s)
// init byte wise reading
aligned = align_get_bits(&s->gb);
- bytestream2_init(&gbytes, aligned, len);
- // read TIFF header
- ret = ff_tdecode_header(&gbytes, &le, &ifd_offset);
- if (ret) {
- av_log(s->avctx, AV_LOG_ERROR, "mjpeg: invalid TIFF header in EXIF data\n");
- } else {
- bytestream2_seek(&gbytes, ifd_offset, SEEK_SET);
-
- // read 0th IFD and store the metadata
- // (return values > 0 indicate the presence of subimage metadata)
- ret = ff_exif_decode_ifd(s->avctx, &gbytes, le, 0, &s->exif_metadata);
- if (ret < 0) {
- av_log(s->avctx, AV_LOG_ERROR, "mjpeg: error decoding EXIF data\n");
- }
+ ret = av_exif_parse_buffer(s->avctx, aligned, len, &s->exif_metadata, AV_EXIF_TIFF_HEADER);
+ if (ret < 0) {
+ av_log(s->avctx, AV_LOG_WARNING, "unable to parse EXIF buffer\n");
+ goto out;
}
- bytes_read = bytestream2_tell(&gbytes);
- skip_bits(&s->gb, bytes_read << 3);
- len -= bytes_read;
+ skip_bits(&s->gb, ret << 3);
+ len -= ret;
goto out;
}
@@ -2384,13 +2371,12 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame,
int index;
int ret = 0;
int is16bit;
- AVDictionaryEntry *e = NULL;
s->force_pal8 = 0;
s->buf_size = buf_size;
- av_dict_free(&s->exif_metadata);
+ av_exif_free(&s->exif_metadata);
av_freep(&s->stereo3d);
s->adobe_transform = -1;
@@ -2868,60 +2854,13 @@ the_end:
}
}
- if (e = av_dict_get(s->exif_metadata, "Orientation", e, AV_DICT_IGNORE_SUFFIX)) {
- char *value = e->value + strspn(e->value, " \n\t\r"), *endptr;
- int orientation = strtol(value, &endptr, 0);
-
- if (!*endptr) {
- AVFrameSideData *sd = NULL;
-
- if (orientation >= 2 && orientation <= 8) {
- int32_t *matrix;
-
- sd = av_frame_new_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9);
- if (!sd) {
- av_log(avctx, AV_LOG_ERROR, "Could not allocate frame side data\n");
- return AVERROR(ENOMEM);
- }
-
- matrix = (int32_t *)sd->data;
-
- switch (orientation) {
- case 2:
- av_display_rotation_set(matrix, 0.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 3:
- av_display_rotation_set(matrix, 180.0);
- break;
- case 4:
- av_display_rotation_set(matrix, 180.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 5:
- av_display_rotation_set(matrix, 90.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 6:
- av_display_rotation_set(matrix, 90.0);
- break;
- case 7:
- av_display_rotation_set(matrix, -90.0);
- av_display_matrix_flip(matrix, 1, 0);
- break;
- case 8:
- av_display_rotation_set(matrix, -90.0);
- break;
- default:
- av_assert0(0);
- }
- }
- }
+ if (s->exif_metadata.entries) {
+ ret = ff_exif_attach_ifd(avctx, frame, &s->exif_metadata);
+ av_exif_free(&s->exif_metadata);
+ if (ret < 0)
+ av_log(avctx, AV_LOG_WARNING, "couldn't attach EXIF metadata\n");
}
- av_dict_copy(&frame->metadata, s->exif_metadata, 0);
- av_dict_free(&s->exif_metadata);
-
if (avctx->codec_id != AV_CODEC_ID_SMVJPEG &&
(avctx->codec_tag == MKTAG('A', 'V', 'R', 'n') ||
avctx->codec_tag == MKTAG('A', 'V', 'D', 'J')) &&
@@ -2973,7 +2912,7 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx)
av_freep(&s->blocks[i]);
av_freep(&s->last_nnz[i]);
}
- av_dict_free(&s->exif_metadata);
+ av_exif_free(&s->exif_metadata);
reset_icc_profile(s);
diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h
index 13c524d597..8b9ed67856 100644
--- a/libavcodec/mjpegdec.h
+++ b/libavcodec/mjpegdec.h
@@ -36,6 +36,7 @@
#include "avcodec.h"
#include "blockdsp.h"
+#include "exif.h"
#include "get_bits.h"
#include "hpeldsp.h"
#include "idctdsp.h"
@@ -138,7 +139,7 @@ typedef struct MJpegDecodeContext {
unsigned int ljpeg_buffer_size;
int extern_huff;
- AVDictionary *exif_metadata;
+ AVExifMetadata exif_metadata;
AVStereo3D *stereo3d; ///!< stereoscopic information (cached, since it is read before frame allocation)
diff --git a/tests/ref/fate/exif-image-embedded b/tests/ref/fate/exif-image-embedded
index 574a3af848..a6617bd89f 100644
--- a/tests/ref/fate/exif-image-embedded
+++ b/tests/ref/fate/exif-image-embedded
@@ -32,6 +32,9 @@ color_transfer=unknown
chroma_location=center
TAG:ExifIFD/UserComment=AppleMark
+[SIDE_DATA]
+side_data_type=EXIF metadata
+[/SIDE_DATA]
[/FRAME]
[FRAME]
media_type=audio
diff --git a/tests/ref/fate/exif-image-jpg b/tests/ref/fate/exif-image-jpg
index 5a8cd10063..5135ae32f8 100644
--- a/tests/ref/fate/exif-image-jpg
+++ b/tests/ref/fate/exif-image-jpg
@@ -197,4 +197,7 @@ TAG:ExifIFD/ExposureMode= 0
TAG:ExifIFD/WhiteBalance= 0
TAG:ExifIFD/DigitalZoomRatio= 4000:4000
TAG:ExifIFD/SceneCaptureType= 0
+[SIDE_DATA]
+side_data_type=EXIF metadata
+[/SIDE_DATA]
[/FRAME]
commit bfb17d26306592c85cf0c4e909099c621177b062
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 11 12:22:50 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:47 2025 -0400
avcodec/exif: add deprecation guards for old avpriv_
Add FF_API_OLD_EXIF deprecation guard based on lavc version < 62 in
order to allow removing the avpriv_ next major version bump.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index fd8c1086f4..66afe11908 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -949,6 +949,7 @@ int ff_exif_decode_ifd(void *logctx, GetByteContext *gb, int le, int depth, AVDi
return ret;
}
+#if FF_API_OLD_EXIF
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
int le, int depth, AVDictionary **metadata)
{
@@ -956,6 +957,7 @@ int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
bytestream2_init(&gb, buf, size);
return ff_exif_decode_ifd(logctx, &gb, le, depth, metadata);
}
+#endif /* FF_API_OLD_EXIF */
static int exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd, AVBufferRef *og)
{
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index f76ab1e125..2661f86582 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -36,6 +36,7 @@
#include "libavutil/buffer.h"
#include "libavutil/dict.h"
#include "libavutil/rational.h"
+#include "version_major.h"
/** Data type identifiers for TIFF tags */
enum AVTiffDataType {
@@ -183,8 +184,10 @@ int av_exif_ifd_to_dict(void *logctx, const AVExifMetadata *ifd, AVDictionary **
*/
AVExifMetadata *av_exif_clone_ifd(const AVExifMetadata *ifd);
+#if FF_API_OLD_EXIF
/* Used by the AVI demuxer */
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
int le, int depth, AVDictionary **metadata);
+#endif /* FF_API_OLD_EXIF */
#endif /* AVCODEC_EXIF_H */
diff --git a/libavcodec/version_major.h b/libavcodec/version_major.h
index 97e4e12065..6ffe82e2e7 100644
--- a/libavcodec/version_major.h
+++ b/libavcodec/version_major.h
@@ -42,6 +42,7 @@
#define FF_API_V408_CODECID (LIBAVCODEC_VERSION_MAJOR < 63)
#define FF_API_CODEC_PROPS (LIBAVCODEC_VERSION_MAJOR < 63)
#define FF_API_EXR_GAMMA (LIBAVCODEC_VERSION_MAJOR < 63)
+#define FF_API_OLD_EXIF (LIBAVCODEC_VERSION_MAJOR < 63)
#define FF_API_NVDEC_OLD_PIX_FMTS (LIBAVCODEC_VERSION_MAJOR < 63)
commit ba2ea285e0f270a0a885b414cafaace6a89b9a91
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 11 12:15:14 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:46 2025 -0400
avformat/avidec: use new EXIF metadata API instead of avpriv_
This commit switches avidec to use the new metadata API exposed by
the previous commit instead of relying on the existing avipriv_ function
exposed by lavc.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavformat/avidec.c b/libavformat/avidec.c
index 81a0ae31ba..8fb6931e5d 100644
--- a/libavformat/avidec.c
+++ b/libavformat/avidec.c
@@ -427,15 +427,21 @@ static int avi_extract_stream_metadata(AVFormatContext *s, AVStream *st)
tag = bytestream2_get_le32(&gb);
switch (tag) {
- case MKTAG('A', 'V', 'I', 'F'):
+ case MKTAG('A', 'V', 'I', 'F'): {
+ AVExifMetadata ifd = { 0 };
+ int ret;
// skip 4 byte padding
bytestream2_skip(&gb, 4);
offset = bytestream2_tell(&gb);
// decode EXIF tags from IFD, AVI is always little-endian
- return avpriv_exif_decode_ifd(s, data + offset, data_size - offset,
- 1, 0, &st->metadata);
- break;
+ ret = av_exif_parse_buffer(s, data + offset, data_size - offset, &ifd, AV_EXIF_ASSUME_LE);
+ if (ret < 0)
+ return ret;
+ ret = av_exif_ifd_to_dict(s, &ifd, &st->metadata);
+ av_exif_free(&ifd);
+ return ret;
+ }
case MKTAG('C', 'A', 'S', 'I'):
avpriv_request_sample(s, "RIFF stream data tag type CASI (%u)", tag);
break;
commit ad77345a5d14862f4701e5ad422b03b14934a5b9
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Thu Mar 6 17:53:41 2025 -0500
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:46 2025 -0400
avcodec/exif: add EXIF parser and struct API
This commit adds a structure to contain parsed EXIF metadata, as well
as code to read and write that struct from/to binary EXIF buffers. Some
internal functions have been moved to exif_internal.h. Code to read
from this new struct and write to an AVDictionary **dict has been added
as well in order to preserve interoperability with existing callers.
The only codec changes so far as of this commit are to call these
interop functions, but in future commits there will be codec changes to
use the new parsing routines instead.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/Changelog b/Changelog
index 98b259f17f..838114934c 100644
--- a/Changelog
+++ b/Changelog
@@ -38,6 +38,7 @@ version 8.0:
- ProRes RAW decoder
- ProRes RAW Vulkan hwaccel
- ffprobe -codec option
+- EXIF Metadata Parsing
version 7.1:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index fb22541f8d..35408949ac 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -15,6 +15,7 @@ HEADERS = ac3_parser.h \
dirac.h \
dv_profile.h \
dxva2.h \
+ exif.h \
jni.h \
mediacodec.h \
packet.h \
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index f6f8918586..fd8c1086f4 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -1,6 +1,7 @@
/*
* EXIF metadata parser
* Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * Copyright (c) 2024-2025 Leo Izen <leo.izen(a)gmail.com>
*
* This file is part of FFmpeg.
*
@@ -23,16 +24,33 @@
* @file
* EXIF metadata parser
* @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * @author Leo Izen <leo.izen(a)gmail.com>
*/
-#include "exif.h"
+#include <inttypes.h>
+
+#include "libavutil/bprint.h"
+#include "libavutil/display.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
+
+#include "bytestream.h"
+#include "exif_internal.h"
#include "tiff_common.h"
+#define EXIF_II_LONG 0x49492a00
+#define EXIF_MM_LONG 0x4d4d002a
+
+#define BASE_TAG_SIZE 12
+#define IFD_EXTRA_SIZE 6
+
#define EXIF_TAG_NAME_LENGTH 32
+#define MAKERNOTE_TAG 0x927c
+#define ORIENTATION_TAG 0x112
struct exif_tag {
- char name[EXIF_TAG_NAME_LENGTH];
- uint16_t id;
+ const char name[EXIF_TAG_NAME_LENGTH];
+ uint16_t id;
};
static const struct exif_tag tag_list[] = { // JEITA CP-3451 EXIF specification:
@@ -152,15 +170,45 @@ static const struct exif_tag tag_list[] = { // JEITA CP-3451 EXIF specification:
{"Saturation", 0xA409},
{"Sharpness", 0xA40A},
{"DeviceSettingDescription", 0xA40B},
- {"SubjectDistanceRange", 0xA40C}
-// {"InteroperabilityIndex", 0x1}, // <- Table 13 Interoperability IFD Attribute Information
+ {"SubjectDistanceRange", 0xA40C},
+
+ /* InteropIFD tags */
+ {"RelatedImageFileFormat", 0x1000},
+ {"RelatedImageWidth", 0x1001},
+ {"RelatedImageLength", 0x1002},
+
+ /* private EXIF tags */
+ {"PrintImageMatching", 0xC4A5}, // <- undocumented meaning
+
+ /* IFD tags */
+ {"ExifIFD", 0x8769}, // <- An IFD pointing to standard Exif metadata
+ {"GPSInfo", 0x8825}, // <- An IFD pointing to GPS Exif Metadata
+ {"InteropIFD", 0xA005}, // <- Table 13 Interoperability IFD Attribute Information
+ {"GlobalParametersIFD", 0x0190},
+ {"ProfileIFD", 0xc6f5},
};
-static const char *exif_get_tag_name(uint16_t id)
-{
- int i;
+/* same as type_sizes but with string == 1 */
+static const size_t exif_sizes[] = {
+ [0] = 0,
+ [AV_TIFF_BYTE] = 1,
+ [AV_TIFF_STRING] = 1,
+ [AV_TIFF_SHORT] = 2,
+ [AV_TIFF_LONG] = 4,
+ [AV_TIFF_RATIONAL] = 8,
+ [AV_TIFF_SBYTE] = 1,
+ [AV_TIFF_UNDEFINED] = 1,
+ [AV_TIFF_SSHORT] = 2,
+ [AV_TIFF_SLONG] = 4,
+ [AV_TIFF_SRATIONAL] = 8,
+ [AV_TIFF_FLOAT] = 4,
+ [AV_TIFF_DOUBLE] = 8,
+ [AV_TIFF_IFD] = 4,
+};
- for (i = 0; i < FF_ARRAY_ELEMS(tag_list); i++) {
+const char *av_exif_get_tag_name(uint16_t id)
+{
+ for (size_t i = 0; i < FF_ARRAY_ELEMS(tag_list); i++) {
if (tag_list[i].id == id)
return tag_list[i].name;
}
@@ -168,106 +216,994 @@ static const char *exif_get_tag_name(uint16_t id)
return NULL;
}
+int32_t av_exif_get_tag_id(const char *name)
+{
+ if (!name)
+ return -1;
-static int exif_add_metadata(void *logctx, int count, int type,
- const char *name, const char *sep,
- GetByteContext *gb, int le,
- AVDictionary **metadata)
+ for (size_t i = 0; i < FF_ARRAY_ELEMS(tag_list); i++) {
+ if (!strcmp(tag_list[i].name, name))
+ return tag_list[i].id;
+ }
+
+ return -1;
+}
+
+static inline void tput16(PutByteContext *pb, const int le, const uint16_t value)
{
- switch(type) {
- case 0:
- av_log(logctx, AV_LOG_WARNING,
- "Invalid TIFF tag type 0 found for %s with size %d\n",
- name, count);
- return 0;
- case AV_TIFF_DOUBLE : return ff_tadd_doubles_metadata(count, name, sep, gb, le, metadata);
- case AV_TIFF_SSHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 1, metadata);
- case AV_TIFF_SHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 0, metadata);
- case AV_TIFF_SBYTE : return ff_tadd_bytes_metadata(count, name, sep, gb, le, 1, metadata);
- case AV_TIFF_BYTE :
- case AV_TIFF_UNDEFINED: return ff_tadd_bytes_metadata(count, name, sep, gb, le, 0, metadata);
- case AV_TIFF_STRING : return ff_tadd_string_metadata(count, name, gb, le, metadata);
- case AV_TIFF_SRATIONAL:
- case AV_TIFF_RATIONAL : return ff_tadd_rational_metadata(count, name, sep, gb, le, metadata);
- case AV_TIFF_SLONG :
- case AV_TIFF_LONG : return ff_tadd_long_metadata(count, name, sep, gb, le, metadata);
- default:
- avpriv_request_sample(logctx, "TIFF tag type (%u)", type);
- return 0;
- };
+ le ? bytestream2_put_le16(pb, value) : bytestream2_put_be16(pb, value);
}
+static inline void tput32(PutByteContext *pb, const int le, const uint32_t value)
+{
+ le ? bytestream2_put_le32(pb, value) : bytestream2_put_be32(pb, value);
+}
-static int exif_decode_tag(void *logctx, GetByteContext *gbytes, int le,
- int depth, AVDictionary **metadata)
+static inline void tput64(PutByteContext *pb, const int le, const uint64_t value)
{
- int ret, cur_pos;
- unsigned id, count;
- enum AVTiffDataType type;
+ le ? bytestream2_put_le64(pb, value) : bytestream2_put_be64(pb, value);
+}
- if (depth > 2) {
- return 0;
+static int exif_read_values(void *logctx, GetByteContext *gb, int le, AVExifEntry *entry)
+{
+ switch (entry->type) {
+ case AV_TIFF_SHORT:
+ case AV_TIFF_LONG:
+ entry->value.uint = av_calloc(entry->count, sizeof(*entry->value.uint));
+ break;
+ case AV_TIFF_SSHORT:
+ case AV_TIFF_SLONG:
+ entry->value.sint = av_calloc(entry->count, sizeof(*entry->value.sint));
+ break;
+ case AV_TIFF_DOUBLE:
+ case AV_TIFF_FLOAT:
+ entry->value.dbl = av_calloc(entry->count, sizeof(*entry->value.dbl));
+ break;
+ case AV_TIFF_RATIONAL:
+ case AV_TIFF_SRATIONAL:
+ entry->value.rat = av_calloc(entry->count, sizeof(*entry->value.rat));
+ break;
+ case AV_TIFF_UNDEFINED:
+ case AV_TIFF_BYTE:
+ entry->value.ubytes = av_mallocz(entry->count);
+ break;
+ case AV_TIFF_SBYTE:
+ entry->value.sbytes = av_mallocz(entry->count);
+ break;
+ case AV_TIFF_STRING:
+ entry->value.str = av_mallocz(entry->count + 1);
+ break;
+ case AV_TIFF_IFD:
+ av_log(logctx, AV_LOG_WARNING, "Bad IFD type for non-IFD tag\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (!entry->value.ptr)
+ return AVERROR(ENOMEM);
+ switch (entry->type) {
+ case AV_TIFF_SHORT:
+ for (size_t i = 0; i < entry->count; i++)
+ entry->value.uint[i] = ff_tget_short(gb, le);
+ break;
+ case AV_TIFF_LONG:
+ for (size_t i = 0; i < entry->count; i++)
+ entry->value.uint[i] = ff_tget_long(gb, le);
+ break;
+ case AV_TIFF_SSHORT:
+ for (size_t i = 0; i < entry->count; i++)
+ entry->value.sint[i] = (int16_t) ff_tget_short(gb, le);
+ break;
+ case AV_TIFF_SLONG:
+ for (size_t i = 0; i < entry->count; i++)
+ entry->value.sint[i] = (int32_t) ff_tget_long(gb, le);
+ break;
+ case AV_TIFF_DOUBLE:
+ for (size_t i = 0; i < entry->count; i++)
+ entry->value.dbl[i] = ff_tget_double(gb, le);
+ break;
+ case AV_TIFF_FLOAT:
+ for (size_t i = 0; i < entry->count; i++) {
+ av_alias32 alias = { .u32 = ff_tget_long(gb, le) };
+ entry->value.dbl[i] = alias.f32;
+ }
+ break;
+ case AV_TIFF_RATIONAL:
+ case AV_TIFF_SRATIONAL:
+ for (size_t i = 0; i < entry->count; i++) {
+ int32_t num = ff_tget_long(gb, le);
+ int32_t den = ff_tget_long(gb, le);
+ entry->value.rat[i] = av_make_q(num, den);
+ }
+ break;
+ case AV_TIFF_UNDEFINED:
+ case AV_TIFF_BYTE:
+ bytestream2_get_buffer(gb, entry->value.ubytes, entry->count);
+ break;
+ case AV_TIFF_SBYTE:
+ bytestream2_get_buffer(gb, entry->value.sbytes, entry->count);
+ break;
+ case AV_TIFF_STRING:
+ bytestream2_get_buffer(gb, entry->value.str, entry->count);
+ break;
}
- ff_tread_tag(gbytes, le, &id, &type, &count, &cur_pos);
+ return 0;
+}
- if (!bytestream2_tell(gbytes)) {
- bytestream2_seek(gbytes, cur_pos, SEEK_SET);
- return 0;
+static void exif_write_values(PutByteContext *pb, int le, const AVExifEntry *entry)
+{
+ switch (entry->type) {
+ case AV_TIFF_SHORT:
+ for (size_t i = 0; i < entry->count; i++)
+ tput16(pb, le, entry->value.uint[i]);
+ break;
+ case AV_TIFF_LONG:
+ for (size_t i = 0; i < entry->count; i++)
+ tput32(pb, le, entry->value.uint[i]);
+ break;
+ case AV_TIFF_SSHORT:
+ for (size_t i = 0; i < entry->count; i++)
+ tput16(pb, le, entry->value.sint[i]);
+ break;
+ case AV_TIFF_SLONG:
+ for (size_t i = 0; i < entry->count; i++)
+ tput32(pb, le, entry->value.sint[i]);
+ break;
+ case AV_TIFF_DOUBLE:
+ for (size_t i = 0; i < entry->count; i++) {
+ const av_alias64 a = { .f64 = entry->value.dbl[i] };
+ tput64(pb, le, a.u64);
+ }
+ break;
+ case AV_TIFF_FLOAT:
+ for (size_t i = 0; i < entry->count; i++) {
+ const av_alias32 a = { .f32 = entry->value.dbl[i] };
+ tput32(pb, le, a.u32);
+ }
+ break;
+ case AV_TIFF_RATIONAL:
+ case AV_TIFF_SRATIONAL:
+ for (size_t i = 0; i < entry->count; i++) {
+ tput32(pb, le, entry->value.rat[i].num);
+ tput32(pb, le, entry->value.rat[i].den);
+ }
+ break;
+ case AV_TIFF_UNDEFINED:
+ case AV_TIFF_BYTE:
+ bytestream2_put_buffer(pb, entry->value.ubytes, entry->count);
+ break;
+ case AV_TIFF_SBYTE:
+ bytestream2_put_buffer(pb, entry->value.sbytes, entry->count);
+ break;
+ case AV_TIFF_STRING:
+ bytestream2_put_buffer(pb, entry->value.str, entry->count);
+ break;
}
+}
- // read count values and add it metadata
- // store metadata or proceed with next IFD
- ret = ff_tis_ifd(id);
- if (ret) {
- ret = ff_exif_decode_ifd(logctx, gbytes, le, depth + 1, metadata);
- } else {
- const char *name = exif_get_tag_name(id);
- char buf[7];
+static const uint8_t aoc_header[] = { 'A', 'O', 'C', 0, };
+static const uint8_t casio_header[] = { 'Q', 'V', 'C', 0, 0, 0, };
+static const uint8_t foveon_header[] = { 'F', 'O', 'V', 'E', 'O', 'N', 0, 0, };
+static const uint8_t fuji_header[] = { 'F', 'U', 'J', 'I', };
+static const uint8_t nikon_header[] = { 'N', 'i', 'k', 'o', 'n', 0, };
+static const uint8_t olympus1_header[] = { 'O', 'L', 'Y', 'M', 'P', 0, };
+static const uint8_t olympus2_header[] = { 'O', 'L', 'Y', 'M', 'P', 'U', 'S', 0, 'I', 'I', };
+static const uint8_t panasonic_header[] = { 'P', 'a', 'n', 'a', 's', 'o', 'n', 'i', 'c', 0, 0, 0, };
+static const uint8_t sigma_header[] = { 'S', 'I', 'G', 'M', 'A', 0, 0, 0, };
+static const uint8_t sony_header[] = { 'S', 'O', 'N', 'Y', ' ', 'D', 'S', 'C', ' ', 0, 0, 0, };
+
+struct exif_makernote_data {
+ const uint8_t *header;
+ size_t header_size;
+ int result;
+};
+
+#define MAKERNOTE_STRUCT(h, r) { \
+ .header = (h), \
+ .header_size = sizeof((h)), \
+ .result = (r), \
+}
+
+static const struct exif_makernote_data makernote_data[] = {
+ MAKERNOTE_STRUCT(aoc_header, 6),
+ MAKERNOTE_STRUCT(casio_header, -1),
+ MAKERNOTE_STRUCT(foveon_header, 10),
+ MAKERNOTE_STRUCT(fuji_header, -1),
+ MAKERNOTE_STRUCT(olympus1_header, 8),
+ MAKERNOTE_STRUCT(olympus2_header, -1),
+ MAKERNOTE_STRUCT(panasonic_header, 12),
+ MAKERNOTE_STRUCT(sigma_header, 10),
+ MAKERNOTE_STRUCT(sony_header, 12),
+};
+
+/*
+ * derived from Exiv2 MakerNote's article
+ * https://exiv2.org/makernote.html or archived at
+ * https://web.archive.org/web/20250311155857/https://exiv2.org/makernote.html
+ */
+static int exif_get_makernote_offset(GetByteContext *gb)
+{
+ if (bytestream2_get_bytes_left(gb) < BASE_TAG_SIZE)
+ return -1;
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(makernote_data); i++) {
+ if (!memcmp(gb->buffer, makernote_data[i].header, makernote_data[i].header_size))
+ return makernote_data[i].result;
+ }
+
+ if (!memcmp(gb->buffer, nikon_header, sizeof(nikon_header))) {
+ if (bytestream2_get_bytes_left(gb) < 14)
+ return -1;
+ else if (AV_RB32(gb->buffer + 10) == EXIF_MM_LONG || AV_RB32(gb->buffer + 10) == EXIF_II_LONG)
+ return -1;
+ return 8;
+ }
+
+ return 0;
+}
+
+static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
+ int depth, AVExifMetadata *ifd);
+
+static int exif_decode_tag(void *logctx, GetByteContext *gb, int le,
+ int depth, AVExifEntry *entry)
+{
+ int ret = 0, makernote_offset = -1, tell, is_ifd, count;
+ enum AVTiffDataType type;
+ uint32_t payload;
+
+ /* safety check to prevent infinite recursion on malicious IFDs */
+ if (depth > 3)
+ return AVERROR_INVALIDDATA;
+
+ tell = bytestream2_tell(gb);
+
+ entry->id = ff_tget_short(gb, le);
+ type = ff_tget_short(gb, le);
+ count = ff_tget_long(gb, le);
+ payload = ff_tget_long(gb, le);
- if (!name) {
- name = buf;
- snprintf(buf, sizeof(buf), "0x%04X", id);
+ av_log(logctx, AV_LOG_DEBUG, "TIFF Tag: id: 0x%04x, type: %d, count: %u, offset: %d, "
+ "payload: %" PRIu32 "\n", entry->id, type, count, tell, payload);
+
+ is_ifd = type == AV_TIFF_IFD || ff_tis_ifd(entry->id) || entry->id == MAKERNOTE_TAG;
+
+ if (is_ifd) {
+ if (!payload)
+ goto end;
+ bytestream2_seek(gb, payload, SEEK_SET);
+ }
+
+ if (entry->id == MAKERNOTE_TAG) {
+ makernote_offset = exif_get_makernote_offset(gb);
+ if (makernote_offset < 0)
+ is_ifd = 0;
+ }
+
+ if (is_ifd) {
+ entry->type = AV_TIFF_IFD;
+ entry->count = 1;
+ entry->ifd_offset = makernote_offset > 0 ? makernote_offset : 0;
+ if (entry->ifd_offset) {
+ entry->ifd_lead = av_malloc(entry->ifd_offset);
+ if (!entry->ifd_lead)
+ return AVERROR(ENOMEM);
+ bytestream2_get_buffer(gb, entry->ifd_lead, entry->ifd_offset);
+ }
+ ret = exif_parse_ifd_list(logctx, gb, le, depth + 1, &entry->value.ifd);
+ if (ret < 0 && entry->id == MAKERNOTE_TAG) {
+ /*
+ * we guessed that MakerNote was an IFD
+ * but we were probably incorrect at this
+ * point so we try again as a binary blob
+ */
+ av_exif_free(&entry->value.ifd);
+ av_log(logctx, AV_LOG_DEBUG, "unrecognized MakerNote IFD, retrying as blob\n");
+ is_ifd = 0;
}
+ }
- ret = exif_add_metadata(logctx, count, type, name, NULL,
- gbytes, le, metadata);
+ /* inverted condition instead of else so we can fall through from above */
+ if (!is_ifd) {
+ entry->type = type == AV_TIFF_IFD ? AV_TIFF_UNDEFINED : type;
+ entry->count = count;
+ bytestream2_seek(gb, count * exif_sizes[type] > 4 ? payload : tell + 8, SEEK_SET);
+ ret = exif_read_values(logctx, gb, le, entry);
}
- bytestream2_seek(gbytes, cur_pos, SEEK_SET);
+end:
+ bytestream2_seek(gb, tell + BASE_TAG_SIZE, SEEK_SET);
return ret;
}
-
-int ff_exif_decode_ifd(void *logctx, GetByteContext *gbytes,
- int le, int depth, AVDictionary **metadata)
+static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le,
+ int depth, AVExifMetadata *ifd)
{
- int i, ret;
- int entries;
+ uint32_t entries;
+ size_t required_size;
+ void *temp;
- entries = ff_tget_short(gbytes, le);
+ av_log(logctx, AV_LOG_DEBUG, "parsing IFD list at offset: %d\n", bytestream2_tell(gb));
- if (bytestream2_get_bytes_left(gbytes) < entries * 12) {
+ if (bytestream2_get_bytes_left(gb) < 2) {
+ av_log(logctx, AV_LOG_ERROR, "not enough bytes remaining in EXIF buffer: 2 required\n");
return AVERROR_INVALIDDATA;
}
- for (i = 0; i < entries; i++) {
- if ((ret = exif_decode_tag(logctx, gbytes, le, depth, metadata)) < 0) {
+ entries = ff_tget_short(gb, le);
+ if (bytestream2_get_bytes_left(gb) < entries * BASE_TAG_SIZE) {
+ av_log(logctx, AV_LOG_ERROR, "not enough bytes remaining in EXIF buffer. entries: %" PRIu32 "\n", entries);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ifd->count = entries;
+ av_log(logctx, AV_LOG_DEBUG, "entry count for IFD: %u\n", ifd->count);
+
+ if (av_size_mult(ifd->count, sizeof(*ifd->entries), &required_size) < 0)
+ return AVERROR(ENOMEM);
+ temp = av_fast_realloc(ifd->entries, &ifd->size, required_size);
+ if (!temp) {
+ av_freep(&ifd->entries);
+ return AVERROR(ENOMEM);
+ }
+ ifd->entries = temp;
+
+ /* entries have pointers in them which can cause issues if */
+ /* they are freed or realloc'd when garbage */
+ memset(ifd->entries, 0, required_size);
+
+ for (uint32_t i = 0; i < entries; i++) {
+ int ret = exif_decode_tag(logctx, gb, le, depth, &ifd->entries[i]);
+ if (ret < 0)
return ret;
+ }
+
+ /*
+ * at the end of an IFD is an pointer to the next IFD
+ * or zero if there are no more IFDs, which is usually the case
+ */
+ return ff_tget_long(gb, le);
+}
+
+/*
+ * note that this function does not free the entry pointer itself
+ * because it's probably part of a larger array that should be freed
+ * all at once
+ */
+static void exif_free_entry(AVExifEntry *entry)
+{
+ if (!entry)
+ return;
+ if (entry->type == AV_TIFF_IFD)
+ av_exif_free(&entry->value.ifd);
+ else
+ av_freep(&entry->value.ptr);
+ av_freep(&entry->ifd_lead);
+}
+
+void av_exif_free(AVExifMetadata *ifd)
+{
+ if (!ifd)
+ return;
+ if (!ifd->entries) {
+ ifd->count = 0;
+ ifd->size = 0;
+ return;
+ }
+ for (size_t i = 0; i < ifd->count; i++) {
+ AVExifEntry *entry = &ifd->entries[i];
+ exif_free_entry(entry);
+ }
+ av_freep(&ifd->entries);
+ ifd->count = 0;
+ ifd->size = 0;
+}
+
+static size_t exif_get_ifd_size(const AVExifMetadata *ifd)
+{
+ /* 6 == 4 + 2; 2-byte entry-count at the beginning */
+ /* plus 4-byte next-IFD pointer at the end */
+ size_t total_size = IFD_EXTRA_SIZE;
+ for (size_t i = 0; i < ifd->count; i++) {
+ const AVExifEntry *entry = &ifd->entries[i];
+ if (entry->type == AV_TIFF_IFD) {
+ total_size += BASE_TAG_SIZE + exif_get_ifd_size(&entry->value.ifd) + entry->ifd_offset;
+ } else {
+ size_t payload_size = entry->count * exif_sizes[entry->type];
+ total_size += BASE_TAG_SIZE + (payload_size > 4 ? payload_size : 0);
+ }
+ }
+ return total_size;
+}
+
+static int exif_write_ifd(void *logctx, PutByteContext *pb, int le, const AVExifMetadata *ifd)
+{
+ int offset, ret, tell;
+ tell = bytestream2_get_bytes_left_p(pb);
+ tput16(pb, le, ifd->count);
+ offset = tell + IFD_EXTRA_SIZE + BASE_TAG_SIZE * (uint32_t) ifd->count;
+ av_log(logctx, AV_LOG_DEBUG, "writing IFD with %u entries and initial offset %d\n", ifd->count, offset);
+ for (size_t i = 0; i < ifd->count; i++) {
+ const AVExifEntry *entry = &ifd->entries[i];
+ tput16(pb, le, entry->id);
+ if (entry->id == MAKERNOTE_TAG && entry->type == AV_TIFF_IFD) {
+ size_t ifd_size = exif_get_ifd_size(&entry->value.ifd);
+ tput16(pb, le, AV_TIFF_UNDEFINED);
+ tput32(pb, le, ifd_size);
+ } else {
+ tput16(pb, le, entry->type);
+ tput32(pb, le, entry->count);
+ }
+ if (entry->type == AV_TIFF_IFD) {
+ int tell = bytestream2_tell_p(pb);
+ tput32(pb, le, offset);
+ bytestream2_seek_p(pb, offset, SEEK_SET);
+ if (entry->ifd_offset)
+ bytestream2_put_buffer(pb, entry->ifd_lead, entry->ifd_offset);
+ ret = exif_write_ifd(logctx, pb, le, &entry->value.ifd);
+ if (ret < 0)
+ return ret;
+ offset += ret + entry->ifd_offset;
+ bytestream2_seek_p(pb, tell + 4, SEEK_SET);
+ } else {
+ size_t payload_size = entry->count * exif_sizes[entry->type];
+ if (payload_size > 4) {
+ int tell = bytestream2_tell_p(pb);
+ tput32(pb, le, offset);
+ bytestream2_seek_p(pb, offset, SEEK_SET);
+ exif_write_values(pb, le, entry);
+ offset += payload_size;
+ bytestream2_seek_p(pb, tell + 4, SEEK_SET);
+ } else {
+ /* zero uninitialized excess payload values */
+ AV_WN32(pb->buffer, 0);
+ exif_write_values(pb, le, entry);
+ }
}
}
- // return next IDF offset or 0x000000000 or a value < 0 for failure
- return ff_tget_long(gbytes, le);
+ /*
+ * we write 0 if this is the top-level exif IFD
+ * indicating that there are no more IFD pointers
+ */
+ tput32(pb, le, 0);
+ return offset - tell;
+}
+
+int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer, enum AVExifHeaderMode header_mode)
+{
+ AVBufferRef *buf = NULL;
+ size_t size, headsize = 8;
+ PutByteContext pb;
+ int ret, off = 0;
+
+#if AV_HAVE_BIGENDIAN
+ int le = 0;
+#else
+ int le = 1;
+#endif
+
+ if (*buffer)
+ return AVERROR(EINVAL);
+
+ size = exif_get_ifd_size(ifd);
+ switch (header_mode) {
+ case AV_EXIF_EXIF00:
+ off = 6;
+ break;
+ case AV_EXIF_T_OFF:
+ off = 4;
+ break;
+ case AV_EXIF_ASSUME_BE:
+ le = 0;
+ headsize = 0;
+ break;
+ case AV_EXIF_ASSUME_LE:
+ le = 1;
+ headsize = 0;
+ break;
+ }
+ buf = av_buffer_alloc(size + off + headsize);
+ if (!buf)
+ return AVERROR(ENOMEM);
+
+ if (header_mode == AV_EXIF_EXIF00) {
+ AV_WL32(buf->data, MKTAG('E','x','i','f'));
+ AV_WN16(buf->data + 4, 0);
+ } else if (header_mode == AV_EXIF_T_OFF) {
+ AV_WN32(buf->data, 0);
+ }
+
+ bytestream2_init_writer(&pb, buf->data + off, buf->size - off);
+
+ if (header_mode != AV_EXIF_ASSUME_BE && header_mode != AV_EXIF_ASSUME_LE) {
+ /* these constants are be32 in both cases */
+ bytestream2_put_be32(&pb, le ? EXIF_II_LONG : EXIF_MM_LONG);
+ tput32(&pb, le, 8);
+ }
+
+ ret = exif_write_ifd(logctx, &pb, le, ifd);
+ if (ret < 0) {
+ av_buffer_unref(&buf);
+ av_log(logctx, AV_LOG_ERROR, "error writing EXIF data: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ *buffer = buf;
+
+ return 0;
+}
+
+int av_exif_parse_buffer(void *logctx, const uint8_t *buf, size_t size,
+ AVExifMetadata *ifd, enum AVExifHeaderMode header_mode)
+{
+ int ret, le;
+ GetByteContext gbytes;
+ if (size > INT_MAX)
+ return AVERROR(EINVAL);
+ size_t off = 0;
+ switch (header_mode) {
+ case AV_EXIF_EXIF00:
+ if (size < 6)
+ return AVERROR_INVALIDDATA;
+ off = 6;
+ /* fallthrough */
+ case AV_EXIF_T_OFF:
+ if (size < 4)
+ return AVERROR_INVALIDDATA;
+ if (!off)
+ off = AV_RB32(buf) + 4;
+ /* fallthrough */
+ case AV_EXIF_TIFF_HEADER: {
+ int ifd_offset;
+ if (size <= off)
+ return AVERROR_INVALIDDATA;
+ bytestream2_init(&gbytes, buf + off, size - off);
+ // read TIFF header
+ ret = ff_tdecode_header(&gbytes, &le, &ifd_offset);
+ if (ret < 0) {
+ av_log(logctx, AV_LOG_ERROR, "invalid TIFF header in EXIF data: %s\n", av_err2str(ret));
+ return ret;
+ }
+ bytestream2_seek(&gbytes, ifd_offset, SEEK_SET);
+ break;
+ }
+ case AV_EXIF_ASSUME_LE:
+ le = 1;
+ bytestream2_init(&gbytes, buf, size);
+ break;
+ case AV_EXIF_ASSUME_BE:
+ le = 0;
+ bytestream2_init(&gbytes, buf, size);
+ break;
+ default:
+ return AVERROR(EINVAL);
+ }
+
+ /*
+ * parse IFD0 here. If the return value is positive that tells us
+ * there is subimage metadata, but we don't parse that IFD here
+ */
+ ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, ifd);
+ if (ret < 0) {
+ av_exif_free(ifd);
+ av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ return bytestream2_tell(&gbytes);
+}
+
+static int attach_displaymatrix(void *logctx, AVFrame *frame, int orientation)
+{
+ AVFrameSideData *sd;
+ int32_t *matrix;
+ /* invalid orientation */
+ if (orientation < 2 || orientation > 8)
+ return 0;
+ sd = av_frame_new_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9);
+ if (!sd) {
+ av_log(logctx, AV_LOG_ERROR, "Could not allocate frame side data\n");
+ return AVERROR(ENOMEM);
+ }
+ matrix = (int32_t *) sd->data;
+
+ switch (orientation) {
+ case 2:
+ av_display_rotation_set(matrix, 0.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 3:
+ av_display_rotation_set(matrix, 180.0);
+ break;
+ case 4:
+ av_display_rotation_set(matrix, 180.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 5:
+ av_display_rotation_set(matrix, 90.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 6:
+ av_display_rotation_set(matrix, 90.0);
+ break;
+ case 7:
+ av_display_rotation_set(matrix, -90.0);
+ av_display_matrix_flip(matrix, 1, 0);
+ break;
+ case 8:
+ av_display_rotation_set(matrix, -90.0);
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ return 0;
+}
+
+#define COLUMN_SEP(i, c) ((i) ? ((i) % (c) ? ", " : "\n") : "")
+
+static int exif_ifd_to_dict(void *logctx, const char *prefix, const AVExifMetadata *ifd, AVDictionary **metadata)
+{
+ AVBPrint bp;
+ int ret = 0;
+ char *key = NULL;
+ char *value = NULL;
+
+ if (!prefix)
+ prefix = "";
+
+ for (uint16_t i = 0; i < ifd->count; i++) {
+ const AVExifEntry *entry = &ifd->entries[i];
+ const char *name = av_exif_get_tag_name(entry->id);
+ av_bprint_init(&bp, entry->count * 10, AV_BPRINT_SIZE_UNLIMITED);
+ if (*prefix)
+ av_bprintf(&bp, "%s/", prefix);
+ if (name)
+ av_bprintf(&bp, "%s", name);
+ else
+ av_bprintf(&bp, "0x%04X", entry->id);
+ ret = av_bprint_finalize(&bp, &key);
+ if (ret < 0)
+ goto end;
+ av_bprint_init(&bp, entry->count * 10, AV_BPRINT_SIZE_UNLIMITED);
+ switch (entry->type) {
+ case AV_TIFF_IFD:
+ ret = exif_ifd_to_dict(logctx, key, &entry->value.ifd, metadata);
+ if (ret < 0)
+ goto end;
+ break;
+ case AV_TIFF_SHORT:
+ case AV_TIFF_LONG:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%7" PRIu32, COLUMN_SEP(j, 8), (uint32_t)entry->value.uint[j]);
+ break;
+ case AV_TIFF_SSHORT:
+ case AV_TIFF_SLONG:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%7" PRId32, COLUMN_SEP(j, 8), (int32_t)entry->value.sint[j]);
+ break;
+ case AV_TIFF_RATIONAL:
+ case AV_TIFF_SRATIONAL:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%7i:%-7i", COLUMN_SEP(j, 4), entry->value.rat[j].num, entry->value.rat[j].den);
+ break;
+ case AV_TIFF_DOUBLE:
+ case AV_TIFF_FLOAT:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%.15g", COLUMN_SEP(j, 4), entry->value.dbl[j]);
+ break;
+ case AV_TIFF_STRING:
+ av_bprintf(&bp, "%s", entry->value.str);
+ break;
+ case AV_TIFF_UNDEFINED:
+ case AV_TIFF_BYTE:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%3i", COLUMN_SEP(j, 16), entry->value.ubytes[j]);
+ break;
+ case AV_TIFF_SBYTE:
+ for (uint32_t j = 0; j < entry->count; j++)
+ av_bprintf(&bp, "%s%3i", COLUMN_SEP(j, 16), entry->value.sbytes[j]);
+ break;
+ }
+ if (entry->type != AV_TIFF_IFD) {
+ if (!av_bprint_is_complete(&bp)) {
+ av_bprint_finalize(&bp, NULL);
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ ret = av_bprint_finalize(&bp, &value);
+ if (ret < 0)
+ goto end;
+ ret = av_dict_set(metadata, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
+ if (ret < 0)
+ goto end;
+ key = NULL;
+ value = NULL;
+ } else {
+ av_freep(&key);
+ }
+ }
+
+end:
+ av_freep(&key);
+ av_freep(&value);
+ return ret;
+}
+
+int av_exif_ifd_to_dict(void *logctx, const AVExifMetadata *ifd, AVDictionary **metadata)
+{
+ return exif_ifd_to_dict(logctx, "", ifd, metadata);
+}
+
+int ff_exif_decode_ifd(void *logctx, GetByteContext *gb, int le, int depth, AVDictionary **metadata)
+{
+ AVExifMetadata ifd = { 0 };
+ int ret = exif_parse_ifd_list(logctx, gb, le, depth, &ifd);
+ if (ret < 0)
+ return ret;
+
+ ret = av_exif_ifd_to_dict(logctx, &ifd, metadata);
+ av_exif_free(&ifd);
+
+ return ret;
}
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
int le, int depth, AVDictionary **metadata)
{
GetByteContext gb;
-
bytestream2_init(&gb, buf, size);
-
return ff_exif_decode_ifd(logctx, &gb, le, depth, metadata);
}
+
+static int exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd, AVBufferRef *og)
+{
+ const AVExifEntry *orient = NULL;
+ AVFrameSideData *sd;
+ AVExifMetadata *cloned = NULL;
+ AVBufferRef *written = NULL;
+ int ret;
+
+ 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) {
+ orient = entry;
+ break;
+ }
+ }
+
+ if (orient && orient->value.uint[0] > 1) {
+ av_log(logctx, AV_LOG_DEBUG, "found nontrivial EXIF orientation: %" PRIu64 "\n", orient->value.uint[0]);
+ ret = attach_displaymatrix(logctx, frame, orient->value.uint[0]);
+ if (ret < 0) {
+ av_log(logctx, AV_LOG_WARNING, "unable to attach displaymatrix from EXIF\n");
+ } else {
+ const AVExifEntry *cloned_orient;
+ cloned = av_exif_clone_ifd(ifd);
+ if (!cloned) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ // will have the same offset in the clone as in the original
+ cloned_orient = &cloned->entries[orient - ifd->entries];
+ cloned_orient->value.uint[0] = 1;
+ }
+ }
+
+ ret = av_exif_ifd_to_dict(logctx, cloned ? cloned : ifd, &frame->metadata);
+ if (ret < 0)
+ return ret;
+
+ if (cloned || !og) {
+ ret = av_exif_write(logctx, cloned ? cloned : ifd, &written, 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);
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ if (og && written && ret >= 0)
+ av_buffer_unref(&og); // as though we called new_side_data on og;
+ av_exif_free(cloned);
+ av_free(cloned);
+ return ret;
+}
+
+#define EXIF_COPY(fname, srcname) do { \
+ size_t sz; \
+ if (av_size_mult(src->count, sizeof(*(fname)), &sz) < 0) { \
+ ret = AVERROR(ENOMEM); \
+ goto end; \
+ } \
+ (fname) = av_memdup((srcname), sz); \
+ if (!(fname)) { \
+ ret = AVERROR(ENOMEM); \
+ goto end; \
+ } \
+} while (0)
+
+static int exif_clone_entry(AVExifEntry *dst, const AVExifEntry *src)
+{
+ int ret = 0;
+
+ dst->count = src->count;
+ dst->id = src->id;
+ dst->type = src->type;
+
+ dst->ifd_offset = src->ifd_offset;
+ if (src->ifd_lead) {
+ dst->ifd_lead = av_memdup(src->ifd_lead, src->ifd_offset);
+ if (!dst->ifd_lead) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ } else {
+ dst->ifd_lead = NULL;
+ }
+
+ switch(src->type) {
+ case AV_TIFF_IFD: {
+ AVExifMetadata *cloned = av_exif_clone_ifd(&src->value.ifd);
+ if (!cloned) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ dst->value.ifd = *cloned;
+ av_freep(&cloned);
+ break;
+ }
+ case AV_TIFF_SHORT:
+ case AV_TIFF_LONG:
+ EXIF_COPY(dst->value.uint, src->value.uint);
+ break;
+ case AV_TIFF_SLONG:
+ case AV_TIFF_SSHORT:
+ EXIF_COPY(dst->value.sint, src->value.sint);
+ break;
+ case AV_TIFF_RATIONAL:
+ case AV_TIFF_SRATIONAL:
+ EXIF_COPY(dst->value.rat, src->value.rat);
+ break;
+ case AV_TIFF_DOUBLE:
+ case AV_TIFF_FLOAT:
+ EXIF_COPY(dst->value.dbl, src->value.dbl);
+ break;
+ case AV_TIFF_BYTE:
+ case AV_TIFF_UNDEFINED:
+ EXIF_COPY(dst->value.ubytes, src->value.ubytes);
+ break;
+ case AV_TIFF_SBYTE:
+ EXIF_COPY(dst->value.sbytes, src->value.sbytes);
+ break;
+ case AV_TIFF_STRING:
+ EXIF_COPY(dst->value.str, src->value.str);
+ break;
+ }
+
+ return 0;
+
+end:
+ av_freep(&dst->ifd_lead);
+ if (src->type == AV_TIFF_IFD)
+ av_exif_free(&dst->value.ifd);
+ else
+ av_freep(&dst->value.ptr);
+ memset(dst, 0, sizeof(*dst));
+
+ return ret;
+}
+
+int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTiffDataType type,
+ uint32_t count, const uint8_t *ifd_lead, uint32_t ifd_offset, const void *value)
+{
+ void *temp;
+ int ret = 0;
+ AVExifEntry *entry = NULL;
+ AVExifEntry src = { 0 };
+
+ if (!ifd || ifd->entries && !ifd->count || ifd->count && !ifd->entries
+ || ifd_lead && !ifd_offset || !ifd_lead && ifd_offset
+ || !value || ifd->count == 0xFFFFu)
+ return AVERROR(EINVAL);
+
+ for (size_t i = 0; i < ifd->count; i++) {
+ if (ifd->entries[i].id == id) {
+ entry = &ifd->entries[i];
+ break;
+ }
+ }
+
+ if (entry) {
+ exif_free_entry(entry);
+ } else {
+ size_t required_size;
+ ret = av_size_mult(ifd->count + 1, sizeof(*ifd->entries), &required_size);
+ if (ret < 0)
+ return AVERROR(ENOMEM);
+ temp = av_fast_realloc(ifd->entries, &ifd->size, required_size);
+ if (!temp)
+ return AVERROR(ENOMEM);
+ ifd->entries = temp;
+ entry = &ifd->entries[ifd->count++];
+ }
+
+ src.count = count;
+ src.id = id;
+ src.type = type;
+ src.ifd_lead = (uint8_t *) ifd_lead;
+ src.ifd_offset = ifd_offset;
+ if (type == AV_TIFF_IFD)
+ src.value.ifd = * (const AVExifMetadata *) value;
+ else
+ src.value.ptr = (void *) value;
+
+ ret = exif_clone_entry(entry, &src);
+
+ if (ret < 0)
+ ifd->count--;
+
+ return ret;
+}
+
+AVExifMetadata *av_exif_clone_ifd(const AVExifMetadata *ifd)
+{
+ AVExifMetadata *ret = av_mallocz(sizeof(*ret));
+ if (!ret)
+ return NULL;
+
+ ret->count = ifd->count;
+ if (ret->count) {
+ size_t required_size;
+ if (av_size_mult(ret->count, sizeof(*ret->entries), &required_size) < 0)
+ goto fail;
+ ret->entries = av_fast_realloc(NULL, &ret->size, required_size);
+ if (!ret->entries)
+ goto fail;
+ }
+
+ for (size_t i = 0; i < ret->count; i++) {
+ const AVExifEntry *entry = &ifd->entries[i];
+ AVExifEntry *ret_entry = &ret->entries[i];
+ int status = exif_clone_entry(ret_entry, entry);
+ if (status < 0)
+ goto fail;
+ }
+
+ return ret;
+
+fail:
+ av_exif_free(ret);
+ av_free(ret);
+ return NULL;
+}
+
+int ff_exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd)
+{
+ return exif_attach_ifd(logctx, frame, ifd, NULL);
+}
+
+int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum AVExifHeaderMode header_mode)
+{
+ int ret;
+ AVExifMetadata ifd = { 0 };
+
+ ret = av_exif_parse_buffer(logctx, data->data, data->size, &ifd, header_mode);
+ if (ret < 0)
+ goto end;
+
+ ret = exif_attach_ifd(logctx, frame, &ifd, data);
+
+end:
+ av_exif_free(&ifd);
+ return ret;
+}
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index 370d4c6eac..f76ab1e125 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -1,6 +1,7 @@
/*
* EXIF metadata parser
* Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * Copyright (c) 2024-2025 Leo Izen <leo.izen(a)gmail.com>
*
* This file is part of FFmpeg.
*
@@ -23,14 +24,18 @@
* @file
* EXIF metadata parser
* @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * @author Leo Izen <leo.izen(a)gmail.com>
*/
#ifndef AVCODEC_EXIF_H
#define AVCODEC_EXIF_H
+#include <stddef.h>
#include <stdint.h>
+
+#include "libavutil/buffer.h"
#include "libavutil/dict.h"
-#include "bytestream.h"
+#include "libavutil/rational.h"
/** Data type identifiers for TIFF tags */
enum AVTiffDataType {
@@ -49,12 +54,137 @@ enum AVTiffDataType {
AV_TIFF_IFD,
};
-/** Recursively decodes all IFD's and
- * adds included TAGS into the metadata dictionary. */
+enum AVExifHeaderMode {
+ /**
+ * The TIFF header starts with 0x49492a00, or 0x4d4d002a.
+ * This one is used internally by FFmpeg.
+ */
+ AV_EXIF_TIFF_HEADER,
+ /** skip the TIFF header, assume little endian */
+ AV_EXIF_ASSUME_LE,
+ /** skip the TIFF header, assume big endian */
+ AV_EXIF_ASSUME_BE,
+ /** The first four bytes point to the actual start, then it's AV_EXIF_TIFF_HEADER */
+ AV_EXIF_T_OFF,
+ /** The first six bytes contain "Exif\0\0", then it's AV_EXIF_TIFF_HEADER */
+ AV_EXIF_EXIF00,
+};
+
+typedef struct AVExifEntry AVExifEntry;
+
+typedef struct AVExifMetadata {
+ /* array of EXIF metadata entries */
+ AVExifEntry *entries;
+ /* number of entries in this array */
+ unsigned int count;
+ /* size of the buffer, used for av_fast_realloc */
+ unsigned int size;
+} AVExifMetadata;
+
+struct AVExifEntry {
+ uint16_t id;
+ enum AVTiffDataType type;
+ uint32_t count;
+
+ /*
+ * These are for IFD-style MakerNote
+ * entries which occur after a fixed
+ * offset rather than at the start of
+ * the entry. The ifd_lead field contains
+ * the leading bytes which typically
+ * identify the type of MakerNote.
+ */
+ uint32_t ifd_offset;
+ uint8_t *ifd_lead;
+
+ /*
+ * An array of entries of size count
+ * Unless it's an IFD, in which case
+ * it's not an array and count = 1
+ */
+ union {
+ void *ptr;
+ int64_t *sint;
+ uint64_t *uint;
+ double *dbl;
+ char *str;
+ uint8_t *ubytes;
+ int8_t *sbytes;
+ AVRational *rat;
+ AVExifMetadata ifd;
+ } value;
+};
+
+/**
+ * Retrieves the tag name associated with the provided tag ID.
+ * If the tag ID is unknown, NULL is returned.
+ *
+ * For example, av_exif_get_tag_name(0x112) returns "Orientation".
+ */
+const char *av_exif_get_tag_name(uint16_t id);
+
+/**
+ * Retrieves the tag ID associated with the provided tag string name.
+ * If the tag name is unknown, a negative number is returned. Otherwise
+ * it always fits inside a uint16_t integer.
+ *
+ * For example, av_exif_get_tag_id("Orientation") returns 274 (0x0112).
+ */
+int32_t av_exif_get_tag_id(const char *name);
+
+/**
+ * Add an entry to the provided EXIF metadata struct. If one already exists with the provided
+ * ID, it will set the existing one to have the other information provided. Otherwise, it
+ * will allocate a new entry.
+ *
+ * This function reallocates ifd->entries using av_realloc and allocates (using av_malloc)
+ * a new value member of the entry, then copies the contents of value into that buffer.
+ */
+int av_exif_set_entry(void *logctx, AVExifMetadata *ifd, uint16_t id, enum AVTiffDataType type,
+ uint32_t count, const uint8_t *ifd_lead, uint32_t ifd_offset, const void *value);
+
+/**
+ * Decodes the EXIF data provided in the buffer and writes it into the
+ * struct *ifd. If this function succeeds, the IFD is owned by the caller
+ * and must be cleared after use by calling av_exif_free(); If this function
+ * fails and returns a negative value, it will call av_exif_free(ifd) before
+ * returning.
+ */
+int av_exif_parse_buffer(void *logctx, const uint8_t *data, size_t size,
+ AVExifMetadata *ifd, enum AVExifHeaderMode header_mode);
+
+/**
+ * Allocates a buffer using av_malloc of an appropriate size and writes the
+ * EXIF data represented by ifd into that buffer.
+ *
+ * Upon error, *buffer will be NULL. The buffer becomes owned by the caller upon
+ * success. The *buffer argument must be NULL before calling.
+ */
+int av_exif_write(void *logctx, const AVExifMetadata *ifd, AVBufferRef **buffer, enum AVExifHeaderMode header_mode);
+
+/**
+ * Frees all resources associated with the given EXIF metadata struct.
+ * Does not free the pointer passed itself, in case it is stack-allocated.
+ * The pointer passed to this function must be freed by the caller,
+ * if it is heap-allocated. Passing NULL is permitted.
+ */
+void av_exif_free(AVExifMetadata *ifd);
+
+/**
+ * Recursively reads all tags from the IFD and stores them in the
+ * provided metadata dictionary.
+ */
+int av_exif_ifd_to_dict(void *logctx, const AVExifMetadata *ifd, AVDictionary **metadata);
+
+/**
+ * Allocates a duplicate of the provided EXIF metadata struct. The caller owns
+ * the duplicate and must free it with av_exif_free. Returns NULL if the duplication
+ * process failed.
+ */
+AVExifMetadata *av_exif_clone_ifd(const AVExifMetadata *ifd);
+
+/* Used by the AVI demuxer */
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
int le, int depth, AVDictionary **metadata);
-int ff_exif_decode_ifd(void *logctx, GetByteContext *gbytes, int le,
- int depth, AVDictionary **metadata);
-
#endif /* AVCODEC_EXIF_H */
diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
new file mode 100644
index 0000000000..bd0bd22015
--- /dev/null
+++ b/libavcodec/exif_internal.h
@@ -0,0 +1,68 @@
+/*
+ * EXIF metadata parser - internal functions
+ * Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * Copyright (c) 2024-2025 Leo Izen <leo.izen(a)gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * EXIF metadata parser - internal functions
+ * @author Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ * @author Leo Izen <leo.izen(a)gmail.com>
+ */
+
+#ifndef AVCODEC_EXIF_INTERNAL_H
+#define AVCODEC_EXIF_INTERNAL_H
+
+#include "libavutil/buffer.h"
+#include "libavutil/dict.h"
+#include "libavutil/frame.h"
+
+#include "bytestream.h"
+#include "exif.h"
+
+/**
+ * Attach the data buffer to the frame. This is mostly a wrapper for
+ * av_side_data_new_from_buffer, but it checks if the orientation tag is
+ * present in the provided EXIF buffer. If it is, it zeroes it out and
+ * 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 transfered to the frame directly. On failure,
+ * the data buffer is left owned by the caller.
+ */
+int ff_exif_attach_buffer(void *logctx, AVFrame *frame, AVBufferRef *data, enum AVExifHeaderMode header_mode);
+
+/**
+ * Attach an already-parsed EXIF metadata struct to the frame as a side data
+ * buffer. It writes the EXIF IFD into the buffer and attaches the buffer to
+ * the frame.
+ *
+ * If the metadata struct contains an orientation tag, it will be zeroed before
+ * writing, and instead, an AV_FRAME_DATA_DISPLAYMATRIX will be attached in
+ * addition to the AV_FRAME_DATA_EXIF side data.
+ */
+int ff_exif_attach_ifd(void *logctx, AVFrame *frame, const AVExifMetadata *ifd);
+
+/* Used by mjpeg decoder */
+int ff_exif_decode_ifd(void *logctx, GetByteContext *gb,
+ int le, int depth, AVDictionary **metadata);
+
+#endif /* AVCODEC_EXIF_INTERNAL_H */
diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index 87d1d02077..983a30cb59 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -53,7 +53,7 @@
#include "jpeglsdec.h"
#include "profiles.h"
#include "put_bits.h"
-#include "exif.h"
+#include "exif_internal.h"
#include "bytestream.h"
#include "tiff_common.h"
diff --git a/libavcodec/version.h b/libavcodec/version.h
index da2264a097..755c90bbc1 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 12
+#define LIBAVCODEC_VERSION_MINOR 13
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavcodec/webp.c b/libavcodec/webp.c
index 7d77d64524..489cf6f016 100644
--- a/libavcodec/webp.c
+++ b/libavcodec/webp.c
@@ -48,7 +48,7 @@
#include "bytestream.h"
#include "codec_internal.h"
#include "decode.h"
-#include "exif.h"
+#include "exif_internal.h"
#include "get_bits.h"
#include "thread.h"
#include "tiff_common.h"
diff --git a/tests/ref/fate/exif-image-embedded b/tests/ref/fate/exif-image-embedded
index 2b258b228d..574a3af848 100644
--- a/tests/ref/fate/exif-image-embedded
+++ b/tests/ref/fate/exif-image-embedded
@@ -30,7 +30,7 @@ color_space=bt470bg
color_primaries=unknown
color_transfer=unknown
chroma_location=center
-TAG:UserComment=AppleMark
+TAG:ExifIFD/UserComment=AppleMark
[/FRAME]
[FRAME]
diff --git a/tests/ref/fate/exif-image-jpg b/tests/ref/fate/exif-image-jpg
index 9915f80aed..5a8cd10063 100644
--- a/tests/ref/fate/exif-image-jpg
+++ b/tests/ref/fate/exif-image-jpg
@@ -33,171 +33,136 @@ chroma_location=center
TAG:ImageDescription=
TAG:Make=Canon
TAG:Model=Canon PowerShot SX200 IS
-TAG:Orientation= 1
+TAG:Orientation= 1
TAG:XResolution= 180:1
TAG:YResolution= 180:1
-TAG:ResolutionUnit= 2
+TAG:ResolutionUnit= 2
TAG:DateTime=2013:07:18 13:12:03
-TAG:YCbCrPositioning= 2
-TAG:ExposureTime= 1:1250
-TAG:FNumber= 40:10
-TAG:ISOSpeedRatings= 160
-TAG:ExifVersion= 48, 50, 50, 49
-TAG:DateTimeOriginal=2013:07:18 13:12:03
-TAG:DateTimeDigitized=2013:07:18 13:12:03
-TAG:ComponentsConfiguration= 1, 2, 3, 0
-TAG:CompressedBitsPerPixel= 3:1
-TAG:ShutterSpeedValue= 329:32
-TAG:ApertureValue= 128:32
-TAG:ExposureBiasValue= 0:3
-TAG:MaxApertureValue= 113:32
-TAG:MeteringMode= 5
-TAG:Flash= 16
-TAG:FocalLength= 5000:1000
-TAG:MakerNote=
- 25, 0, 1, 0, 3, 0, 48, 0, 0, 0, 28, 4, 0, 0, 2, 0
- 3, 0, 4, 0, 0, 0, 124, 4, 0, 0, 3, 0, 3, 0, 4, 0
- 0, 0, 132, 4, 0, 0, 4, 0, 3, 0, 34, 0, 0, 0, 140, 4
- 0, 0, 0, 0, 3, 0, 6, 0, 0, 0, 208, 4, 0, 0, 6, 0
- 2, 0, 28, 0, 0, 0, 220, 4, 0, 0, 7, 0, 2, 0, 22, 0
- 0, 0, 252, 4, 0, 0, 8, 0, 4, 0, 1, 0, 0, 0, 17, 166
- 15, 0, 9, 0, 2, 0, 32, 0, 0, 0, 20, 5, 0, 0, 13, 0
- 4, 0, 167, 0, 0, 0, 52, 5, 0, 0, 16, 0, 4, 0, 1, 0
- 0, 0, 0, 0, 96, 2, 38, 0, 3, 0, 48, 0, 0, 0, 208, 7
- 0, 0, 19, 0, 3, 0, 4, 0, 0, 0, 48, 8, 0, 0, 24, 0
- 1, 0, 0, 1, 0, 0, 56, 8, 0, 0, 25, 0, 3, 0, 1, 0
- 0, 0, 1, 0, 0, 0, 28, 0, 3, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 29, 0, 3, 0, 16, 0, 0, 0, 56, 9, 0, 0, 30, 0
- 4, 0, 1, 0, 0, 0, 0, 4, 0, 1, 31, 0, 3, 0, 69, 0
- 0, 0, 88, 9, 0, 0, 34, 0, 3, 0, 208, 0, 0, 0, 226, 9
- 0, 0, 35, 0, 4, 0, 2, 0, 0, 0, 130, 11, 0, 0, 39, 0
- 3, 0, 5, 0, 0, 0, 138, 11, 0, 0, 40, 0, 1, 0, 16, 0
- 0, 0, 148, 11, 0, 0, 208, 0, 4, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 45, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0
- 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 255, 255
- 1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0
- 3, 0, 1, 0, 6, 64, 0, 0, 255, 127, 255, 255, 96, 234, 136, 19
-232, 3, 113, 0, 221, 0, 255, 255, 0, 0, 0, 0, 0, 0, 1, 0
- 0, 0, 1, 0, 0, 0, 160, 15, 160, 15, 0, 0, 0, 0, 255, 255
- 0, 0, 255, 127, 255, 127, 0, 0, 0, 0, 255, 255, 90, 0, 2, 0
-136, 19, 250, 0, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0
- 18, 0, 160, 0, 68, 1, 128, 0, 73, 1, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 119, 0, 0, 0, 128, 0, 73, 1, 0, 0, 0, 0
- 23, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 77
- 71, 58, 80, 111, 119, 101, 114, 83, 104, 111, 116, 32, 83, 88, 50, 48
- 48, 32, 73, 83, 32, 74, 80, 69, 71, 0, 0, 0, 0, 0, 70, 105
-114, 109, 119, 97, 114, 101, 32, 86, 101, 114, 115, 105, 111, 110, 32, 49
- 46, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 211, 1, 0, 0, 155, 1
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 1
- 0, 0, 221, 3, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 2, 0, 0, 123, 3
- 0, 0, 165, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0
- 0, 0, 66, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 70, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0
- 0, 0, 204, 3, 0, 0, 138, 3, 0, 0, 138, 3, 0, 0, 128, 1
- 0, 0, 66, 4, 0, 0, 165, 255, 255, 255, 0, 0, 0, 0, 0, 0
- 0, 0, 138, 3, 0, 0, 138, 3, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 35, 0, 0, 0, 120, 0, 0, 0, 120, 0, 0, 0, 102, 255
-255, 255, 208, 0, 0, 0, 114, 255, 255, 255, 208, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 114, 255, 255, 255, 208, 0, 0, 0, 12, 0
- 0, 0, 204, 0, 0, 0, 239, 255, 255, 255, 201, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4
- 0, 0, 0, 5, 0, 0, 8, 0, 0, 0, 239, 255, 255, 255, 201, 0
- 0, 0, 24, 0, 0, 0, 143, 3, 0, 0, 125, 6, 0, 0, 97, 6
- 0, 0, 143, 3, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 0, 0, 91, 1, 0, 0, 113, 4, 0, 0, 204, 3
- 0, 0, 147, 2, 0, 0, 165, 255, 255, 255, 10, 0, 0, 0, 128, 0
- 0, 0, 251, 1, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 74, 2
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 2, 0, 0, 150, 2
- 0, 0, 204, 2, 0, 0, 241, 2, 0, 0, 0, 0, 0, 0, 128, 0
- 0, 0, 0, 0, 0, 0, 8, 162, 255, 255, 70, 2, 0, 0, 69, 2
- 0, 0, 69, 2, 0, 0, 65, 2, 0, 0, 66, 2, 0, 0, 68, 2
- 0, 0, 66, 2, 0, 0, 67, 2, 0, 0, 67, 2, 0, 0, 68, 2
- 0, 0, 18, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 56, 5, 0, 0, 250, 0, 0, 0, 53, 1, 0, 0, 58, 0
- 0, 0, 5, 4, 0, 0, 193, 0, 0, 0, 240, 0, 0, 0, 45, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0
- 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0
- 0, 0, 6, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0
- 0, 0, 0, 244, 255, 255, 133, 0, 0, 0, 102, 2, 0, 0, 243, 1
- 0, 0, 0, 0, 0, 0, 99, 2, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 192, 0, 0, 0, 4, 1, 0, 0, 0, 1, 0, 0, 4, 1
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 13, 0, 0, 0, 237, 157
- 54, 41, 96, 0, 4, 0, 9, 0, 9, 0, 160, 15, 200, 8, 100, 0
-100, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0
- 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0
- 18, 0, 18, 0, 18, 0, 238, 255, 0, 0, 18, 0, 238, 255, 0, 0
- 18, 0, 238, 255, 0, 0, 18, 0, 238, 255, 238, 255, 238, 255, 0, 0
- 0, 0, 0, 0, 18, 0, 18, 0, 18, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 159, 0, 15, 0, 104, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 1, 0, 0, 0
- 2, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 1, 0, 0, 0
- 4, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 160, 1, 0, 0, 0, 0, 16, 0, 8, 0, 1, 0
- 1, 0, 128, 2, 224, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 8, 0, 128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0
-255, 255, 0, 0, 0, 0, 239, 154, 237, 228, 191, 235, 20, 171, 30, 6
- 2, 129, 88, 251, 56, 49, 73, 73, 42, 0, 222, 2, 0, 0
-TAG:UserComment=
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+TAG:YCbCrPositioning= 2
+TAG:ExifIFD/ExposureTime= 1:1250
+TAG:ExifIFD/FNumber= 40:10
+TAG:ExifIFD/ISOSpeedRatings= 160
+TAG:ExifIFD/ExifVersion= 48, 50, 50, 49
+TAG:ExifIFD/DateTimeOriginal=2013:07:18 13:12:03
+TAG:ExifIFD/DateTimeDigitized=2013:07:18 13:12:03
+TAG:ExifIFD/ComponentsConfiguration= 1, 2, 3, 0
+TAG:ExifIFD/CompressedBitsPerPixel= 3:1
+TAG:ExifIFD/ShutterSpeedValue= 329:32
+TAG:ExifIFD/ApertureValue= 128:32
+TAG:ExifIFD/ExposureBiasValue= 0:3
+TAG:ExifIFD/MaxApertureValue= 113:32
+TAG:ExifIFD/MeteringMode= 5
+TAG:ExifIFD/Flash= 16
+TAG:ExifIFD/FocalLength= 5000:1000
+TAG:ExifIFD/MakerNote/GPSLatitudeRef= 96, 2, 0, 3, 0, 0, 0, 4
+ 65535, 1, 9, 0, 0, 0, 0, 0
+ 15, 3, 1, 16390, 0, 32767, 65535, 60000
+ 5000, 1000, 113, 221, 65535, 0, 0, 0
+ 1, 0, 1, 0, 4000, 4000, 0, 0
+ 65535, 0, 32767, 32767, 0, 0, 65535, 90
+TAG:ExifIFD/MakerNote/GPSLatitude= 2, 5000, 250, 187
+TAG:ExifIFD/MakerNote/GPSLongitudeRef= 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSLongitude= 68, 18, 160, 324, 128, 329, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 1, 119, 0, 128, 329, 0
+ 0, 23, 250, 0, 0, 0, 0, 0
+ 0, 0
+TAG:ExifIFD/MakerNote/GPSVersionID= 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSAltitude=IMG:PowerShot SX200 IS JPEG
+TAG:ExifIFD/MakerNote/GPSTimeStamp=Firmware Version 1.00
+TAG:ExifIFD/MakerNote/GPSSatellites=1025553
+TAG:ExifIFD/MakerNote/GPSStatus=
+TAG:ExifIFD/MakerNote/GPSSpeed= 5, 467, 411, 0, 0, 0, 384, 989
+ 56, 0, 0, 0, 0, 576, 891, 4294967205
+ 0, 0, 7, 66, 10, 17, 70, 0
+ 0, 81, 0, 972, 906, 906, 384, 1090
+4294967205, 0, 0, 906, 906, 0, 0, 1
+ 0, 5, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 35, 120, 120, 4294967142, 208
+4294967154, 208, 0, 0, 4294967154, 208, 12, 204
+4294967279, 201, 0, 0, 0, 0, 0, 0
+ 1024, 1024, 1024, 1280, 8, 4294967279, 201, 24
+ 911, 1661, 1633, 911, 100, 0, 0, 1
+ 347, 1137, 972, 659, 4294967205, 10, 128, 507
+ 4, 0, 586, 0, 0, 609, 662, 716
+ 753, 0, 128, 0, 4294943240, 582, 581, 581
+ 577, 578, 580, 578, 579, 579, 580, 1554
+ 0, 0, 0, 1336, 250, 309, 58, 1029
+ 193, 240, 45, 0, 0, 3, 3, 8
+ 0, 0, 0, 0, 0, 0, 107, 4294967046
+ 0, 65535, 0, 4294964224, 133, 614, 499, 0
+ 611, 0, 0, 192, 260, 256, 260, 0
+ 0, 96, 0, 0, 33, 13, 691445229
+TAG:ExifIFD/MakerNote/GPSImgDirectionRef=39845888
+TAG:ExifIFD/MakerNote/0x0026= 96, 4, 9, 9, 4000, 2248, 100, 100
+ 18, 18, 18, 18, 18, 18, 18, 18
+ 18, 18, 18, 18, 18, 18, 18, 18
+ 18, 18, 65518, 0, 18, 65518, 0, 18
+ 65518, 0, 18, 65518, 65518, 65518, 0, 0
+ 0, 18, 18, 18, 1, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDestLatitudeRef= 0, 159, 15, 104
+TAG:ExifIFD/MakerNote/GPSDestBearing= 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDestDistanceRef= 1
+TAG:ExifIFD/MakerNote/GPSAreaInformation= 0
+TAG:ExifIFD/MakerNote/GPSDateStamp= 32, 1, 0, 2, 2, 2, 2, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDifferential=16778240
+TAG:ExifIFD/MakerNote/0x001F= 138, 1, 0, 4, 8, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/0x0022= 416, 0, 0, 16, 8, 1, 1, 640
+ 480, 0, 0, 0, 0, 0, 8, 384
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/0x0023= 8, 0
+TAG:ExifIFD/MakerNote/0x0027= 10, 0, 65535, 0, 0
+TAG:ExifIFD/MakerNote/0x0028=239, 154, 237, 228, 191, 235, 20, 171, 30, 6, 2, 129, 88, 251, 56, 49
+TAG:ExifIFD/MakerNote/0x00D0= 0
+TAG:ExifIFD/MakerNote/0x002D= 0
+TAG:ExifIFD/UserComment= 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -214,22 +179,22 @@ TAG:UserComment=
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0
-TAG:FlashpixVersion= 48, 49, 48, 48
-TAG:ColorSpace= 1
-TAG:PixelXDimension= 4000
-TAG:PixelYDimension= 2248
-TAG:GPSLatitudeRef=R98
-TAG:GPSLatitude= 48, 49, 48, 48
-TAG:0x1001= 4000
-TAG:0x1002= 2248
-TAG:FocalPlaneXResolution=4000000:244
-TAG:FocalPlaneYResolution=2248000:183
-TAG:FocalPlaneResolutionUnit= 2
-TAG:SensingMethod= 2
-TAG:FileSource= 3
-TAG:CustomRendered= 0
-TAG:ExposureMode= 0
-TAG:WhiteBalance= 0
-TAG:DigitalZoomRatio= 4000:4000
-TAG:SceneCaptureType= 0
+TAG:ExifIFD/FlashpixVersion= 48, 49, 48, 48
+TAG:ExifIFD/ColorSpace= 1
+TAG:ExifIFD/PixelXDimension= 4000
+TAG:ExifIFD/PixelYDimension= 2248
+TAG:ExifIFD/InteropIFD/GPSLatitudeRef=R98
+TAG:ExifIFD/InteropIFD/GPSLatitude= 48, 49, 48, 48
+TAG:ExifIFD/InteropIFD/RelatedImageWidth= 4000
+TAG:ExifIFD/InteropIFD/RelatedImageLength= 2248
+TAG:ExifIFD/FocalPlaneXResolution=4000000:244
+TAG:ExifIFD/FocalPlaneYResolution=2248000:183
+TAG:ExifIFD/FocalPlaneResolutionUnit= 2
+TAG:ExifIFD/SensingMethod= 2
+TAG:ExifIFD/FileSource= 3
+TAG:ExifIFD/CustomRendered= 0
+TAG:ExifIFD/ExposureMode= 0
+TAG:ExifIFD/WhiteBalance= 0
+TAG:ExifIFD/DigitalZoomRatio= 4000:4000
+TAG:ExifIFD/SceneCaptureType= 0
[/FRAME]
diff --git a/tests/ref/fate/exif-image-webp b/tests/ref/fate/exif-image-webp
index eaae281a86..3a0e6e2c24 100644
--- a/tests/ref/fate/exif-image-webp
+++ b/tests/ref/fate/exif-image-webp
@@ -33,171 +33,136 @@ chroma_location=unspecified
TAG:ImageDescription=
TAG:Make=Canon
TAG:Model=Canon PowerShot SX200 IS
-TAG:Orientation= 1
+TAG:Orientation= 1
TAG:XResolution= 180:1
TAG:YResolution= 180:1
-TAG:ResolutionUnit= 2
+TAG:ResolutionUnit= 2
TAG:DateTime=2013:07:18 13:12:03
-TAG:YCbCrPositioning= 2
-TAG:ExposureTime= 1:1250
-TAG:FNumber= 40:10
-TAG:ISOSpeedRatings= 160
-TAG:ExifVersion= 48, 50, 50, 49
-TAG:DateTimeOriginal=2013:07:18 13:12:03
-TAG:DateTimeDigitized=2013:07:18 13:12:03
-TAG:ComponentsConfiguration= 1, 2, 3, 0
-TAG:CompressedBitsPerPixel= 3:1
-TAG:ShutterSpeedValue= 329:32
-TAG:ApertureValue= 128:32
-TAG:ExposureBiasValue= 0:3
-TAG:MaxApertureValue= 113:32
-TAG:MeteringMode= 5
-TAG:Flash= 16
-TAG:FocalLength= 5000:1000
-TAG:MakerNote=
- 25, 0, 1, 0, 3, 0, 48, 0, 0, 0, 28, 4, 0, 0, 2, 0
- 3, 0, 4, 0, 0, 0, 124, 4, 0, 0, 3, 0, 3, 0, 4, 0
- 0, 0, 132, 4, 0, 0, 4, 0, 3, 0, 34, 0, 0, 0, 140, 4
- 0, 0, 0, 0, 3, 0, 6, 0, 0, 0, 208, 4, 0, 0, 6, 0
- 2, 0, 28, 0, 0, 0, 220, 4, 0, 0, 7, 0, 2, 0, 22, 0
- 0, 0, 252, 4, 0, 0, 8, 0, 4, 0, 1, 0, 0, 0, 17, 166
- 15, 0, 9, 0, 2, 0, 32, 0, 0, 0, 20, 5, 0, 0, 13, 0
- 4, 0, 167, 0, 0, 0, 52, 5, 0, 0, 16, 0, 4, 0, 1, 0
- 0, 0, 0, 0, 96, 2, 38, 0, 3, 0, 48, 0, 0, 0, 208, 7
- 0, 0, 19, 0, 3, 0, 4, 0, 0, 0, 48, 8, 0, 0, 24, 0
- 1, 0, 0, 1, 0, 0, 56, 8, 0, 0, 25, 0, 3, 0, 1, 0
- 0, 0, 1, 0, 0, 0, 28, 0, 3, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 29, 0, 3, 0, 16, 0, 0, 0, 56, 9, 0, 0, 30, 0
- 4, 0, 1, 0, 0, 0, 0, 4, 0, 1, 31, 0, 3, 0, 69, 0
- 0, 0, 88, 9, 0, 0, 34, 0, 3, 0, 208, 0, 0, 0, 226, 9
- 0, 0, 35, 0, 4, 0, 2, 0, 0, 0, 130, 11, 0, 0, 39, 0
- 3, 0, 5, 0, 0, 0, 138, 11, 0, 0, 40, 0, 1, 0, 16, 0
- 0, 0, 148, 11, 0, 0, 208, 0, 4, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 45, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0
- 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 255, 255
- 1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0
- 3, 0, 1, 0, 6, 64, 0, 0, 255, 127, 255, 255, 96, 234, 136, 19
-232, 3, 113, 0, 221, 0, 255, 255, 0, 0, 0, 0, 0, 0, 1, 0
- 0, 0, 1, 0, 0, 0, 160, 15, 160, 15, 0, 0, 0, 0, 255, 255
- 0, 0, 255, 127, 255, 127, 0, 0, 0, 0, 255, 255, 90, 0, 2, 0
-136, 19, 250, 0, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0
- 18, 0, 160, 0, 68, 1, 128, 0, 73, 1, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 119, 0, 0, 0, 128, 0, 73, 1, 0, 0, 0, 0
- 23, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 77
- 71, 58, 80, 111, 119, 101, 114, 83, 104, 111, 116, 32, 83, 88, 50, 48
- 48, 32, 73, 83, 32, 74, 80, 69, 71, 0, 0, 0, 0, 0, 70, 105
-114, 109, 119, 97, 114, 101, 32, 86, 101, 114, 115, 105, 111, 110, 32, 49
- 46, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 211, 1, 0, 0, 155, 1
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 1
- 0, 0, 221, 3, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 2, 0, 0, 123, 3
- 0, 0, 165, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0
- 0, 0, 66, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 70, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0
- 0, 0, 204, 3, 0, 0, 138, 3, 0, 0, 138, 3, 0, 0, 128, 1
- 0, 0, 66, 4, 0, 0, 165, 255, 255, 255, 0, 0, 0, 0, 0, 0
- 0, 0, 138, 3, 0, 0, 138, 3, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 35, 0, 0, 0, 120, 0, 0, 0, 120, 0, 0, 0, 102, 255
-255, 255, 208, 0, 0, 0, 114, 255, 255, 255, 208, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 114, 255, 255, 255, 208, 0, 0, 0, 12, 0
- 0, 0, 204, 0, 0, 0, 239, 255, 255, 255, 201, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4
- 0, 0, 0, 5, 0, 0, 8, 0, 0, 0, 239, 255, 255, 255, 201, 0
- 0, 0, 24, 0, 0, 0, 143, 3, 0, 0, 125, 6, 0, 0, 97, 6
- 0, 0, 143, 3, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 1, 0, 0, 0, 91, 1, 0, 0, 113, 4, 0, 0, 204, 3
- 0, 0, 147, 2, 0, 0, 165, 255, 255, 255, 10, 0, 0, 0, 128, 0
- 0, 0, 251, 1, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 74, 2
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 2, 0, 0, 150, 2
- 0, 0, 204, 2, 0, 0, 241, 2, 0, 0, 0, 0, 0, 0, 128, 0
- 0, 0, 0, 0, 0, 0, 8, 162, 255, 255, 70, 2, 0, 0, 69, 2
- 0, 0, 69, 2, 0, 0, 65, 2, 0, 0, 66, 2, 0, 0, 68, 2
- 0, 0, 66, 2, 0, 0, 67, 2, 0, 0, 67, 2, 0, 0, 68, 2
- 0, 0, 18, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 56, 5, 0, 0, 250, 0, 0, 0, 53, 1, 0, 0, 58, 0
- 0, 0, 5, 4, 0, 0, 193, 0, 0, 0, 240, 0, 0, 0, 45, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0
- 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0
- 0, 0, 6, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0
- 0, 0, 0, 244, 255, 255, 133, 0, 0, 0, 102, 2, 0, 0, 243, 1
- 0, 0, 0, 0, 0, 0, 99, 2, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 192, 0, 0, 0, 4, 1, 0, 0, 0, 1, 0, 0, 4, 1
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 13, 0, 0, 0, 237, 157
- 54, 41, 96, 0, 4, 0, 9, 0, 9, 0, 160, 15, 200, 8, 100, 0
-100, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0
- 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0, 18, 0
- 18, 0, 18, 0, 18, 0, 238, 255, 0, 0, 18, 0, 238, 255, 0, 0
- 18, 0, 238, 255, 0, 0, 18, 0, 238, 255, 238, 255, 238, 255, 0, 0
- 0, 0, 0, 0, 18, 0, 18, 0, 18, 0, 1, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 159, 0, 15, 0, 104, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 1, 0, 0, 0
- 2, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 1, 0, 0, 0
- 4, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 160, 1, 0, 0, 0, 0, 16, 0, 8, 0, 1, 0
- 1, 0, 128, 2, 224, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 8, 0, 128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0
-255, 255, 0, 0, 0, 0, 239, 154, 237, 228, 191, 235, 20, 171, 30, 6
- 2, 129, 88, 251, 56, 49, 73, 73, 42, 0, 222, 2, 0, 0
-TAG:UserComment=
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+TAG:YCbCrPositioning= 2
+TAG:ExifIFD/ExposureTime= 1:1250
+TAG:ExifIFD/FNumber= 40:10
+TAG:ExifIFD/ISOSpeedRatings= 160
+TAG:ExifIFD/ExifVersion= 48, 50, 50, 49
+TAG:ExifIFD/DateTimeOriginal=2013:07:18 13:12:03
+TAG:ExifIFD/DateTimeDigitized=2013:07:18 13:12:03
+TAG:ExifIFD/ComponentsConfiguration= 1, 2, 3, 0
+TAG:ExifIFD/CompressedBitsPerPixel= 3:1
+TAG:ExifIFD/ShutterSpeedValue= 329:32
+TAG:ExifIFD/ApertureValue= 128:32
+TAG:ExifIFD/ExposureBiasValue= 0:3
+TAG:ExifIFD/MaxApertureValue= 113:32
+TAG:ExifIFD/MeteringMode= 5
+TAG:ExifIFD/Flash= 16
+TAG:ExifIFD/FocalLength= 5000:1000
+TAG:ExifIFD/MakerNote/GPSLatitudeRef= 96, 2, 0, 3, 0, 0, 0, 4
+ 65535, 1, 9, 0, 0, 0, 0, 0
+ 15, 3, 1, 16390, 0, 32767, 65535, 60000
+ 5000, 1000, 113, 221, 65535, 0, 0, 0
+ 1, 0, 1, 0, 4000, 4000, 0, 0
+ 65535, 0, 32767, 32767, 0, 0, 65535, 90
+TAG:ExifIFD/MakerNote/GPSLatitude= 2, 5000, 250, 187
+TAG:ExifIFD/MakerNote/GPSLongitudeRef= 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSLongitude= 68, 18, 160, 324, 128, 329, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 1, 119, 0, 128, 329, 0
+ 0, 23, 250, 0, 0, 0, 0, 0
+ 0, 0
+TAG:ExifIFD/MakerNote/GPSVersionID= 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSAltitude=IMG:PowerShot SX200 IS JPEG
+TAG:ExifIFD/MakerNote/GPSTimeStamp=Firmware Version 1.00
+TAG:ExifIFD/MakerNote/GPSSatellites=1025553
+TAG:ExifIFD/MakerNote/GPSStatus=
+TAG:ExifIFD/MakerNote/GPSSpeed= 5, 467, 411, 0, 0, 0, 384, 989
+ 56, 0, 0, 0, 0, 576, 891, 4294967205
+ 0, 0, 7, 66, 10, 17, 70, 0
+ 0, 81, 0, 972, 906, 906, 384, 1090
+4294967205, 0, 0, 906, 906, 0, 0, 1
+ 0, 5, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 35, 120, 120, 4294967142, 208
+4294967154, 208, 0, 0, 4294967154, 208, 12, 204
+4294967279, 201, 0, 0, 0, 0, 0, 0
+ 1024, 1024, 1024, 1280, 8, 4294967279, 201, 24
+ 911, 1661, 1633, 911, 100, 0, 0, 1
+ 347, 1137, 972, 659, 4294967205, 10, 128, 507
+ 4, 0, 586, 0, 0, 609, 662, 716
+ 753, 0, 128, 0, 4294943240, 582, 581, 581
+ 577, 578, 580, 578, 579, 579, 580, 1554
+ 0, 0, 0, 1336, 250, 309, 58, 1029
+ 193, 240, 45, 0, 0, 3, 3, 8
+ 0, 0, 0, 0, 0, 0, 107, 4294967046
+ 0, 65535, 0, 4294964224, 133, 614, 499, 0
+ 611, 0, 0, 192, 260, 256, 260, 0
+ 0, 96, 0, 0, 33, 13, 691445229
+TAG:ExifIFD/MakerNote/GPSImgDirectionRef=39845888
+TAG:ExifIFD/MakerNote/0x0026= 96, 4, 9, 9, 4000, 2248, 100, 100
+ 18, 18, 18, 18, 18, 18, 18, 18
+ 18, 18, 18, 18, 18, 18, 18, 18
+ 18, 18, 65518, 0, 18, 65518, 0, 18
+ 65518, 0, 18, 65518, 65518, 65518, 0, 0
+ 0, 18, 18, 18, 1, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDestLatitudeRef= 0, 159, 15, 104
+TAG:ExifIFD/MakerNote/GPSDestBearing= 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDestDistanceRef= 1
+TAG:ExifIFD/MakerNote/GPSAreaInformation= 0
+TAG:ExifIFD/MakerNote/GPSDateStamp= 32, 1, 0, 2, 2, 2, 2, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/GPSDifferential=16778240
+TAG:ExifIFD/MakerNote/0x001F= 138, 1, 0, 4, 8, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/0x0022= 416, 0, 0, 16, 8, 1, 1, 640
+ 480, 0, 0, 0, 0, 0, 8, 384
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0
+TAG:ExifIFD/MakerNote/0x0023= 8, 0
+TAG:ExifIFD/MakerNote/0x0027= 10, 0, 65535, 0, 0
+TAG:ExifIFD/MakerNote/0x0028=239, 154, 237, 228, 191, 235, 20, 171, 30, 6, 2, 129, 88, 251, 56, 49
+TAG:ExifIFD/MakerNote/0x00D0= 0
+TAG:ExifIFD/MakerNote/0x002D= 0
+TAG:ExifIFD/UserComment= 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -214,22 +179,22 @@ TAG:UserComment=
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0
-TAG:FlashpixVersion= 48, 49, 48, 48
-TAG:ColorSpace= 1
-TAG:PixelXDimension= 4000
-TAG:PixelYDimension= 2248
-TAG:GPSLatitudeRef=R98
-TAG:GPSLatitude= 48, 49, 48, 48
-TAG:0x1001= 4000
-TAG:0x1002= 2248
-TAG:FocalPlaneXResolution=4000000:244
-TAG:FocalPlaneYResolution=2248000:183
-TAG:FocalPlaneResolutionUnit= 2
-TAG:SensingMethod= 2
-TAG:FileSource= 3
-TAG:CustomRendered= 0
-TAG:ExposureMode= 0
-TAG:WhiteBalance= 0
-TAG:DigitalZoomRatio= 4000:4000
-TAG:SceneCaptureType= 0
+TAG:ExifIFD/FlashpixVersion= 48, 49, 48, 48
+TAG:ExifIFD/ColorSpace= 1
+TAG:ExifIFD/PixelXDimension= 4000
+TAG:ExifIFD/PixelYDimension= 2248
+TAG:ExifIFD/InteropIFD/GPSLatitudeRef=R98
+TAG:ExifIFD/InteropIFD/GPSLatitude= 48, 49, 48, 48
+TAG:ExifIFD/InteropIFD/RelatedImageWidth= 4000
+TAG:ExifIFD/InteropIFD/RelatedImageLength= 2248
+TAG:ExifIFD/FocalPlaneXResolution=4000000:244
+TAG:ExifIFD/FocalPlaneYResolution=2248000:183
+TAG:ExifIFD/FocalPlaneResolutionUnit= 2
+TAG:ExifIFD/SensingMethod= 2
+TAG:ExifIFD/FileSource= 3
+TAG:ExifIFD/CustomRendered= 0
+TAG:ExifIFD/ExposureMode= 0
+TAG:ExifIFD/WhiteBalance= 0
+TAG:ExifIFD/DigitalZoomRatio= 4000:4000
+TAG:ExifIFD/SceneCaptureType= 0
[/FRAME]
commit bb90b262d6d23f1bca3587a48abc15b951cbbf05
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Wed Mar 5 09:20:20 2025 -0500
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:46 2025 -0400
avutil/side_data: add EXIF side data type
This commit adds support for the additional side data type
AV_FRAME_DATA_EXIF, which contains a buffer of an EXIF metadata
payload.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavutil/frame.h b/libavutil/frame.h
index d440cfba2e..d7d98e67e4 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -254,6 +254,12 @@ enum AVFrameSideDataType {
* libavutil/tdrdi.h.
*/
AV_FRAME_DATA_3D_REFERENCE_DISPLAYS,
+
+ /**
+ * 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_FRAME_DATA_EXIF,
};
enum AVActiveFormatDescription {
diff --git a/libavutil/side_data.c b/libavutil/side_data.c
index fa2a2c2a13..8df117478a 100644
--- a/libavutil/side_data.c
+++ b/libavutil/side_data.c
@@ -53,6 +53,7 @@ static const AVSideDataDescriptor sd_props[] = {
[AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT] = { "Ambient viewing environment", AV_SIDE_DATA_PROP_GLOBAL },
[AV_FRAME_DATA_SPHERICAL] = { "Spherical Mapping", AV_SIDE_DATA_PROP_GLOBAL | AV_SIDE_DATA_PROP_SIZE_DEPENDENT },
[AV_FRAME_DATA_ICC_PROFILE] = { "ICC profile", AV_SIDE_DATA_PROP_GLOBAL | AV_SIDE_DATA_PROP_COLOR_DEPENDENT },
+ [AV_FRAME_DATA_EXIF] = { "EXIF metadata", AV_SIDE_DATA_PROP_GLOBAL },
[AV_FRAME_DATA_SEI_UNREGISTERED] = { "H.26[45] User Data Unregistered SEI message", AV_SIDE_DATA_PROP_MULTI },
[AV_FRAME_DATA_VIDEO_HINT] = { "Encoding video hint", AV_SIDE_DATA_PROP_SIZE_DEPENDENT },
[AV_FRAME_DATA_3D_REFERENCE_DISPLAYS] = { "3D Reference Displays Information", AV_SIDE_DATA_PROP_GLOBAL },
diff --git a/libavutil/version.h b/libavutil/version.h
index 5cde712c9f..27b9fe73d8 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 60
-#define LIBAVUTIL_VERSION_MINOR 9
+#define LIBAVUTIL_VERSION_MINOR 10
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
commit a99fff4e2d4058c57599ba0b968862af82bdad5b
Author: Leo Izen <leo.izen(a)gmail.com>
AuthorDate: Tue Mar 11 13:41:19 2025 -0400
Commit: Leo Izen <leo.izen(a)gmail.com>
CommitDate: Tue Aug 19 11:26:40 2025 -0400
avcodec/tiff_common: rename TIFF enum constants
This makes the enum TiffTypes public by moving it to the public header
exif.h and renaming it to AVTiffDataType, as well as adding an AV_
prefix in front of each of the entry names. This allows callers to use
enum AVTiffDataType without pulling in all of tiff_common.h, as that
header is not public.
Signed-off-by: Leo Izen <leo.izen(a)gmail.com>
diff --git a/libavcodec/exif.c b/libavcodec/exif.c
index 959d114d09..f6f8918586 100644
--- a/libavcodec/exif.c
+++ b/libavcodec/exif.c
@@ -180,17 +180,17 @@ static int exif_add_metadata(void *logctx, int count, int type,
"Invalid TIFF tag type 0 found for %s with size %d\n",
name, count);
return 0;
- case TIFF_DOUBLE : return ff_tadd_doubles_metadata(count, name, sep, gb, le, metadata);
- case TIFF_SSHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 1, metadata);
- case TIFF_SHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 0, metadata);
- case TIFF_SBYTE : return ff_tadd_bytes_metadata(count, name, sep, gb, le, 1, metadata);
- case TIFF_BYTE :
- case TIFF_UNDEFINED: return ff_tadd_bytes_metadata(count, name, sep, gb, le, 0, metadata);
- case TIFF_STRING : return ff_tadd_string_metadata(count, name, gb, le, metadata);
- case TIFF_SRATIONAL:
- case TIFF_RATIONAL : return ff_tadd_rational_metadata(count, name, sep, gb, le, metadata);
- case TIFF_SLONG :
- case TIFF_LONG : return ff_tadd_long_metadata(count, name, sep, gb, le, metadata);
+ case AV_TIFF_DOUBLE : return ff_tadd_doubles_metadata(count, name, sep, gb, le, metadata);
+ case AV_TIFF_SSHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 1, metadata);
+ case AV_TIFF_SHORT : return ff_tadd_shorts_metadata(count, name, sep, gb, le, 0, metadata);
+ case AV_TIFF_SBYTE : return ff_tadd_bytes_metadata(count, name, sep, gb, le, 1, metadata);
+ case AV_TIFF_BYTE :
+ case AV_TIFF_UNDEFINED: return ff_tadd_bytes_metadata(count, name, sep, gb, le, 0, metadata);
+ case AV_TIFF_STRING : return ff_tadd_string_metadata(count, name, gb, le, metadata);
+ case AV_TIFF_SRATIONAL:
+ case AV_TIFF_RATIONAL : return ff_tadd_rational_metadata(count, name, sep, gb, le, metadata);
+ case AV_TIFF_SLONG :
+ case AV_TIFF_LONG : return ff_tadd_long_metadata(count, name, sep, gb, le, metadata);
default:
avpriv_request_sample(logctx, "TIFF tag type (%u)", type);
return 0;
@@ -203,7 +203,7 @@ static int exif_decode_tag(void *logctx, GetByteContext *gbytes, int le,
{
int ret, cur_pos;
unsigned id, count;
- enum TiffTypes type;
+ enum AVTiffDataType type;
if (depth > 2) {
return 0;
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
index f70d21391a..370d4c6eac 100644
--- a/libavcodec/exif.h
+++ b/libavcodec/exif.h
@@ -32,6 +32,23 @@
#include "libavutil/dict.h"
#include "bytestream.h"
+/** Data type identifiers for TIFF tags */
+enum AVTiffDataType {
+ AV_TIFF_BYTE = 1,
+ AV_TIFF_STRING,
+ AV_TIFF_SHORT,
+ AV_TIFF_LONG,
+ AV_TIFF_RATIONAL,
+ AV_TIFF_SBYTE,
+ AV_TIFF_UNDEFINED,
+ AV_TIFF_SSHORT,
+ AV_TIFF_SLONG,
+ AV_TIFF_SRATIONAL,
+ AV_TIFF_FLOAT,
+ AV_TIFF_DOUBLE,
+ AV_TIFF_IFD,
+};
+
/** Recursively decodes all IFD's and
* adds included TAGS into the metadata dictionary. */
int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size,
diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index e515845a83..b468598fbb 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -273,9 +273,9 @@ static int add_metadata(int count, int type,
const char *name, const char *sep, TiffContext *s, AVFrame *frame)
{
switch(type) {
- case TIFF_DOUBLE: return ff_tadd_doubles_metadata(count, name, sep, &s->gb, s->le, &frame->metadata);
- case TIFF_SHORT : return ff_tadd_shorts_metadata(count, name, sep, &s->gb, s->le, 0, &frame->metadata);
- case TIFF_STRING: return ff_tadd_string_metadata(count, name, &s->gb, s->le, &frame->metadata);
+ case AV_TIFF_DOUBLE: return ff_tadd_doubles_metadata(count, name, sep, &s->gb, s->le, &frame->metadata);
+ case AV_TIFF_SHORT : return ff_tadd_shorts_metadata(count, name, sep, &s->gb, s->le, 0, &frame->metadata);
+ case AV_TIFF_STRING: return ff_tadd_string_metadata(count, name, &s->gb, s->le, &frame->metadata);
default : return AVERROR_INVALIDDATA;
};
}
@@ -1271,12 +1271,12 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
off = bytestream2_tell(&s->gb);
if (count == 1) {
switch (type) {
- case TIFF_BYTE:
- case TIFF_SHORT:
- case TIFF_LONG:
+ case AV_TIFF_BYTE:
+ case AV_TIFF_SHORT:
+ case AV_TIFF_LONG:
value = ff_tget(&s->gb, type, s->le);
break;
- case TIFF_RATIONAL:
+ case AV_TIFF_RATIONAL:
value = ff_tget_long(&s->gb, s->le);
value2 = ff_tget_long(&s->gb, s->le);
if (!value2) {
@@ -1285,7 +1285,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
break;
- case TIFF_STRING:
+ case AV_TIFF_STRING:
if (count <= 4) {
break;
}
@@ -1320,9 +1320,9 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
s->bpp = value;
else {
switch (type) {
- case TIFF_BYTE:
- case TIFF_SHORT:
- case TIFF_LONG:
+ case AV_TIFF_BYTE:
+ case AV_TIFF_SHORT:
+ case AV_TIFF_LONG:
s->bpp = 0;
if (bytestream2_get_bytes_left(&s->gb) < type_sizes[type] * count)
return AVERROR_INVALIDDATA;
@@ -1389,7 +1389,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
break;
case TIFF_ROWSPERSTRIP:
- if (!value || (type == TIFF_LONG && value == UINT_MAX))
+ if (!value || (type == AV_TIFF_LONG && value == UINT_MAX))
value = s->height;
s->rps = FFMIN(value, s->height);
break;
@@ -1470,7 +1470,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
return AVERROR_INVALIDDATA;
s->black_level[0] = value / (float)value2;
for (int i = 0; i < count && count > 1; i++) {
- if (type == TIFF_RATIONAL) {
+ if (type == AV_TIFF_RATIONAL) {
value = ff_tget_long(&s->gb, s->le);
value2 = ff_tget_long(&s->gb, s->le);
if (!value2) {
@@ -1479,7 +1479,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
s->black_level[i] = value / (float)value2;
- } else if (type == TIFF_SRATIONAL) {
+ } else if (type == AV_TIFF_SRATIONAL) {
int value = ff_tget_long(&s->gb, s->le);
int value2 = ff_tget_long(&s->gb, s->le);
if (!value2) {
@@ -1786,7 +1786,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
break;
case DNG_ANALOG_BALANCE:
- if (type != TIFF_RATIONAL)
+ if (type != AV_TIFF_RATIONAL)
break;
for (int i = 0; i < 3; i++) {
@@ -1801,7 +1801,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
break;
case DNG_AS_SHOT_NEUTRAL:
- if (type != TIFF_RATIONAL)
+ if (type != AV_TIFF_RATIONAL)
break;
for (int i = 0; i < 3; i++) {
@@ -1816,7 +1816,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
}
break;
case DNG_AS_SHOT_WHITE_XY:
- if (type != TIFF_RATIONAL)
+ if (type != AV_TIFF_RATIONAL)
break;
for (int i = 0; i < 2; i++) {
diff --git a/libavcodec/tiff_common.c b/libavcodec/tiff_common.c
index 22ebca814c..ee8061944b 100644
--- a/libavcodec/tiff_common.c
+++ b/libavcodec/tiff_common.c
@@ -64,9 +64,9 @@ double ff_tget_double(GetByteContext *gb, int le)
unsigned ff_tget(GetByteContext *gb, int type, int le)
{
switch (type) {
- case TIFF_BYTE: return bytestream2_get_byte(gb);
- case TIFF_SHORT: return ff_tget_short(gb, le);
- case TIFF_LONG: return ff_tget_long(gb, le);
+ case AV_TIFF_BYTE: return bytestream2_get_byte(gb);
+ case AV_TIFF_SHORT: return ff_tget_short(gb, le);
+ case AV_TIFF_LONG: return ff_tget_long(gb, le);
default: return UINT_MAX;
}
}
@@ -273,7 +273,7 @@ int ff_tread_tag(GetByteContext *gb, int le, unsigned *tag, unsigned *type,
// seek to offset if this is an IFD-tag or
// if count values do not fit into the offset value
- if (ifd_tag || (*count > 4 || !(type_sizes[*type] * (*count) <= 4 || *type == TIFF_STRING))) {
+ if (ifd_tag || (*count > 4 || !(type_sizes[*type] * (*count) <= 4 || *type == AV_TIFF_STRING))) {
bytestream2_seek(gb, ff_tget_long (gb, le), SEEK_SET);
}
diff --git a/libavcodec/tiff_common.h b/libavcodec/tiff_common.h
index e429b8eaeb..11f2e739a4 100644
--- a/libavcodec/tiff_common.h
+++ b/libavcodec/tiff_common.h
@@ -31,23 +31,7 @@
#include <stdint.h>
#include "libavutil/dict.h"
#include "bytestream.h"
-
-/** data type identifiers for TIFF tags */
-enum TiffTypes {
- TIFF_BYTE = 1,
- TIFF_STRING,
- TIFF_SHORT,
- TIFF_LONG,
- TIFF_RATIONAL,
- TIFF_SBYTE,
- TIFF_UNDEFINED,
- TIFF_SSHORT,
- TIFF_SLONG,
- TIFF_SRATIONAL,
- TIFF_FLOAT,
- TIFF_DOUBLE,
- TIFF_IFD
-};
+#include "exif.h"
/** sizes of various TIFF field types (string size = 100)*/
static const uint8_t type_sizes[14] = {
diff --git a/libavcodec/tiffenc.c b/libavcodec/tiffenc.c
index 66facb4174..9a5291c257 100644
--- a/libavcodec/tiffenc.c
+++ b/libavcodec/tiffenc.c
@@ -105,7 +105,7 @@ static inline int check_size(TiffEncoderContext *s, uint64_t need)
* @param type type of values
* @param flip = 0 - normal copy, >0 - flip
*/
-static void tnput(uint8_t **p, int n, const uint8_t *val, enum TiffTypes type,
+static void tnput(uint8_t **p, int n, const uint8_t *val, enum AVTiffDataType type,
int flip)
{
int i;
@@ -126,7 +126,7 @@ static void tnput(uint8_t **p, int n, const uint8_t *val, enum TiffTypes type,
* @param ptr_val pointer to values
*/
static int add_entry(TiffEncoderContext *s, enum TiffTags tag,
- enum TiffTypes type, int count, const void *ptr_val)
+ enum AVTiffDataType type, int count, const void *ptr_val)
{
uint8_t *entries_ptr = s->entries + 12 * s->num_entries;
@@ -150,12 +150,12 @@ static int add_entry(TiffEncoderContext *s, enum TiffTags tag,
}
static int add_entry1(TiffEncoderContext *s,
- enum TiffTags tag, enum TiffTypes type, int val)
+ enum TiffTags tag, enum AVTiffDataType type, int val)
{
uint16_t w = val;
uint32_t dw = val;
return add_entry(s, tag, type, 1,
- type == TIFF_SHORT ? (void *)&w : (void *)&dw);
+ type == AV_TIFF_SHORT ? (void *)&w : (void *)&dw);
}
/**
@@ -453,23 +453,23 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
s->num_entries = 0;
- ADD_ENTRY1(s, TIFF_SUBFILE, TIFF_LONG, 0);
- ADD_ENTRY1(s, TIFF_WIDTH, TIFF_LONG, s->width);
- ADD_ENTRY1(s, TIFF_HEIGHT, TIFF_LONG, s->height);
+ ADD_ENTRY1(s, TIFF_SUBFILE, AV_TIFF_LONG, 0);
+ ADD_ENTRY1(s, TIFF_WIDTH, AV_TIFF_LONG, s->width);
+ ADD_ENTRY1(s, TIFF_HEIGHT, AV_TIFF_LONG, s->height);
if (s->bpp_tab_size)
- ADD_ENTRY(s, TIFF_BPP, TIFF_SHORT, s->bpp_tab_size, bpp_tab);
+ ADD_ENTRY(s, TIFF_BPP, AV_TIFF_SHORT, s->bpp_tab_size, bpp_tab);
- ADD_ENTRY1(s, TIFF_COMPR, TIFF_SHORT, s->compr);
- ADD_ENTRY1(s, TIFF_PHOTOMETRIC, TIFF_SHORT, s->photometric_interpretation);
- ADD_ENTRY(s, TIFF_STRIP_OFFS, TIFF_LONG, strips, s->strip_offsets);
+ ADD_ENTRY1(s, TIFF_COMPR, AV_TIFF_SHORT, s->compr);
+ ADD_ENTRY1(s, TIFF_PHOTOMETRIC, AV_TIFF_SHORT, s->photometric_interpretation);
+ ADD_ENTRY(s, TIFF_STRIP_OFFS, AV_TIFF_LONG, strips, s->strip_offsets);
if (s->bpp_tab_size)
- ADD_ENTRY1(s, TIFF_SAMPLES_PER_PIXEL, TIFF_SHORT, s->bpp_tab_size);
+ ADD_ENTRY1(s, TIFF_SAMPLES_PER_PIXEL, AV_TIFF_SHORT, s->bpp_tab_size);
- ADD_ENTRY1(s, TIFF_ROWSPERSTRIP, TIFF_LONG, s->rps);
- ADD_ENTRY(s, TIFF_STRIP_SIZE, TIFF_LONG, strips, s->strip_sizes);
- ADD_ENTRY(s, TIFF_XRES, TIFF_RATIONAL, 1, res);
+ ADD_ENTRY1(s, TIFF_ROWSPERSTRIP, AV_TIFF_LONG, s->rps);
+ ADD_ENTRY(s, TIFF_STRIP_SIZE, AV_TIFF_LONG, strips, s->strip_sizes);
+ ADD_ENTRY(s, TIFF_XRES, AV_TIFF_RATIONAL, 1, res);
if (avctx->sample_aspect_ratio.num > 0 &&
avctx->sample_aspect_ratio.den > 0) {
AVRational y = av_mul_q(av_make_q(s->dpi, 1),
@@ -477,11 +477,11 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
res[0] = y.num;
res[1] = y.den;
}
- ADD_ENTRY(s, TIFF_YRES, TIFF_RATIONAL, 1, res);
- ADD_ENTRY1(s, TIFF_RES_UNIT, TIFF_SHORT, 2);
+ ADD_ENTRY(s, TIFF_YRES, AV_TIFF_RATIONAL, 1, res);
+ ADD_ENTRY1(s, TIFF_RES_UNIT, AV_TIFF_SHORT, 2);
if (!(avctx->flags & AV_CODEC_FLAG_BITEXACT))
- ADD_ENTRY(s, TIFF_SOFTWARE_NAME, TIFF_STRING,
+ ADD_ENTRY(s, TIFF_SOFTWARE_NAME, AV_TIFF_STRING,
strlen(LIBAVCODEC_IDENT) + 1, LIBAVCODEC_IDENT);
if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
@@ -492,17 +492,17 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
pal[i + 256] = ((rgb >> 8) & 0xff) * 257;
pal[i + 512] = (rgb & 0xff) * 257;
}
- ADD_ENTRY(s, TIFF_PAL, TIFF_SHORT, 256 * 3, pal);
+ ADD_ENTRY(s, TIFF_PAL, AV_TIFF_SHORT, 256 * 3, pal);
}
if (alpha)
- ADD_ENTRY1(s,TIFF_EXTRASAMPLES, TIFF_SHORT, 2);
+ ADD_ENTRY1(s,TIFF_EXTRASAMPLES, AV_TIFF_SHORT, 2);
if (is_yuv) {
/** according to CCIR Recommendation 601.1 */
uint32_t refbw[12] = { 15, 1, 235, 1, 128, 1, 240, 1, 128, 1, 240, 1 };
- ADD_ENTRY(s, TIFF_YCBCR_SUBSAMPLING, TIFF_SHORT, 2, s->subsampling);
+ ADD_ENTRY(s, TIFF_YCBCR_SUBSAMPLING, AV_TIFF_SHORT, 2, s->subsampling);
if (avctx->chroma_sample_location == AVCHROMA_LOC_TOPLEFT)
- ADD_ENTRY1(s, TIFF_YCBCR_POSITIONING, TIFF_SHORT, 2);
- ADD_ENTRY(s, TIFF_REFERENCE_BW, TIFF_RATIONAL, 6, refbw);
+ ADD_ENTRY1(s, TIFF_YCBCR_POSITIONING, AV_TIFF_SHORT, 2);
+ ADD_ENTRY(s, TIFF_REFERENCE_BW, AV_TIFF_RATIONAL, 6, refbw);
}
// write offset to dir
bytestream_put_le32(&offset, ptr - pkt->data);
diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c
index 44f5c20755..a0d5e7fb55 100644
--- a/libavformat/mlvdec.c
+++ b/libavformat/mlvdec.c
@@ -429,7 +429,7 @@ static int read_header(AVFormatContext *avctx)
static void write_tiff_short(PutByteContext *pb, int tag, int value)
{
bytestream2_put_le16(pb, tag);
- bytestream2_put_le16(pb, TIFF_SHORT);
+ bytestream2_put_le16(pb, AV_TIFF_SHORT);
bytestream2_put_le32(pb, 1);
bytestream2_put_le16(pb, value);
bytestream2_put_le16(pb, 0);
@@ -438,7 +438,7 @@ static void write_tiff_short(PutByteContext *pb, int tag, int value)
static void write_tiff_short2(PutByteContext *pb, int tag, int v1, int v2)
{
bytestream2_put_le16(pb, tag);
- bytestream2_put_le16(pb, TIFF_SHORT);
+ bytestream2_put_le16(pb, AV_TIFF_SHORT);
bytestream2_put_le32(pb, 2);
bytestream2_put_le16(pb, v1);
bytestream2_put_le16(pb, v2);
@@ -447,7 +447,7 @@ static void write_tiff_short2(PutByteContext *pb, int tag, int v1, int v2)
static void write_tiff_long(PutByteContext *pb, int tag, int value)
{
bytestream2_put_le16(pb, tag);
- bytestream2_put_le16(pb, TIFF_LONG);
+ bytestream2_put_le16(pb, AV_TIFF_LONG);
bytestream2_put_le32(pb, 1);
bytestream2_put_le32(pb, value);
}
@@ -455,7 +455,7 @@ static void write_tiff_long(PutByteContext *pb, int tag, int value)
static void write_tiff_byte4(PutByteContext *pb, int tag, int v1, int v2, int v3, int v4)
{
bytestream2_put_le16(pb, tag);
- bytestream2_put_le16(pb, TIFF_BYTE);
+ bytestream2_put_le16(pb, AV_TIFF_BYTE);
bytestream2_put_le32(pb, 4);
bytestream2_put_byte(pb, v1);
bytestream2_put_byte(pb, v2);
@@ -506,7 +506,7 @@ static int get_packet_lj92(AVFormatContext *avctx, AVStream *st, AVIOContext *pb
write_tiff_long(pb, DNG_WHITE_LEVEL, mlv->white_level);
bytestream2_put_le16(pb, DNG_COLOR_MATRIX1);
- bytestream2_put_le16(pb, TIFF_SRATIONAL);
+ bytestream2_put_le16(pb, AV_TIFF_SRATIONAL);
bytestream2_put_le32(pb, 9);
bytestream2_put_le32(pb, 0); /* matrixofs */
matrixofs = pb->buffer - 4;
-----------------------------------------------------------------------
Summary of changes:
Changelog | 1 +
doc/APIchanges | 17 +
fftools/ffprobe.c | 2 +
libavcodec/Makefile | 1 +
libavcodec/exif.c | 1351 ++++++++++++++++++++++++++++--
libavcodec/exif.h | 201 ++++-
libavcodec/exif_internal.h | 84 ++
libavcodec/libjxldec.c | 78 +-
libavcodec/libjxlenc.c | 65 +-
libavcodec/mjpegdec.c | 91 +-
libavcodec/mjpegdec.h | 3 +-
libavcodec/pngdec.c | 143 ++++
libavcodec/pngenc.c | 16 +
libavcodec/tiff.c | 48 +-
libavcodec/tiff_common.c | 8 +-
libavcodec/tiff_common.h | 18 +-
libavcodec/tiffenc.c | 46 +-
libavcodec/version.h | 2 +-
libavcodec/version_major.h | 1 +
libavcodec/webp.c | 30 +-
libavformat/avidec.c | 14 +-
libavformat/mlvdec.c | 10 +-
libavutil/frame.h | 6 +
libavutil/side_data.c | 1 +
libavutil/version.h | 2 +-
tests/ref/fate/cover-art-mp3-id3v2-remux | 6 +-
tests/ref/fate/exif-image-embedded | 6 +-
tests/ref/fate/exif-image-jpg | 329 ++++----
tests/ref/fate/exif-image-tiff | 25 +-
tests/ref/fate/exif-image-webp | 329 ++++----
tests/ref/fate/mov-cover-image | 6 +-
31 files changed, 2323 insertions(+), 617 deletions(-)
create mode 100644 libavcodec/exif_internal.h
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. a6b5a382dd hwcontext_vulkan: transfer EXCLUSIVE images to correct queue families
by ffmpeg-git@ffmpeg.org 19 Aug '25
by ffmpeg-git@ffmpeg.org 19 Aug '25
19 Aug '25
The branch, master has been updated
via a6b5a382dd7ecdd27c5d0ebba688e1db409d18fd (commit)
from c05fc27dd33b361eb0105157ab7d3a01c2ffa782 (commit)
- Log -----------------------------------------------------------------
commit a6b5a382dd7ecdd27c5d0ebba688e1db409d18fd
Author: vytskalt <vytskalt(a)protonmail.com>
AuthorDate: Tue Aug 19 17:14:15 2025 +0300
Commit: Lynne <dev(a)lynne.ee>
CommitDate: Tue Aug 19 14:34:17 2025 +0000
hwcontext_vulkan: transfer EXCLUSIVE images to correct queue families
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index e066fa8d18..2e04e63212 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -2464,7 +2464,7 @@ static int prepare_frame(AVHWFramesContext *hwfc, FFVkExecPool *ectx,
VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS];
int nb_img_bar = 0;
- uint32_t dst_qf = VK_QUEUE_FAMILY_IGNORED;
+ uint32_t dst_qf = p->nb_img_qfs > 1 ? VK_QUEUE_FAMILY_IGNORED : p->img_qfs[0];
VkImageLayout new_layout;
VkAccessFlags2 new_access;
VkPipelineStageFlagBits2 src_stage = VK_PIPELINE_STAGE_2_NONE;
@@ -4546,7 +4546,7 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc,
VK_ACCESS_TRANSFER_READ_BIT,
upload ? VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL :
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
- VK_QUEUE_FAMILY_IGNORED);
+ p->nb_img_qfs > 1 ? VK_QUEUE_FAMILY_IGNORED : p->img_qfs[0]);
vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
-----------------------------------------------------------------------
Summary of changes:
libavutil/hwcontext_vulkan.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. c05fc27dd3 aacdec_usac: use RefStruct to track unfinished extension buffers
by ffmpeg-git@ffmpeg.org 19 Aug '25
by ffmpeg-git@ffmpeg.org 19 Aug '25
19 Aug '25
The branch, master has been updated
via c05fc27dd33b361eb0105157ab7d3a01c2ffa782 (commit)
from cdee519d40e61bd65ba5b3fbec00acd50a08d0d9 (commit)
- Log -----------------------------------------------------------------
commit c05fc27dd33b361eb0105157ab7d3a01c2ffa782
Author: Lynne <dev(a)lynne.ee>
AuthorDate: Mon Aug 11 22:26:35 2025 +0900
Commit: michaelni <michael(a)niedermayer.cc>
CommitDate: Tue Aug 19 14:32:21 2025 +0000
aacdec_usac: use RefStruct to track unfinished extension buffers
Extensions in AAC USAC can be stored across multiple frames (mainly to keep CBR compliance).
This means that we need to reallocate a buffer when new data is received, accumulate the bitstream data,
and so on until the end of extension flag is signalled and the extension can be decoded.
This is made more complicated by the way in which the AAC channel layout switching is performed.
After decades of evolution, our AAC decoder evolved to double-buffer its entire configuration.
All changes are buffered, verified, and applied, on a per-frame basis if required, in often
random order.
Since we allocate the extension data on heap, this means that if configuration is applied,
in order to avoid double-freeing, we have to keep track of what we've allocated.
It should be noted that extensions which are spread in multiple frames are generally rare,
so an optimization to introduce av_refstruct_realloc() wouldn't generally be useful across the codebase.
Therefore, a copy is good enough for now.
Thanks to Michael Niedermayer for additional fixing.
Fixes: double free
Fixes: 393523547/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_AAC_LATM_fuzzer-6740617236905984
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c
index 947079bc3d..9b42014ee8 100644
--- a/libavcodec/aac/aacdec.c
+++ b/libavcodec/aac/aacdec.c
@@ -62,6 +62,7 @@
#include "libavutil/opt.h"
#include "libavutil/tx.h"
#include "libavutil/version.h"
+#include "libavutil/refstruct.h"
/*
* supported tools
@@ -421,6 +422,26 @@ static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags)
return layout;
}
+static void copy_oc(OutputConfiguration *dst, OutputConfiguration *src)
+{
+ int i;
+
+ for (i = 0; i < src->usac.nb_elems; i++) {
+ AACUsacElemConfig *src_e = &src->usac.elems[i];
+ AACUsacElemConfig *dst_e = &dst->usac.elems[i];
+ /* dst_e->ext.pl_buf is guaranteed to be set to src_e->ext.pl_buf
+ * upon this function's return */
+ av_refstruct_replace(&dst_e->ext.pl_buf, src_e->ext.pl_buf);
+ }
+
+ /* Unref all additional buffers to close leaks */
+ for (; i < dst->usac.nb_elems; i++)
+ av_refstruct_unref(&dst->usac.elems[i].ext.pl_buf);
+
+ /* Set all other properties */
+ *dst = *src;
+}
+
/**
* Save current output configuration if and only if it has been locked.
*/
@@ -429,7 +450,7 @@ static int push_output_configuration(AACDecContext *ac)
int pushed = 0;
if (ac->oc[1].status == OC_LOCKED || ac->oc[0].status == OC_NONE) {
- ac->oc[0] = ac->oc[1];
+ copy_oc(&ac->oc[0], &ac->oc[1]);
pushed = 1;
}
ac->oc[1].status = OC_NONE;
@@ -443,7 +464,8 @@ static int push_output_configuration(AACDecContext *ac)
static void pop_output_configuration(AACDecContext *ac)
{
if (ac->oc[1].status != OC_LOCKED && ac->oc[0].status != OC_NONE) {
- ac->oc[1] = ac->oc[0];
+ copy_oc(&ac->oc[1], &ac->oc[0]);
+
ac->avctx->ch_layout = ac->oc[1].ch_layout;
ff_aac_output_configure(ac, ac->oc[1].layout_map, ac->oc[1].layout_map_tags,
ac->oc[1].status, 0);
@@ -1110,7 +1132,7 @@ static av_cold int decode_close(AVCodecContext *avctx)
AACUSACConfig *usac = &oc->usac;
for (int j = 0; j < usac->nb_elems; j++) {
AACUsacElemConfig *ec = &usac->elems[j];
- av_freep(&ec->ext.pl_data);
+ av_refstruct_unref(&ec->ext.pl_buf);
}
av_channel_layout_uninit(&ac->oc[i].ch_layout);
diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h
index e5a79a7139..b3763fdccc 100644
--- a/libavcodec/aac/aacdec.h
+++ b/libavcodec/aac/aacdec.h
@@ -344,7 +344,7 @@ typedef struct AACUsacElemConfig {
uint8_t payload_frag;
uint32_t default_len;
uint32_t pl_data_offset;
- uint8_t *pl_data;
+ uint8_t *pl_buf;
} ext;
} AACUsacElemConfig;
@@ -353,7 +353,7 @@ typedef struct AACUSACConfig {
uint16_t core_frame_len;
uint16_t stream_identifier;
- AACUsacElemConfig elems[64];
+ AACUsacElemConfig elems[MAX_ELEM_ID];
int nb_elems;
struct {
diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c
index e03e6e015f..c4b821bbba 100644
--- a/libavcodec/aac/aacdec_usac.c
+++ b/libavcodec/aac/aacdec_usac.c
@@ -24,12 +24,13 @@
#include "aacdec_ac.h"
#include "libavcodec/aacsbr.h"
-
#include "libavcodec/aactab.h"
-#include "libavutil/mem.h"
#include "libavcodec/mpeg4audio.h"
#include "libavcodec/unary.h"
+#include "libavutil/mem.h"
+#include "libavutil/refstruct.h"
+
/* Number of scalefactor bands per complex prediction band, equal to 2. */
#define SFB_PER_PRED_BAND 2
@@ -1574,7 +1575,6 @@ static int parse_audio_preroll(AACDecContext *ac, GetBitContext *gb)
static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
GetBitContext *gb)
{
- uint8_t *tmp;
uint8_t pl_frag_start = 1;
uint8_t pl_frag_end = 1;
uint32_t len;
@@ -1601,18 +1601,26 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
if (pl_frag_start)
e->ext.pl_data_offset = 0;
- /* If an extension starts and ends this packet, we can directly use it */
+ /* If an extension starts and ends this packet, we can directly use it below.
+ * Otherwise, we have to copy it to a buffer and accumulate it. */
if (!(pl_frag_start && pl_frag_end)) {
- tmp = av_realloc(e->ext.pl_data, e->ext.pl_data_offset + len);
- if (!tmp) {
- av_free(e->ext.pl_data);
+ /* Reallocate the data */
+ uint8_t *tmp_buf = av_refstruct_alloc_ext(e->ext.pl_data_offset + len,
+ AV_REFSTRUCT_FLAG_NO_ZEROING,
+ NULL, NULL);
+ if (!tmp_buf)
return AVERROR(ENOMEM);
- }
- e->ext.pl_data = tmp;
+
+ /* Copy the data over only if we had saved data to begin with */
+ if (e->ext.pl_buf)
+ memcpy(tmp_buf, e->ext.pl_buf, e->ext.pl_data_offset);
+
+ av_refstruct_unref(&e->ext.pl_buf);
+ e->ext.pl_buf = tmp_buf;
/* Readout data to a buffer */
for (int i = 0; i < len; i++)
- e->ext.pl_data[e->ext.pl_data_offset + i] = get_bits(gb, 8);
+ e->ext.pl_buf[e->ext.pl_data_offset + i] = get_bits(gb, 8);
}
e->ext.pl_data_offset += len;
@@ -1624,7 +1632,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
GetBitContext *gb2 = gb;
GetBitContext gbc;
if (!(pl_frag_start && pl_frag_end)) {
- ret = init_get_bits8(&gbc, e->ext.pl_data, pl_len);
+ ret = init_get_bits8(&gbc, e->ext.pl_buf, pl_len);
if (ret < 0)
return ret;
@@ -1642,7 +1650,7 @@ static int parse_ext_ele(AACDecContext *ac, AACUsacElemConfig *e,
/* This should never happen */
av_assert0(0);
}
- av_freep(&e->ext.pl_data);
+ av_refstruct_unref(&e->ext.pl_buf);
if (ret < 0)
return ret;
-----------------------------------------------------------------------
Summary of changes:
libavcodec/aac/aacdec.c | 28 +++++++++++++++++++++++++---
libavcodec/aac/aacdec.h | 4 ++--
libavcodec/aac/aacdec_usac.c | 32 ++++++++++++++++++++------------
3 files changed, 47 insertions(+), 17 deletions(-)
hooks/post-receive
--
1
0
[ffmpeg] branch master updated. cdee519d40 avcodec/dxv: Check coded_height, to avoid invalid av_clip()
by ffmpeg-git@ffmpeg.org 19 Aug '25
by ffmpeg-git@ffmpeg.org 19 Aug '25
19 Aug '25
The branch, master has been updated
via cdee519d40e61bd65ba5b3fbec00acd50a08d0d9 (commit)
via 0a5046c09996262d0f8b1802a4b34816f72fff06 (commit)
from 7bfaa6d662f1f5eb000b0fae8288b07440464bff (commit)
- Log -----------------------------------------------------------------
commit cdee519d40e61bd65ba5b3fbec00acd50a08d0d9
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 19 03:12:37 2025 +0200
Commit: James Almer <jamrial(a)gmail.com>
CommitDate: Tue Aug 19 11:25:39 2025 -0300
avcodec/dxv: Check coded_height, to avoid invalid av_clip()
Fixes: assertion failure
Fixes: 438961582/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5850827739955200
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index de01f1bacf..07eee253e7 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -940,6 +940,8 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame,
}
break;
}
+ if (avctx->coded_height / 2 / TEXTURE_BLOCK_H < 1)
+ return AVERROR_INVALIDDATA;
texdsp_ctx.slice_count = av_clip(avctx->thread_count, 1,
avctx->coded_height / TEXTURE_BLOCK_H);
commit 0a5046c09996262d0f8b1802a4b34816f72fff06
Author: Michael Niedermayer <michael(a)niedermayer.cc>
AuthorDate: Tue Aug 19 03:09:14 2025 +0200
Commit: James Almer <jamrial(a)gmail.com>
CommitDate: Tue Aug 19 11:25:39 2025 -0300
avcode: Use av_fast_realloc() in ff_lzf_uncompress()
Fixes: 438961582/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_DXV_DEC_fuzzer-5850827739955200
Fixes: mixed up realloc() functions
Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg
Signed-off-by: Michael Niedermayer <michael(a)niedermayer.cc>
Co-Authored-by: James Almer <jamrial(a)gmail.com>
Signed-off-by: James Almer <jamrial(a)gmail.com>
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
index dd82e450b1..de01f1bacf 100644
--- a/libavcodec/dxv.c
+++ b/libavcodec/dxv.c
@@ -42,7 +42,7 @@ typedef struct DXVContext {
uint8_t *ctex_data; // Compressed chroma texture
unsigned ctex_data_size;
- int64_t tex_size; // Texture size
+ size_t tex_size; // Texture size
int64_t ctex_size; // Chroma texture size
uint8_t *op_data[4]; // Opcodes
@@ -828,7 +828,7 @@ static int dxv_decompress_dxt5(AVCodecContext *avctx)
static int dxv_decompress_lzf(AVCodecContext *avctx)
{
DXVContext *ctx = avctx->priv_data;
- return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size);
+ return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size, &ctx->tex_data_size);
}
static int dxv_decompress_raw(AVCodecContext *avctx)
diff --git a/libavcodec/lzf.c b/libavcodec/lzf.c
index 94b369dd59..8f223b1f42 100644
--- a/libavcodec/lzf.c
+++ b/libavcodec/lzf.c
@@ -37,7 +37,22 @@
#define LZF_LITERAL_MAX (1 << 5)
#define LZF_LONG_BACKREF 7 + 2
-int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
+
+static inline int lzf_realloc(uint8_t **buf, size_t *size, int addition, unsigned *allocated_size)
+{
+ void *ptr = av_fast_realloc(*buf, allocated_size, *size + addition);
+
+ if (!ptr) {
+ av_freep(buf); //probably not needed
+ return AVERROR(ENOMEM);
+ }
+ *buf = ptr;
+ *size += addition;
+
+ return 0;
+}
+
+int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, size_t *size, unsigned *allocated_size)
{
int ret = 0;
uint8_t *p = *buf;
@@ -49,8 +64,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
if (s < LZF_LITERAL_MAX) {
s++;
if (s > *size - len) {
- *size += s + *size /2;
- ret = av_reallocp(buf, *size);
+ ret = lzf_realloc(buf, size, s, allocated_size);
if (ret < 0)
return ret;
p = *buf + len;
@@ -75,8 +89,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size)
return AVERROR_INVALIDDATA;
if (l > *size - len) {
- *size += l + *size / 2;
- ret = av_reallocp(buf, *size);
+ ret = lzf_realloc(buf, size, l, allocated_size);
if (ret < 0)
return ret;
p = *buf + len;
diff --git a/libavcodec/lzf.h b/libavcodec/lzf.h
index 0ad73d9f79..e61ebff727 100644
--- a/libavcodec/lzf.h
+++ b/libavcodec/lzf.h
@@ -24,6 +24,6 @@
#include "bytestream.h"
-int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size);
+int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, size_t *size, unsigned *allocated_size);
#endif /* AVCODEC_LZF_H */
diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c
index 246a3e0174..d99de1810e 100644
--- a/libavcodec/notchlc.c
+++ b/libavcodec/notchlc.c
@@ -40,7 +40,8 @@ typedef struct NotchLCContext {
unsigned uncompressed_size;
uint8_t *lzf_buffer;
- int64_t lzf_size;
+ size_t lzf_size;
+ unsigned lzf_alloc_size;
unsigned texture_size_x;
unsigned texture_size_y;
@@ -490,7 +491,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p,
return AVERROR_PATCHWELCOME;
if (s->format == 0) {
- ret = ff_lzf_uncompress(gb, &s->lzf_buffer, &s->lzf_size);
+ ret = ff_lzf_uncompress(gb, &s->lzf_buffer, &s->lzf_size, &s->lzf_alloc_size);
if (ret < 0)
return ret;
-----------------------------------------------------------------------
Summary of changes:
libavcodec/dxv.c | 6 ++++--
libavcodec/lzf.c | 23 ++++++++++++++++++-----
libavcodec/lzf.h | 2 +-
libavcodec/notchlc.c | 5 +++--
4 files changed, 26 insertions(+), 10 deletions(-)
hooks/post-receive
--
1
0