[FFmpeg-devel] [PATCH v2 1/3] various: change EXIF metadata into AVFrameSideData

Anton Khirnov anton at khirnov.net
Tue Oct 1 14:35:02 EEST 2024


Quoting Leo Izen (2024-09-16 16:43:42)
> This patch centralizes much of the EXIF parsing and handling code for
> libavcodec, and delegates its own AVFrameSideData type to containing
> the buffer that holds EXIF metadata. This patch also adds exposes the
> exif parsing routing so it can be called by ffprobe, and updates the
> corresponding FATE tests to read the keys from the side data instead of
> from the main frame metadata.

Does that mean you're no longer exporting frame metadata? That would
break existing callers that are reading it.

> This commit also deprecates an avpriv_ function in exif.h, exposing the
> parsing functionality as a public API in the exported libavcodec/exif.h
> header file.
> 
> Signed-off-by: Leo Izen <leo.izen at gmail.com>
> ---
>  fftools/ffprobe.c                  |  27 +-
>  libavcodec/Makefile                |   1 +
>  libavcodec/exif.c                  | 394 +++++++++++++++++++++++++++--
>  libavcodec/exif.h                  |  32 ++-
>  libavcodec/exif_internal.h         |  55 ++++
>  libavcodec/mjpegdec.c              |  91 +------
>  libavcodec/mjpegdec.h              |   2 +-
>  libavcodec/tiff.c                  |  19 +-
>  libavcodec/tiff.h                  |   1 +
>  libavcodec/version.h               |   2 +-
>  libavcodec/webp.c                  |  38 +--
>  libavformat/avidec.c               |   4 +-
>  libavutil/frame.c                  |   2 +
>  libavutil/frame.h                  |   6 +
>  tests/ref/fate/exif-image-embedded |   5 +-
>  tests/ref/fate/exif-image-jpg      |  91 +++----
>  tests/ref/fate/exif-image-webp     |  91 +++----
>  17 files changed, 631 insertions(+), 230 deletions(-)
>  create mode 100644 libavcodec/exif_internal.h

As James said, it's a bit too many changes at once. I'd split it into:
* add new side data type
* add lavc public API
* codec changes
* lavf changes
* ffprobe changes

> diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h
> new file mode 100644
> index 0000000000..2c788856d6
> --- /dev/null
> +++ b/libavcodec/exif_internal.h
> +/**
> + * Attach an AVBufferRef containing EXIF metadata to a frame.
> + * This function allocates the necessary AVFrameSideData and attaches it,
> + * and also attaches any necessary other sidedata that can be read from the,
> + * EXIF metadata, such as a display matrix.
> + */
> +int ff_exif_attach(void *logctx, AVFrame *frame, AVBufferRef **data);

Tiff and webp can leak data if this function fails. You should probably
declare that it always takes ownership of data and unref it on failure.

> +
> +/**
> + * Collects an IFD into a buffer. **buffer points to an AVBufferRef *, which will
> + * be allocated by this function. The caller needs to unref the buffer when it is
> + * done with it. This function also writes the EXIF/TIFF header into the buffer
> + * based on the endianness, so encoders can pass the buffer as-is. This function can be
> + * used by TIFF or other codecs that have non-zero IFD offsets on their EXIF metadata.
> + */
> +int ff_exif_collect_ifd(void *logctx, GetByteContext *gb, int le, AVBufferRef **buffer);
> +
> +#endif /* AVCODEC_EXIF_INTERNAL_H */
> diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
> index 86ec58713c..9ed71976df 100644
> --- a/libavcodec/mjpegdec.c
> +++ b/libavcodec/mjpegdec.c
> @@ -43,6 +43,7 @@
>  #include "codec_internal.h"
>  #include "copy_block.h"
>  #include "decode.h"
> +#include "exif_internal.h"
>  #include "hwaccel_internal.h"
>  #include "hwconfig.h"
>  #include "idctdsp.h"
> @@ -2039,8 +2040,6 @@ 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;
>          const uint8_t *aligned;
>  
>          skip_bits(&s->gb, 16); // skip padding
> @@ -2048,27 +2047,12 @@ 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");
> -            }
> -        }
> -
> -        bytes_read = bytestream2_tell(&gbytes);
> -        skip_bits(&s->gb, bytes_read << 3);
> -        len -= bytes_read;
> -
> +        s->exif_buffer = av_buffer_alloc(len);

Is it guaranteed s->exif_buffer is NULL at this point, so you're not
leaking anything?

> diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
> index 37b56e9757..45c3ae8c97 100644
> --- a/libavcodec/tiff.c
> +++ b/libavcodec/tiff.c
> @@ -46,6 +46,7 @@
>  #include "bytestream.h"
>  #include "codec_internal.h"
>  #include "decode.h"
> +#include "exif_internal.h"
>  #include "faxcompr.h"
>  #include "lzw.h"
>  #include "tiff.h"
> @@ -1268,7 +1269,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
>          s->last_tag = tag;
>  
>      off = bytestream2_tell(&s->gb);
> -    if (count == 1) {
> +    if (count == 1 && tag != TIFF_EXIFTAG) {
>          switch (type) {
>          case TIFF_BYTE:
>          case TIFF_SHORT:
> @@ -1770,6 +1771,22 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
>      case TIFF_SOFTWARE_NAME:
>          ADD_METADATA(count, "software", NULL);
>          break;
> +    case TIFF_EXIFTAG: {
> +        AVBufferRef *exif = NULL;
> +        int next;
> +        gb_temp = s->gb;
> +        next = ff_exif_collect_ifd(s->avctx, &gb_temp, s->le, &exif);
> +        if (next < 0)
> +            av_log(s->avctx, AV_LOG_ERROR, "Error parsing TIFF exif tags: %d\n", next);
> +        else if (next)
> +            bytestream2_seek(&s->gb, next, SEEK_SET);
> +        if (exif) {
> +            ret = ff_exif_attach(s->avctx, frame, &exif);
> +            if (ret < 0)
> +                return ret;
> +        }
> +        break;
> +    }

Is this adding support for something that was not parsed before? Should
probably also be a separate commit.

-- 
Anton Khirnov


More information about the ffmpeg-devel mailing list