From patchwork Tue Feb 13 21:24:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 46240 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c493:b0:19e:cdac:8cce with SMTP id eo19csp1382pzb; Tue, 13 Feb 2024 13:25:29 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCX4J7AOgRE3h0wJO0ByJrzEfSfk0yJAHlD2/cqPGLyWIdLLgFxbmdXf9Ru2kPYVcybpp3m/pXlzeJxHq+eaEJTg/XgLqApOlkkGHQ== X-Google-Smtp-Source: AGHT+IEutymxQZeASwI66jRHBLyBdydbXWgmBdcasGVzdTEhrIWPhKE1AIa0I47aHaVoHrBtRrIr X-Received: by 2002:a17:906:3794:b0:a3c:b23a:9491 with SMTP id n20-20020a170906379400b00a3cb23a9491mr302365ejc.19.1707859528483; Tue, 13 Feb 2024 13:25:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1707859528; cv=none; d=google.com; s=arc-20160816; b=OHaIOANc2n0c8dajjAYPqf/L8TUSZ8MfcIIk5ZKaXBEd+yCGhchzzyFf2mxJ54fDL+ /J/nfLolEP4KLXuMAT0/Hoa9JPzjRhmSi6f6otMhnpAEG+vVArpE4CMP3DB90gt/cdGk dPuEVqiiVRLGTuzm3MQxrVtGd5hbZMIO5AYA1bqTld4mcU8HN35MYhhGDCUO+UGG5fhm oLuZHJN6REfpcziXc7bRykPRyTKtnQLqdc8QzCL4loUBHwl6cGxTZ3FtdoogpltfwYoG qInehexIzGS9ZR/pHB1EklD7WIRdO/fe3SLlir6pI57NEN0Xw/4HouxotVa6xqhGWOHJ FLSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=3+n+glsQP3yurDeEbVI57UupmmWmqfvxd+nC7v4xrZA=; fh=eWfG1e7+Ms21f9VrJYPhUwiRnVeTiUCYxpi+XKdn454=; b=scimCxEdoBdz9sAnGI/ZX4ehRirSJmcKQvE+c00c0qUyUHOTWKpwtQ6IR2nWRv5gf1 KNj2Nse0mPvUYWP/Nr1v//B7mqBb+p8a1cKwT7ZBUVHVcpqZ2GKvIlQhIeCL7O38RoaY tmZ7yaNVOj0hPYNe5T+KXQWYPk8hi7P6doN6zPtQQH5aO2h2BSeWwe23reO8eQN91MhD uM/b4dnWBDTNDSuL/MlR/PXxOcbXr7EIS+eb8QSe9dA5gH/q+N+Qx3hEV47NnxhLULSK kXLXRz7qQRIg9BL2xQBQtL+PxBBma2Qj0Yf51Z4KtETiPDeJ1PZkKq7s8p7QCqTefMcJ v58g==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=dM4+jfk1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com X-Forwarded-Encrypted: i=1; AJvYcCXhl9xShKjTfHiCEEuP18QYCUXzOka1KOLeYT1GFYPcYf7sKufRmb9uMuaORH/ar+ZZvcW1Y1h4crxs/Cz0XRavbzpAjzA2lw0N23F3nFxAsbk911tojVRbMJ0uyxLhmvs4JXQa/2r/ZsrTsDj58sPRgatTxAGqifL7hKGzjLk6/REZOFVzjg3qGt6Lm+Bbl2+eKymO8YJ70WNYqN7t2FKjngZNsg0D1jjYbSGyFq/f3cAxWRz29u+CF0oyex+IrLQi08RrRdxas2iIZjuZ73E+Ssh4xjr9Fj6j6nMtR9plUHKIwpZE93JDcj/UIubbPfc7WfQvLkeF0VSFbj5sI+ZYoIjPsrN0Yvoh66OeyKMQ3cDxiis2KwRC0Vl0Pb+4S6CUj1IrgX9UCI5D4PVF9eoDLDm+BFSgz4zT0zoYJUUpzfDUxRYDs0+a7rCR2vkQecsVuLOGxnCuJycAEVPF93K7xdcRo5VCpNi5FZZgEulJw4VvT93wVGBQMuBxt18348BCXTMNYvR2u3L9OspyV3F4CEh+31EtGxLyuUcgZzekLGrvdnJxsetRmkkidtM0JWJsMlIeBvIbGDyMNzPIq4x2G2MVAcKDsiSrkpyQTTwVgNrDEy33ISMmiqzN5Xcc3M1tpGZnamY83D7D2sUYLO0F7jnJRKoxBFH+/9XETU6uTUP9f8/9Z1sejzfYirnm2oohg+3/Ps3byVaxfht0YD9qc72rY/6AkLkUZNfxg4FxAUKRJFxDZsaRY7lYX9H3fuLdinyJnWQvl2/buWa7R+f5QmtFq3ThGwhd85jqPL1fdO2lMAzB6IbG3qgkDSyjkDPP70Rscnrjn+J65iFrA1db5ncbX5tAmP+NKm2J6HOBhDwcPS4oBV5Q2liCZNFbMPt2XxE3+RzVbDcMCpjHEyK4LLBb9p5Oe8KHnL7B83tsnl5L+OzBnt+87//VsllS6KHr9l AIsas7Y8Wc/FrDpZGI728otOcoVlefF1Z7okCdIHfgq0rsnZKswA9qJ/WGdztRmVzRnQjDa1WgRlElZHLhWO7u0k/pFI1zT+FfYB87SAjWIrhppyP6KMVy77si3vr1QUMQLpefRzQtYgZvGckh4xNsPzZVIS0MJigRjCDCOLMgWN2yDYMkysSNsS4e81f7hTnetJs0ORlqAgqtwffs8G5MRM7ZJTpc+9KHqtWrlN/LPWzHKt0QBxLa7Qccz4qqvgNpJF8MudJp2p4SQgTkGT+eNYNY8Tu1L7ZwzHLw0XXqBBlD8aqXJTKmt/KZyLkhGMblh3sy9hm3H0yUqx87el3+fjgTfPbhGfZAeHGglo7b9/1mnoAsuT9wDLZR9KTkrf/fDeqsU0li5iohYSgJcxya+ImiVgRb7tUnIv8wQjGwYYSk7OhA558+aLbWOpGBphYK+ykOJJM+HwHZBXtwg3t+e0wJ1A3ORZ98Xp+IXlAWw9f2uutcq6ve2cEnFgTi3WR3dGcRsCwZhVWfaSC1+ckBDrfN2TetktpqJthjI60cmISMXl5D9y7edHuAwnwskHWuEDeU/bjwvBgZPjjyf6iH2dw/NMMpogQuV7mgwWZ6icuRsSKEOac8m8C6PkZ0 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d8-20020a170906344800b00a3d157d74fcsi624188ejb.77.2024.02.13.13.25.22; Tue, 13 Feb 2024 13:25:28 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=dM4+jfk1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3949268C15D; Tue, 13 Feb 2024 23:25:09 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f51.google.com (mail-qv1-f51.google.com [209.85.219.51]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4BA3D68C4E1 for ; Tue, 13 Feb 2024 23:25:01 +0200 (EET) Received: by mail-qv1-f51.google.com with SMTP id 6a1803df08f44-68155fca099so18536336d6.1 for ; Tue, 13 Feb 2024 13:25:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707859500; x=1708464300; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S3O2DaDomRX3r58Zbab2191/RCYtmG2nOxbdnM8bBHo=; b=dM4+jfk10cj+O4PCNpY6XCJO/XioDEOpuQVUTMmtz3qUPXCnhFsYi8O2aIpp8iMcCt r1Pe7n4iKbHV9hpkcV7iKLjkvpfIUHTTNQl1g1k0/41V1NdeyqWhhVlGsUmpLyM8gz7d dibSavU5qlrgVYe03dH8UecubZLNw88BAJ76BUeqrBwTL7LXzaXwKiINSYIH+Ofvgd2j QsjO2kq5CL+Z18iBSGt+OKLFgGu0UqThHQurExOvxNUA0QG6ZZxXfxdQHo9NlchCzOeq QSv+pLUQ+iSMZmfabjq74T2aUhGBtUUc24HWeLX+uwPvMN3s2rpcjfiHTHT3c00bb9P2 itqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707859500; x=1708464300; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S3O2DaDomRX3r58Zbab2191/RCYtmG2nOxbdnM8bBHo=; b=apDkkvtYuy6+xJcpFxo+SODZ4MLTsULybLyoODp6hhc1TZTwSJSbfN6/kFbqj7JMzT gJNdhe2MEZ/RcEr1FqbhDJMWvvJi5Oik5YUJK6t7YqiS09MRlHocv9jUzsULO6d8D3nA sXqZFCjFmv1gyjmeu5zr/dQWqF4SBVAgsYzZNoMbiCSNrmJzZ9En27nPtw55PT9E09jC VUfKlPkjC++AZCQZLRlsmVcm+yrFy+ymm0pHLCcMcsfoS6cGLl2we6/8A/8syn3pdeGa WIJf6z7DpGDBl9HIFhExODDDlR1qUOIJTpV/HJvvDQxuaX9Mc1SRHY55dnI7SX7DwDHh ItNw== X-Gm-Message-State: AOJu0YxuotvmnY3MFPjNevve/jlpO8TUAFzI4yRHxI0g4rhJSHWv2fiY BNeB1FkygPbibuOoQ4148OtkJy4wy4cca2ZBp5sPrOpdVV5u8217/KSDuXLe X-Received: by 2002:a05:620a:27c3:b0:785:caa1:30ec with SMTP id i3-20020a05620a27c300b00785caa130ecmr817596qkp.2.1707859499490; Tue, 13 Feb 2024 13:24:59 -0800 (PST) Received: from gauss.local (c-68-56-149-176.hsd1.mi.comcast.net. [68.56.149.176]) by smtp.gmail.com with ESMTPSA id g5-20020a37e205000000b007861bea2972sm1523016qki.6.2024.02.13.13.24.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Feb 2024 13:24:59 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 13 Feb 2024 16:24:54 -0500 Message-ID: <20240213212456.167386-2-leo.izen@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240213212456.167386-1-leo.izen@gmail.com> References: <20240213212456.167386-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/3] various: change EXIF metadata into AVFrameSideData X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: ZF0fr5hg2BcR 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. This commit also removes an avpriv_ function in exif.h, exposing the parsing functionality as a public API in the exported libavcodec/exif.h header file. As such, this commit requires an ABI break and can only be applied during a major version bump. Signed-off-by: Leo Izen --- fftools/ffprobe.c | 27 ++- libavcodec/Makefile | 1 + libavcodec/exif.c | 267 +++++++++++++++++++++++++++-- libavcodec/exif.h | 21 ++- libavcodec/exif_internal.h | 41 +++++ libavcodec/mjpegdec.c | 96 ++--------- libavcodec/mjpegdec.h | 2 +- libavcodec/tiff.c | 19 +- libavcodec/tiff.h | 1 + libavcodec/webp.c | 38 ++-- libavformat/avidec.c | 4 +- libavutil/frame.c | 1 + 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 +++++----- 16 files changed, 482 insertions(+), 229 deletions(-) create mode 100644 libavcodec/exif_internal.h diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index aa1153e709..7c3cd4a0ee 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -32,6 +32,7 @@ #include "libavformat/avformat.h" #include "libavformat/version.h" #include "libavcodec/avcodec.h" +#include "libavcodec/exif.h" #include "libavcodec/version.h" #include "libavutil/ambient_viewing_environment.h" #include "libavutil/avassert.h" @@ -1982,19 +1983,30 @@ static void writer_register_all(void) memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \ } -static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id) +static inline int show_dict(WriterContext *w, const AVDictionary *tags) { const AVDictionaryEntry *tag = NULL; int ret = 0; - if (!tags) return 0; - writer_print_section_header(w, NULL, section_id); - while ((tag = av_dict_iterate(tags, tag))) { - if ((ret = print_str_validate(tag->key, tag->value)) < 0) + ret = print_str_validate(tag->key, tag->value); + if (ret < 0) break; } + return ret; +} + +static inline int show_tags(WriterContext *w, const AVDictionary *tags, int section_id) +{ + int ret; + + if (!tags) + return 0; + writer_print_section_header(w, NULL, section_id); + + ret = show_dict(w, tags); + writer_print_section_footer(w); return ret; @@ -2700,6 +2712,11 @@ static void print_frame_side_data(WriterContext *w, print_dynamic_hdr_vivid(w, metadata); } else if (sd->type == AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT) { print_ambient_viewing_environment(w, (const AVAmbientViewingEnvironment *)sd->data); + } else if (sd->type == AV_FRAME_DATA_EXIF) { + AVDictionary *dict = NULL; + int ret = av_exif_parse_buffer(NULL, sd->data, sd->size, &dict, AV_EXIF_PARSE_TIFF_HEADER); + if (ret >= 0) + show_dict(w, dict); } writer_print_section_footer(w); } diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 470d7cb9b1..fea2f9b8b3 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -16,6 +16,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 959d114d09..0b7ad1c07c 100644 --- a/libavcodec/exif.c +++ b/libavcodec/exif.c @@ -1,6 +1,7 @@ /* * EXIF metadata parser * Copyright (c) 2013 Thilo Borgmann + * Copyright (c) 2024 Leo Izen * * This file is part of FFmpeg. * @@ -23,9 +24,12 @@ * @file * EXIF metadata parser * @author Thilo Borgmann + * @author Leo Izen */ -#include "exif.h" +#include "libavutil/display.h" + +#include "exif_internal.h" #include "tiff_common.h" #define EXIF_TAG_NAME_LENGTH 32 @@ -197,6 +201,8 @@ static int exif_add_metadata(void *logctx, int count, int type, }; } +static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le, + int depth, AVDictionary **metadata); static int exif_decode_tag(void *logctx, GetByteContext *gbytes, int le, int depth, AVDictionary **metadata) @@ -220,7 +226,7 @@ static int exif_decode_tag(void *logctx, GetByteContext *gbytes, int le, // 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); + ret = exif_parse_ifd_list(logctx, gbytes, le, depth + 1, metadata); } else { const char *name = exif_get_tag_name(id); char buf[7]; @@ -239,35 +245,262 @@ static int exif_decode_tag(void *logctx, GetByteContext *gbytes, int le, return ret; } +static int exif_get_collect_size(void *logctx, GetByteContext *gb, int le, int depth) +{ + int entries, total_size = 2; + GetByteContext gbytes; + + if (depth > 2) + return 0; + + gbytes = *gb; + entries = ff_tget_short(&gbytes, le); + if (bytestream2_get_bytes_left(&gbytes) < entries * 12) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < entries; i++) { + int cur_pos; + unsigned id, count; + enum TiffTypes type; + ff_tread_tag(&gbytes, le, &id, &type, &count, &cur_pos); + if (!bytestream2_tell(&gbytes)) { + bytestream2_seek(&gbytes, cur_pos, SEEK_SET); + continue; + } + if (ff_tis_ifd(id)) { + int ret = exif_get_collect_size(logctx, &gbytes, le, depth + 1); + if (ret < 0) + return ret; + total_size += ret + 12; + } else { + int payload_size = type == TIFF_STRING ? count : count * type_sizes[type]; + if (payload_size > 4) + total_size += 12 + payload_size; + else + total_size += 12; + } + bytestream2_seek(&gbytes, cur_pos, SEEK_SET); + } + + return total_size; +} + +static inline void tput16(PutByteContext *pb, const int le, const unsigned int value) +{ + le ? bytestream2_put_le16(pb, value) : bytestream2_put_be16(pb, value); +} + +static inline void tput32(PutByteContext *pb, const int le, const unsigned int value) +{ + le ? bytestream2_put_le32(pb, value) : bytestream2_put_be32(pb, value); +} + +static int exif_collect_ifd_list(void *logctx, GetByteContext *gb, int le, int depth, PutByteContext *pb) +{ + int entries, ret = 0, offset; + GetByteContext gbytes; + + if (depth > 2) + return 0; + + gbytes = *gb; + entries = ff_tget_short(&gbytes, le); + if (bytestream2_get_bytes_left(&gbytes) < entries * 12) + return AVERROR_INVALIDDATA; + + tput16(pb, le, entries); + offset = bytestream2_tell_p(pb) + entries * 12; + for (int i = 0; i < entries; i++) { + int cur_pos; + unsigned id, count; + enum TiffTypes type; + ff_tread_tag(&gbytes, le, &id, &type, &count, &cur_pos); + if (!bytestream2_tell(&gbytes)) { + bytestream2_seek(&gbytes, cur_pos, SEEK_SET); + continue; + } + if (bytestream2_get_bytes_left_p(pb) < 12) + return AVERROR_BUFFER_TOO_SMALL; + tput16(pb, le, id); + tput16(pb, le, type); + tput32(pb, le, count); + if (ff_tis_ifd(id)) { + int tell = bytestream2_tell_p(pb); + tput32(pb, le, offset); + bytestream2_seek_p(pb, offset, SEEK_SET); + ret = exif_collect_ifd_list(logctx, &gbytes, le, depth + 1, pb); + if (ret < 0) + return ret; + offset += ret; + bytestream2_seek_p(pb, tell + 4, SEEK_SET); + } else { + int payload_size = type == TIFF_STRING ? count : count * type_sizes[type]; + if (payload_size > 4) { + int tell = bytestream2_tell_p(pb); + tput32(pb, le, offset); + bytestream2_seek_p(pb, offset, SEEK_SET); + if (bytestream2_get_bytes_left(&gbytes) < payload_size) + return AVERROR_INVALIDDATA; + bytestream2_put_buffer(pb, gbytes.buffer, payload_size); + offset += payload_size; + bytestream2_seek_p(pb, tell + 4, SEEK_SET); + } else { + bytestream2_put_ne32(pb, bytestream2_get_ne32(&gbytes)); + } + } + bytestream2_seek(&gbytes, cur_pos, SEEK_SET); + } + + return offset; +} -int ff_exif_decode_ifd(void *logctx, GetByteContext *gbytes, - int le, int depth, AVDictionary **metadata) +int ff_exif_collect_ifd(void *logctx, GetByteContext *gb, int le, AVBufferRef **buffer) { - int i, ret; - int entries; + AVBufferRef *ref = NULL; + int total_size, ret; + PutByteContext pb; + if (!buffer) + return 0; + + total_size = exif_get_collect_size(logctx, gb, le, 0); + if (total_size <= 0) + return total_size; + total_size += 8; + ref = av_buffer_alloc(total_size); + if (!ref) + return AVERROR(ENOMEM); + bytestream2_init_writer(&pb, ref->data, total_size); + bytestream2_put_be32(&pb, le ? 0x49492a00 : 0x4d4d002a); + tput32(&pb, le, 8); + + ret = exif_collect_ifd_list(logctx, gb, le, 0, &pb); + if (ret < 0) + av_buffer_unref(&ref); - entries = ff_tget_short(gbytes, le); + *buffer = ref; + return ret; +} - if (bytestream2_get_bytes_left(gbytes) < entries * 12) { +static int exif_parse_ifd_list(void *logctx, GetByteContext *gb, int le, + int depth, AVDictionary **metadata) +{ + int entries = ff_tget_short(gb, le); + if (bytestream2_get_bytes_left(gb) < entries * 12) return AVERROR_INVALIDDATA; + + for (int i = 0; i < entries; i++) { + int ret = exif_decode_tag(logctx, gb, le, depth, metadata); + if (ret < 0) + return ret; } - for (i = 0; i < entries; i++) { - if ((ret = exif_decode_tag(logctx, gbytes, le, depth, metadata)) < 0) { + // return next IDF offset or 0x000000000 or a value < 0 for failure + return ff_tget_long(gb, le); +} + +int av_exif_parse_buffer(void *logctx, const uint8_t *buf, size_t size, + AVDictionary **metadata, enum AVExifParseMode parse_mode) +{ + int ret, le; + GetByteContext gbytes; + if (size > INT_MAX) + return AVERROR(EINVAL); + bytestream2_init(&gbytes, buf, size); + if (parse_mode == AV_EXIF_PARSE_TIFF_HEADER) { + int ifd_offset; + // 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\n"); return ret; } + bytestream2_seek(&gbytes, ifd_offset, SEEK_SET); + } else { + le = parse_mode == AV_EXIF_ASSUME_LE; } - // return next IDF offset or 0x000000000 or a value < 0 for failure - return ff_tget_long(gbytes, le); + // read 0th IFD and store the metadata + // (return values > 0 indicate the presence of subimage metadata) + ret = exif_parse_ifd_list(logctx, &gbytes, le, 0, metadata); + if (ret < 0) { + av_log(logctx, AV_LOG_ERROR, "error decoding EXIF data\n"); + return ret; + } + + return bytestream2_tell(&gbytes); +} + +static int attach_displaymatrix(void *logctx, AVFrame *frame, const char *value) +{ + char *endptr; + AVFrameSideData *sd; + long orientation = strtol(value, &endptr, 0); + int32_t *matrix; + /* invalid string */ + if (*endptr || endptr == value) + return 0; + /* 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; } -int avpriv_exif_decode_ifd(void *logctx, const uint8_t *buf, int size, - int le, int depth, AVDictionary **metadata) +int ff_exif_attach(void *logctx, AVFrame *frame, AVBufferRef **data) { - GetByteContext gb; + const AVDictionaryEntry *e = NULL; + int ret; + AVDictionary *m = NULL; + AVBufferRef *buffer = *data; + AVFrameSideData *sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_EXIF, buffer); + if (!sd) + return AVERROR(ENOMEM); + *data = NULL; + ret = av_exif_parse_buffer(logctx, buffer->data, buffer->size, &m, AV_EXIF_PARSE_TIFF_HEADER); + if (ret < 0) + return ret; - bytestream2_init(&gb, buf, size); + if ((e = av_dict_get(m, "Orientation", e, AV_DICT_IGNORE_SUFFIX))) { + ret = attach_displaymatrix(logctx, frame, e->value); + if (ret < 0) + return ret; + } - return ff_exif_decode_ifd(logctx, &gb, le, depth, metadata); + return 0; } diff --git a/libavcodec/exif.h b/libavcodec/exif.h index f70d21391a..284a6deffa 100644 --- a/libavcodec/exif.h +++ b/libavcodec/exif.h @@ -1,6 +1,7 @@ /* * EXIF metadata parser * Copyright (c) 2013 Thilo Borgmann + * Copyright (c) 2024 Leo Izen * * This file is part of FFmpeg. * @@ -23,21 +24,27 @@ * @file * EXIF metadata parser * @author Thilo Borgmann + * @author Leo Izen */ #ifndef AVCODEC_EXIF_H #define AVCODEC_EXIF_H #include + #include "libavutil/dict.h" -#include "bytestream.h" -/** 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, - int le, int depth, AVDictionary **metadata); +enum AVExifParseMode { + AV_EXIF_PARSE_TIFF_HEADER, + AV_EXIF_ASSUME_LE, + AV_EXIF_ASSUME_BE, +}; -int ff_exif_decode_ifd(void *logctx, GetByteContext *gbytes, int le, - int depth, AVDictionary **metadata); +/** + * Recursively decodes all IFD's and + * adds included TAGS into the metadata dictionary. + */ +int av_exif_parse_buffer(void *logctx, const uint8_t *data, size_t size, + AVDictionary **metadata, enum AVExifParseMode parse_mode); #endif /* AVCODEC_EXIF_H */ diff --git a/libavcodec/exif_internal.h b/libavcodec/exif_internal.h new file mode 100644 index 0000000000..e0e04e0020 --- /dev/null +++ b/libavcodec/exif_internal.h @@ -0,0 +1,41 @@ +/* + * EXIF metadata parser - internal functions + * Copyright (c) 2013 Thilo Borgmann + * Copyright (c) 2024 Leo Izen + * + * 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 + * @author Leo Izen + */ + +#ifndef AVCODEC_EXIF_INTERNAL_H +#define AVCODEC_EXIF_INTERNAL_H + +#include "libavutil/buffer.h" +#include "libavutil/frame.h" +#include "bytestream.h" +#include "exif.h" + +int ff_exif_attach(void *logctx, AVFrame *frame, AVBufferRef **data); +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 81f724d230..c34dd0db40 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -42,6 +42,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" @@ -52,11 +53,6 @@ #include "jpeglsdec.h" #include "profiles.h" #include "put_bits.h" -#include "tiff.h" -#include "exif.h" -#include "bytestream.h" -#include "tiff_common.h" - static int init_default_huffman_tables(MJpegDecodeContext *s) { @@ -2040,8 +2036,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 @@ -2049,27 +2043,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); + if (!s->exif_buffer) + return AVERROR(ENOMEM); + memcpy(s->exif_buffer->data, aligned, len); + skip_bits(&s->gb, len << 3); + len = 0; goto out; } @@ -2381,13 +2360,12 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, int i, 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_buffer_unref(&s->exif_buffer); av_freep(&s->stereo3d); s->adobe_transform = -1; @@ -2853,60 +2831,12 @@ 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_buffer) { + ret = ff_exif_attach(s->avctx, frame, &s->exif_buffer); + if (ret < 0) + return ret; } - 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')) && @@ -2961,7 +2891,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_buffer_unref(&s->exif_buffer); reset_icc_profile(s); diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 13c524d597..780381445f 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -138,7 +138,7 @@ typedef struct MJpegDecodeContext { unsigned int ljpeg_buffer_size; int extern_huff; - AVDictionary *exif_metadata; + AVBufferRef *exif_buffer; AVStereo3D *stereo3d; ///!< stereoscopic information (cached, since it is read before frame allocation) diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index 3ce441aa2c..47b6173907 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -44,6 +44,7 @@ #include "bytestream.h" #include "codec_internal.h" #include "decode.h" +#include "exif_internal.h" #include "faxcompr.h" #include "lzw.h" #include "tiff.h" @@ -1271,7 +1272,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: @@ -1751,6 +1752,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; + } case DNG_VERSION: if (count == 4) { unsigned int ver[4]; diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h index e67c59abad..b8d0228570 100644 --- a/libavcodec/tiff.h +++ b/libavcodec/tiff.h @@ -97,6 +97,7 @@ enum TiffTags { TIFF_GEO_KEY_DIRECTORY = 0x87AF, TIFF_GEO_DOUBLE_PARAMS = 0x87B0, TIFF_GEO_ASCII_PARAMS = 0x87B1, + TIFF_EXIFTAG = 0x8769, }; /** abridged list of DNG tags */ diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 54b3fde6dc..e8eb748ea1 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -35,6 +35,9 @@ * Exif metadata * ICC profile * + * @author Leo Izen + * Exif metadata + * * Unimplemented: * - Animation * - XMP metadata @@ -47,7 +50,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" @@ -1451,13 +1454,12 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, break; } case MKTAG('E', 'X', 'I', 'F'): { - int le, ifd_offset, exif_offset = bytestream2_tell(&gb); - AVDictionary *exif_metadata = NULL; - GetByteContext exif_gb; + AVBufferRef *buf; if (s->has_exif) { av_log(avctx, AV_LOG_VERBOSE, "Ignoring extra EXIF chunk\n"); - goto exif_end; + bytestream2_skip(&gb, chunk_size); + break; } if (!(vp8x_flags & VP8X_FLAG_EXIF_METADATA)) av_log(avctx, AV_LOG_WARNING, @@ -1465,25 +1467,13 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, "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"); - goto exif_end; - } - - 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; - } - - av_dict_copy(&p->metadata, exif_metadata, 0); - -exif_end: - av_dict_free(&exif_metadata); - bytestream2_skip(&gb, chunk_size); + buf = av_buffer_alloc(chunk_size); + if (!buf) + return AVERROR(ENOMEM); + bytestream2_get_buffer(&gb, buf->data, chunk_size); + ret = ff_exif_attach(avctx, p, &buf); + if (ret < 0) + return ret; break; } case MKTAG('I', 'C', 'C', 'P'): { diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 00bd7a98a9..2e89e1dfda 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -431,8 +431,8 @@ static int avi_extract_stream_metadata(AVFormatContext *s, AVStream *st) 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); + return av_exif_parse_buffer(s, data + offset, data_size - offset, + &st->metadata, AV_EXIF_ASSUME_LE); break; case MKTAG('C', 'A', 'S', 'I'): avpriv_request_sample(s, "RIFF stream data tag type CASI (%u)", tag); diff --git a/libavutil/frame.c b/libavutil/frame.c index a3f07ca089..856d16cb9b 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -958,6 +958,7 @@ const char *av_frame_side_data_name(enum AVFrameSideDataType type) case AV_FRAME_DATA_DOVI_RPU_BUFFER: return "Dolby Vision RPU Data"; case AV_FRAME_DATA_DOVI_METADATA: return "Dolby Vision Metadata"; case AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT: return "Ambient viewing environment"; + case AV_FRAME_DATA_EXIF: return "EXIF metadata"; } return NULL; } diff --git a/libavutil/frame.h b/libavutil/frame.h index c0c1b23db7..d1ad086dfb 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -224,6 +224,12 @@ enum AVFrameSideDataType { * encoding. */ AV_FRAME_DATA_VIDEO_HINT, + + /** + * 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/tests/ref/fate/exif-image-embedded b/tests/ref/fate/exif-image-embedded index d5937e9f86..98e7782025 100644 --- a/tests/ref/fate/exif-image-embedded +++ b/tests/ref/fate/exif-image-embedded @@ -33,8 +33,11 @@ color_space=bt470bg color_primaries=unknown color_transfer=unknown chroma_location=center -TAG:UserComment=AppleMark +[SIDE_DATA] +side_data_type=EXIF metadata +UserComment=AppleMark +[/SIDE_DATA] [/FRAME] [FRAME] media_type=audio diff --git a/tests/ref/fate/exif-image-jpg b/tests/ref/fate/exif-image-jpg index bed265b717..f5f4fcee6b 100644 --- a/tests/ref/fate/exif-image-jpg +++ b/tests/ref/fate/exif-image-jpg @@ -33,31 +33,33 @@ color_space=bt470bg color_primaries=unknown color_transfer=unknown chroma_location=center -TAG:ImageDescription= -TAG:Make=Canon -TAG:Model=Canon PowerShot SX200 IS -TAG:Orientation= 1 -TAG:XResolution= 180:1 -TAG:YResolution= 180:1 -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= +[SIDE_DATA] +side_data_type=EXIF metadata +ImageDescription= +Make=Canon +Model=Canon PowerShot SX200 IS +Orientation= 1 +XResolution= 180:1 +YResolution= 180:1 +ResolutionUnit= 2 +DateTime=2013:07:18 13:12:03 +YCbCrPositioning= 2 +ExposureTime= 1:1250 +FNumber= 40:10 +ISOSpeedRatings= 160 +ExifVersion= 48, 50, 50, 49 +DateTimeOriginal=2013:07:18 13:12:03 +DateTimeDigitized=2013:07:18 13:12:03 +ComponentsConfiguration= 1, 2, 3, 0 +CompressedBitsPerPixel= 3:1 +ShutterSpeedValue= 329:32 +ApertureValue= 128:32 +ExposureBiasValue= 0:3 +MaxApertureValue= 113:32 +MeteringMode= 5 +Flash= 16 +FocalLength= 5000:1000 +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 @@ -199,7 +201,7 @@ TAG:MakerNote= 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= +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 @@ -217,22 +219,23 @@ 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 +FlashpixVersion= 48, 49, 48, 48 +ColorSpace= 1 +PixelXDimension= 4000 +PixelYDimension= 2248 +GPSLatitudeRef=R98 +GPSLatitude= 48, 49, 48, 48 +0x1001= 4000 +0x1002= 2248 +FocalPlaneXResolution=4000000:244 +FocalPlaneYResolution=2248000:183 +FocalPlaneResolutionUnit= 2 +SensingMethod= 2 +FileSource= 3 +CustomRendered= 0 +ExposureMode= 0 +WhiteBalance= 0 +DigitalZoomRatio= 4000:4000 +SceneCaptureType= 0 +[/SIDE_DATA] [/FRAME] diff --git a/tests/ref/fate/exif-image-webp b/tests/ref/fate/exif-image-webp index 783abefc60..4cebb326e2 100644 --- a/tests/ref/fate/exif-image-webp +++ b/tests/ref/fate/exif-image-webp @@ -33,31 +33,33 @@ color_space=bt470bg color_primaries=unknown color_transfer=unknown chroma_location=unspecified -TAG:ImageDescription= -TAG:Make=Canon -TAG:Model=Canon PowerShot SX200 IS -TAG:Orientation= 1 -TAG:XResolution= 180:1 -TAG:YResolution= 180:1 -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= +[SIDE_DATA] +side_data_type=EXIF metadata +ImageDescription= +Make=Canon +Model=Canon PowerShot SX200 IS +Orientation= 1 +XResolution= 180:1 +YResolution= 180:1 +ResolutionUnit= 2 +DateTime=2013:07:18 13:12:03 +YCbCrPositioning= 2 +ExposureTime= 1:1250 +FNumber= 40:10 +ISOSpeedRatings= 160 +ExifVersion= 48, 50, 50, 49 +DateTimeOriginal=2013:07:18 13:12:03 +DateTimeDigitized=2013:07:18 13:12:03 +ComponentsConfiguration= 1, 2, 3, 0 +CompressedBitsPerPixel= 3:1 +ShutterSpeedValue= 329:32 +ApertureValue= 128:32 +ExposureBiasValue= 0:3 +MaxApertureValue= 113:32 +MeteringMode= 5 +Flash= 16 +FocalLength= 5000:1000 +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 @@ -199,7 +201,7 @@ TAG:MakerNote= 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= +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 @@ -217,22 +219,23 @@ 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 +FlashpixVersion= 48, 49, 48, 48 +ColorSpace= 1 +PixelXDimension= 4000 +PixelYDimension= 2248 +GPSLatitudeRef=R98 +GPSLatitude= 48, 49, 48, 48 +0x1001= 4000 +0x1002= 2248 +FocalPlaneXResolution=4000000:244 +FocalPlaneYResolution=2248000:183 +FocalPlaneResolutionUnit= 2 +SensingMethod= 2 +FileSource= 3 +CustomRendered= 0 +ExposureMode= 0 +WhiteBalance= 0 +DigitalZoomRatio= 4000:4000 +SceneCaptureType= 0 +[/SIDE_DATA] [/FRAME] From patchwork Tue Feb 13 21:24:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 46241 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c493:b0:19e:cdac:8cce with SMTP id eo19csp1422pzb; Tue, 13 Feb 2024 13:25:35 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCU12FeQtSf01z9SH3CEP0xwbuFNQ3fFiFwH2w0W+2eAtDhaIvtRAmWnrZrh89p26QunVTW/c4r7lz+DmBtmTo0I0GFIaNV7BGvHuw== X-Google-Smtp-Source: AGHT+IEzL6L66eMvoeDZazgY0ZGWDAre2f8LDZoYeomY+pHEv88lzNMp2/6HOQNffUYsh/r/ha5j X-Received: by 2002:a17:906:3c53:b0:a3d:1f59:7410 with SMTP id i19-20020a1709063c5300b00a3d1f597410mr319755ejg.22.1707859534140; Tue, 13 Feb 2024 13:25:34 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1707859534; cv=none; d=google.com; s=arc-20160816; b=wumoFd8fSTyKMeId+bVnqcWg5HMbOCsaLlx+ztAfAjKyywlulIPRZ9qigSmApBAgmQ Csr/GIGeZenA4o8E9yI2kmQoA47EgKbVqqS3mynxqMY3a9iGvZdkqzBRkRlGXMG7rTxf QqxMPxgMCjTo8LqRNNg7UlZwJTdBWk2QxkW91jVWKP0yv7eL2w3Jlyde9mUdhyg76CVV aybyJHv2I428IwAPW6oVMLrh2WeOytuOT3fKf1k9rQ0ypzEzvpi/hr7bmOgSspwXnZiA Ao81JbgewVR+/9SOmLB92ceZzX3Uj89QUieTVyZBRyQGfN3Y54zBpqHc9U3kxuBLSgtN QAnQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=PR8OQD/qk1co7b8lHVJ7uwvO0RLTyxefI8RPi8slFJI=; fh=jIgkv312T4osuDyxj+ESHhnTIQlO4PatAkqL3Q6SMxc=; b=Jcg9M+MQ1eSo+iM5DsROxEWbPIzMCqiSGQI/0EtyHLgTWKF9+N4gaFyfxxzx9pUZZk ZdnyP6EbN5WI1OlWXhxGBeaUdwBo3YAFjlMZlaaWYweZEkkGCE0mhl9BR/Rmc8sMrEj5 pItoSefpnrrHrhAdnEzYud8fsr12VZNSrkR8wG9yO1GzXfCCXHjPBjzMDuCCmIobTtQy GfxPdEmV1jeQTDUmUUxXaGyO14zisww7qtH7YRWBMf1+uguKkTgwtxplGpo9/ZJzOpFz hJXowKANyrJ03E3HGUmtNqZbDY7UWYTn0dqLpuSbUdpiwr9nfa/aas8cyI0edR1U0DZJ 6X+Q==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=BY3dpkF8; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com X-Forwarded-Encrypted: i=1; AJvYcCXIPN34iWzrsAo5yITCXBOHABx+SAEI3D0SBxAj6vspU4t+22tAQHAW7o3p4lqPbQ16pKhZiYRsGnxsEgW7iU8h20pzmDx8UC1Bso1Qr/3QqQg7m8Ic9q9E3zMtMon9xKhyBID0wwqemUcwN1TgTpU1g4x/kyWU/7Ll4MZy3bl1F2AqKnDHSyl78wm9BpM5nwHOIDzq6hLVhaQcUPEcn34qfIz6Sm7z9/vQzpanMmxjgqDMeLydlJ+fqvGarWZHjKFYlN41Hb3khfP79+1+IyI1VVtH6I3UHVrwZx7y+omn01rQSRntzQ9pLb93DlNuHOWDVIj5fvta0VzOU/kcG6xJCZYfdwoyw51a3yZRevzU63s0Oqp8+TfmaTo5qyaHCr58j4oxcOCmB1hrTX04ZEmHVqil5mkb6p++1+BF5ijJOxVQPTv28z2N0WXGILo15vbpqZam6ZBR35rDcSf8bNCS/yRx+zlNyEvBfCbdcffHZiOfCj8JbnxtUn60aJrdhZVDCKS/KvGB1n4wrhDnLSc8XqfLXkS1ca+8URFNI4gccAR60H+b05vldzGUBArHZ57YFdrJGti3h/0AxVRWiouEBxfl/pDwfMnceIj15dx4XzoMvDbloq/KSQIgRNjv2KkT4XbRPzyYHMmEWBm/pMeRieKREi2oby4bmSyN49YrMjQ7w1wKFU6jffEJAxLzYSgQkgoEVUYIelO1r9+n6jRF7pmEwSU0OIY2rTvR7nUWV0QFDf2zR/UHm1ZmujL5PdbkT847sxOFSrxtYPcVLsyajb6Uas6ai1DazUGcvEUIYpihA+r5jmx+t6clK0GkIvjDmV0ewnm2T3uCzzooHUyLprJP2qudy6Wz8vBzA8CwynOMJcG4L6Di9qKs5MiXkWxn6/1HfVpTHzr5cCEEm9AsRfKgmurgxX1aWNM/YKH90WKTtYDrLcKSuKDpZXTPNOGz91 Dh4YbYGDdmYYeyNy0OKjuLHJ3wtiAjtG8LDS2XxTTk4UcZ4zg/3f+cQmcuFQyiYk6IrThTcbkI0sDF1hsOgKkBH3/mVGxyFfl7kjXo9PvQz2kCjKaZ6/0rwxYcNbADwKukDtORr+ryWjhSXeUWpNJtOyg0u4ZMHJMlffoPhjIDWeoQk4//O3CVu0jpeC67+cyOdH8QDaqmOj9sse+6tNXAgIwCJWDXYUACpfhHcJOHTUFmaY1Zism3LxitsRQ7ENmxw372Yh0N5nFjhX0mlkMo2GvS3tJf+UVWMFk6EQkNORDAMx3gvVsa8JB60tY7lFJzXFSaqoXEssfNEA3JFXLVDlyos5aROVnTnoxoaDTQY2XeFqTlq7tKnAs8mjIiVgA8+eOlVjIxh7+Xmx4LUf7GmZOgnTPytykNttystrRPBlUQCoyu7M8FI/qrzTwmWdJ/ai6PWAwDviH6H4TTlOJPWNp442NdNKt/PK5My78MhB7RFJhv6wvk4AJVXjUXxGKR1WcDIVbhCil2mdE+wdEG/15OgJfJup8Y7W24/Ea2Wjs4UgGL8ZJQ4PNDtXvL12IXnWlFNeevmm+Euv0bB6TT9Hi+herNz8zcdytW9Q== Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id k11-20020a17090627cb00b00a3cbc549b42si1516044ejc.172.2024.02.13.13.25.32; Tue, 13 Feb 2024 13:25:34 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=BY3dpkF8; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 72CFC68D1AF; Tue, 13 Feb 2024 23:25:10 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-oi1-f179.google.com (mail-oi1-f179.google.com [209.85.167.179]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 030A468C4E1 for ; Tue, 13 Feb 2024 23:25:02 +0200 (EET) Received: by mail-oi1-f179.google.com with SMTP id 5614622812f47-3be110bbff9so1416098b6e.1 for ; Tue, 13 Feb 2024 13:25:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707859500; x=1708464300; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uVhqDlK8gauvT38E5XzDPqYApJ0R68fVXpc7QAfsy44=; b=BY3dpkF8S9Z/8TdSVnrL5jEUzLRMEBlGSJnP8DaXzvfomEcCWceWXjy/Wvj3JpysRT 9FHPE+IKUdY2psH4YYcrY/eJoAmOtiuj+awWxzrRBk1bNc/xGVqLf2gCsn17yQK3UN/E yN0hcQuVUyE8f1qvqrBUN6xYCGuzOEss+pu2IhN3sLDH8utimOMN/c8Xgmm10DTgRJv9 Fh1zV4mm2ucFDFVpV8oXwwQy5fvhDFF5RtMWQot/YHaUiFxnrKoVlqKjIn8FQL5RP2cc amvP2G0br01Udrd8zaZFgUy2TAwHv0/n8/a76y5ktoTeqGeBJp/2M2z28nWXJbF0aZa3 tpqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707859500; x=1708464300; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uVhqDlK8gauvT38E5XzDPqYApJ0R68fVXpc7QAfsy44=; b=Z4e0EQgKp61mFCstAllPgLF0r8k1aqKUSKUooLMUntny2zDopnpxBH4LvPCmDgHnDK 13FJc9VN/osG/Ri46gYypG8VsPGy12ChXKKMRFQSDu2sLVha4nk8IJijYKsHYUQ9haDr HsLlknUgmPtQIUQeDZl7aDiNn88LXcQ/Hhmi12sR7msZQDChkdorQjHyPM1RBaOkUfqf AWVa7hFkmqeYg74EYpBkrObjg6xlh2yHqINH4+P1G5vALvdsoroC3SbHuDc4UhSYT+gB IXQHlaVml3q/PcAyyj3OhOg2NXhD7s/5jawKmNLtpWj3N+X1iZWrV4lU1kAJZEnZagUY q9LQ== X-Gm-Message-State: AOJu0Yw4JHlCNfebmP80ReUGt1AYQnmDcvzFfkP1V6PQ0HrxmN/NF/4k tbtIK8XFEQ9g8+xgo1VT7Ls/VMY54vuaRrm/3QApdO6ndstdt2Rnv3ufYDZG X-Received: by 2002:a05:6870:9e85:b0:21a:3083:5118 with SMTP id pu5-20020a0568709e8500b0021a30835118mr204677oab.1.1707859500069; Tue, 13 Feb 2024 13:25:00 -0800 (PST) Received: from gauss.local (c-68-56-149-176.hsd1.mi.comcast.net. [68.56.149.176]) by smtp.gmail.com with ESMTPSA id g5-20020a37e205000000b007861bea2972sm1523016qki.6.2024.02.13.13.24.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Feb 2024 13:24:59 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 13 Feb 2024 16:24:55 -0500 Message-ID: <20240213212456.167386-3-leo.izen@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240213212456.167386-1-leo.izen@gmail.com> References: <20240213212456.167386-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/3] avcodec/pngdec: parse eXIf chunk X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: CFZwkQ/bS0uN Add support to parse eXIf chunks using the new EXIF framework. Signed-off-by: Leo Izen --- libavcodec/pngdec.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index 026da30c25..e7951d1802 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -38,6 +38,7 @@ #include "bytestream.h" #include "codec_internal.h" #include "decode.h" +#include "exif_internal.h" #include "apng.h" #include "png.h" #include "pngdsp.h" @@ -123,6 +124,7 @@ 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 */ @@ -652,6 +654,26 @@ 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; + } + if (s->pic_state & PNG_IDAT) { + av_log(avctx, AV_LOG_ERROR, "eXIf after IDAT\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 @@ -890,6 +912,12 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, p->flags |= AV_FRAME_FLAG_KEY; p->flags |= AV_FRAME_FLAG_INTERLACED * !!s->interlace_type; + if (s->exif_data) { + ret = ff_exif_attach(avctx, p, &s->exif_data); + if (ret < 0) + return ret; + } + if ((ret = populate_avctx_color_fields(avctx, p)) < 0) return ret; ff_thread_finish_setup(avctx); @@ -1571,6 +1599,12 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, s->mdvc_max_lum = bytestream2_get_be32u(&gb_chunk); s->mdvc_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"); @@ -1907,6 +1941,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); From patchwork Tue Feb 13 21:24:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 46242 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c493:b0:19e:cdac:8cce with SMTP id eo19csp1462pzb; Tue, 13 Feb 2024 13:25:42 -0800 (PST) X-Forwarded-Encrypted: i=2; AJvYcCUVTWCrubByVytnqtfp/2tUtAjfHx+MQzPe3vCSMCBytl9hUktcVJi5OlGY4YeTMY/oy3WE0t6vvgxxKbumQJZVrtrxIrFBw6NkYg== X-Google-Smtp-Source: AGHT+IEeMFpc+i5ixCAROHZrMvicTkDjrc7ELIT3gFvne+gc2LzdF8WXIhOp7gU8kekATTUG3kes X-Received: by 2002:a17:906:2792:b0:a3c:c323:2069 with SMTP id j18-20020a170906279200b00a3cc3232069mr38200ejc.28.1707859541751; Tue, 13 Feb 2024 13:25:41 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1707859541; cv=none; d=google.com; s=arc-20160816; b=TC4t5PMWcGScigWa4fwpcpmMTxDjn7v4LTiJ8KrZ5wEkDWFl3t05FzgUarWyq/a7vc 6MsFQFF6GPzLdk8RGAm6QimBiioP6YEq2T4tR0TyKC77Sp/gCqzaQoPoC0bV+vuQ5mfg 6Uzk+M1JajLfdtTLKQf5Khj2dbvJIrJmDC4WudMG6aNZ8NGXwZxMkIYP3C+iK+6u1214 WgmquGUwWIU8LAlzSK4ZU8sJTP9KwZE2t7vwgS0mQ/AfMtlVaVSX3heoy30Ty4PtvQcP fCriBoytKDXFZCJLUxQ1rH/Xe5jSOHgjQmfnZpduhvy9AIbsXT0G9Ox8AtgBRb9biS5a BYcg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=UqEjdrv52yWTAx/v/af1WZfv9va0F+Y1BabciTxM5Y8=; fh=2LZVopQ5ptjXHnOUXbfmZCG4ChL+U5rX5clh7qx1Qa0=; b=gwOAhfRwOjWxIFZGFH8HtWf2jWKDZCSjfQ14J55Xq8EBwZb7D7UMSDDjp+gXKbBUmH owtrp+lse8U6K5VekvTnp/vyZ0b2vScm42J0UqRK0v98UB1JH+q3du2iCHdyS8/cklny hOGbPDp9l3ZqCPekS5QYcyk+KDhq5GLzyIaLMY1EmkAypg0uFKTCKjgohPabgpQ4nI0G cAJqu9I0+InMrl3DqOQo+NBp/gzSpOuKvRspZwQmFo/w4N8nI7OVu0ppml2/SAkfjM4D 8qNJQ1Cb4TmhyE0qCs5/ebYnC5htJyPFVxgTS6jNFCmelwAGiYMtZd14ozycSFJNpxSc 2vaw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=VokVakUx; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com X-Forwarded-Encrypted: i=1; AJvYcCX8LuXTW81otx7KDZtyNyn6wemDPpiLVVC6+Tbi+mQQUwxg9vT8kNf9t6InrNVoJfitYJT5nZgnK95v1sCC9XWtMk1rdmTbv6Sd6rtgHt3o4/8fk8FQpL7u+YIHOV/4xWyHpVslcyzftjkGz5avcUhX9JS2pkMhuvo+6w+8JOCBVtzzo8euxDP8gdouhBD+u1Ke659UXx4V+bErHK4nc961RfqPLH0HDPfWkJF9c08YJs9HkFvC4Z0G6AekbFKEQ6vs+7y9HcMnrlq+mo0JRGydl7SYlvPIxfx6Bg4ObkC8gMQffDE7z26qU1pp7Q/lDNdkR89g1kbEWkSybq2AuCt+hhN8QBvq9J9uEpQJr3xmNdH2Ia1hSBc8g2yo0zvEDrsfZCiMmrH1sjwegguKBrjBjpI5fG6jHyRuwni0kX9DACooiZKOwUNffSErb8fEePvHjTZqwggxcrbUeYfSfmSyEy5XYk9pm/QLcNcB3bt8/fzijbZzU4WsUErFTiYuT7odQD80pNbba12FRBTnYZBz7hhQUhsWxQfK1or7ytehsZ33E99o8W8WSdS7xegpu/A8NtRKl/vHZO/45jvqGbV6F5pXqx9X5DzIL9fgX0jTiD3wMJnbG4PXKFCBVuOUTDmuehF6U0K5Ef8WDBiQlPycTCuUOje0lBJ31Zo2ZFWEU5bIzoeZ5Mz7fZv21NEXEhUa+SFBwNCfa8bG9z5JYzXgu3Fnt1OA1OkUBZB/+XyT/uuOl5lPU3CoCAjcE/vbwTATwvd45Z7CMtyla2F1rwZT26OfFdihg7hZVhA3F5c3cxVQL/CFrpPG5xJxAagPh2idvCP1rdozh4xkc9fAopwMMmP/y6JAX8PdLhv3mQO/KitiO2qaMBcXkjtuan9vwTghZm+kErboCSt2TPoNDzmHvnn47t+2TviRJg7HvaIKEwu3NIWvyQXpiNOwbUqj4fcTp3 nbjsSP2g6XaIC1V3LGHtRXjMjaPNY/xd3VY1VA9opI7FtO1+6TtnZH4OLbUJrtujvNZLEvTuDuOFyjG/MfwtIyiMVrLdobOyuAbPO0raU2Bjrs9cIux/KkVpTqILkTytf9hxoxCWi3lzwoZjr3JYNzokZn9Jgw8YtskrXfmNvpewLzwFUGctSHUnyaVSRVrRhhUodNm0yOYO/c85R6I2cuOoqzpN9JdzGpXezGs0vU7+erSmRqIaa7Y2q4xZCOaDFAOdy60PBR/xMKyhQdfHkGOwMirm4Iu0fQGm8FESJV91l7F1y3HCc+wEzc2+WCw6dyfuyr+jHLaCi87wALIDPe5KNUtlUS3TXJrDVY5jhVzGAUcDFpR0mVRS6ohxxX5vRdPQOEv79+3J9BLn7VtGWTjrtX7aYAQuc2hdPFp3SP7IpymQJK/Z7pBHfg/lodfboAFVC3Vlom/6vru4g0TUwLdGyzIq+xHiW6IF83waJlKUTs0BETvA49GrnUqtLqXz0cz9MDl1rJ9aD+3QK/PuGwlSBWEwZ4/DTmqtBnzzMAhnfE/R/lAMPWh3H+7dXuORes4Nz2v0DyZC9YM0xZuAoz4D0QaJA8MzKqQrACW8sBN04n+taqOSH2kiEZ+dM= Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id gn38-20020a1709070d2600b00a3cfc0286b5si1085012ejc.420.2024.02.13.13.25.40; Tue, 13 Feb 2024 13:25:41 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=VokVakUx; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7977168D1C1; Tue, 13 Feb 2024 23:25:11 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qv1-f54.google.com (mail-qv1-f54.google.com [209.85.219.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 26E5E68C4E1 for ; Tue, 13 Feb 2024 23:25:02 +0200 (EET) Received: by mail-qv1-f54.google.com with SMTP id 6a1803df08f44-68155fca099so18536376d6.1 for ; Tue, 13 Feb 2024 13:25:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707859501; x=1708464301; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WKDVTgd80GgN+YNYW92qz6WylJtqySsa39GCHUeL080=; b=VokVakUxqzur1bUAp7sefSBn1vvvRxS28O46Zkhq/D13jW5gjme4Sv07VvmvVz6inX tVXHhxa+wJRC4G3+NnKvDsgoWRWTBcYE36vF5RrEUKxYEh/A175vMgWKYUtktd0mFeF7 OmJEltBULFlnfRRdLpMZmtGVng2gnm6CWAN3kz/e1Oe/Z/LhCeAkj/WAht+BhDl5d8BK ZieVGvl33x5mpo0tUlTUpC5KUYe20t7jtNvJVoCLmimFMsPL6IOzgUGeH+fvbZI4wNPV bH5dDkDr9OlFHkDTcXxZZ1j7zWmO7Xp3YKqcMHveMx2zS2zahRNzI9Tn2nlX/S1k3i9/ mv9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707859501; x=1708464301; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WKDVTgd80GgN+YNYW92qz6WylJtqySsa39GCHUeL080=; b=Cw9pCd/Bg892mny4qCgAMlg3KEjLVAVhYZxfv7gkNylLbZsGZB7DuYwmanGijf9o0e ZkVjGKZyioqpFrw9iLzA9HoujKsE3ZKxWefiBhf7jbGIKShkrDFjnXOca0N9HXtdUXRF q6mH9RiAaAj5o6FeHv+PxyISpAmQhJDoeKHovtp6upqbzokcNEteVJwNipUk69dzERIz UAqaZake1blX/GQHY/vBaMVsEnV1a6aaepsLeV87dHnKSrADB32DlVsFmGJundjq1SCB JwR+5xBmFT6XDjyZn5Qn4p6dfu6+fzRZrdEpOGWjCvUwey2bO+GmKPDButjrQY8tIXPE fJfQ== X-Gm-Message-State: AOJu0YyuvGf2P28jw6kSnbLHZDrlrI1xbsSktewU3pxncXeaJ3MPSTCy 2FO6d5OfRJec1jwAgoBrRiKXYH7+XwqhPkSww7b66OYJsuRTSA72nGoJyw16 X-Received: by 2002:a05:620a:1b9b:b0:785:d092:bb5a with SMTP id dv27-20020a05620a1b9b00b00785d092bb5amr818332qkb.7.1707859500878; Tue, 13 Feb 2024 13:25:00 -0800 (PST) Received: from gauss.local (c-68-56-149-176.hsd1.mi.comcast.net. [68.56.149.176]) by smtp.gmail.com with ESMTPSA id g5-20020a37e205000000b007861bea2972sm1523016qki.6.2024.02.13.13.25.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Feb 2024 13:25:00 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Tue, 13 Feb 2024 16:24:56 -0500 Message-ID: <20240213212456.167386-4-leo.izen@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240213212456.167386-1-leo.izen@gmail.com> References: <20240213212456.167386-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 3/3] avcodec/pngenc: write eXIf chunks X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Rzw3beSHJXOn Write EXIF metadata exposed AV_FRAME_DATA_EXIF as an eXIf chunk to PNG files, if present. Signed-off-by: Leo Izen --- libavcodec/pngenc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index 50689cb50c..a302c879da 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -413,6 +413,10 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) } } + side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_EXIF); + if (side_data) + png_write_chunk(&s->bytestream, MKTAG('e', 'X', 'I', 'f'), side_data->data, FFMIN(side_data->size, INT_MAX)); + side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_ICC_PROFILE); if ((ret = png_write_iccp(s, side_data))) return ret;