[FFmpeg-devel] [Read EXIF metadata 2/3] Add EXIF metadata parser to libavcodec.

Thilo Borgmann thilo.borgmann at googlemail.com
Fri Aug 9 16:41:32 CEST 2013


2/3 rev 3

-Thilo
-------------- next part --------------
>From 1b2045a2a0d5754fa02dc5e7729ad7bc19998670 Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgmann at googlemail.com>
Date: Fri, 9 Aug 2013 16:33:43 +0200
Subject: [PATCH 2/3] Add EXIF metadata parser to libavcodec.

---
 libavcodec/exif.c |  228 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/exif.h |  168 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 396 insertions(+), 0 deletions(-)
 create mode 100644 libavcodec/exif.c
 create mode 100644 libavcodec/exif.h

diff --git a/libavcodec/exif.c b/libavcodec/exif.c
new file mode 100644
index 0000000..776c094
--- /dev/null
+++ b/libavcodec/exif.c
@@ -0,0 +1,228 @@
+/*
+ * EXIF metadata parser
+ * Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ googlemail.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
+ * @author Thilo Borgmann <thilo.borgmann _at_ googlemail.com>
+ */
+
+#include "exif.h"
+
+
+static char *rationals2str(int *rp, int count, const char *sep)
+{
+    int i;
+    char *ap, *ap0;
+    uint64_t component_len;
+    if (!sep) sep = ", ";
+    component_len = 20LL+1LL+20LL + strlen(sep);
+    ap    = av_malloc_array(count + 1, component_len);
+    ap0   = ap;
+    ap[0] = '\0';
+    for (i = 0; i < count; i++) {
+        unsigned l = snprintf(ap, component_len, "%i:%i%s", rp[2*i + 0], rp[2*i + 1], sep);
+        if (l >= component_len) {
+            av_free(ap0);
+            return NULL;
+        }
+        ap += l;
+    }
+    ap0[strlen(ap0) - strlen(sep)] = '\0';
+    return ap0;
+}
+
+
+static char *longs2str(int32_t *lp, int count, const char *sep)
+{
+    int i;
+    char *ap, *ap0;
+    uint64_t component_len;
+    if (!sep) sep = ", ";
+    component_len = 11LL + strlen(sep);
+    ap    = av_malloc_array(count + 1, component_len);
+    ap0   = ap;
+    ap[0] = '\0';
+    for (i = 0; i < count; i++) {
+        unsigned l = snprintf(ap, component_len, "%i%s", lp[i], sep);
+        if (l >= component_len) {
+            av_free(ap0);
+            return NULL;
+        }
+        ap += l;
+    }
+    ap0[strlen(ap0) - strlen(sep)] = '\0';
+    return ap0;
+}
+
+
+static int exif_add_rational_metadata(int count, const char *name, const char *sep,
+                                      GetByteContext *gb, int le, AVDictionary **metadata)
+{
+    char *ap;
+    int i;
+    int32_t *rp;
+
+    if (count >= INT_MAX / sizeof(int64_t) || count <= 0)
+        return AVERROR_INVALIDDATA;
+    if (bytestream2_get_bytes_left(gb) < count * sizeof(int64_t))
+        return AVERROR_INVALIDDATA;
+
+    rp = av_malloc(2 * count * sizeof(int32_t));
+    if (!rp)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < count; i++) {
+        rp[2*i + 0] = ff_tget_long(gb, le);
+        rp[2*i + 1] = ff_tget_long(gb, le);
+    }
+    ap = rationals2str(rp, count, sep);
+    av_freep(&rp);
+    if (!ap)
+        return AVERROR(ENOMEM);
+    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
+    return 0;
+}
+
+
+static int exif_add_long_metadata(int count, const char *name, const char *sep,
+                                  GetByteContext *gb, int le, AVDictionary **metadata)
+{
+    char *ap;
+    int i;
+    int32_t *lp;
+
+    if (count >= INT_MAX / sizeof(int32_t) || count <= 0)
+        return AVERROR_INVALIDDATA;
+    if (bytestream2_get_bytes_left(gb) < count * sizeof(int32_t))
+        return AVERROR_INVALIDDATA;
+
+    lp = av_malloc(count * sizeof(int32_t));
+    if (!lp)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < count; i++) {
+        lp[i] = ff_tget_long(gb, le);
+    }
+    ap = longs2str(lp, count, sep);
+    av_freep(&lp);
+    if (!ap)
+        return AVERROR(ENOMEM);
+    av_dict_set(metadata, name, ap, AV_DICT_DONT_STRDUP_VAL);
+    return 0;
+}
+
+
+
+static const char *exif_get_tag_name(uint16_t id)
+{
+    int i;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(tag_list); i++) {
+        if (tag_list[i].id == id)
+            return tag_list[i].name;
+    }
+
+    return NULL;
+}
+
+
+static int exif_add_metadata(int count, int type, const char *name, const char *sep,
+                             GetByteContext *gb, int le, AVDictionary **metadata)
+{
+    switch(type) {
+    case TIFF_DOUBLE   : return ff_tadd_doubles_metadata(count, name, sep, gb, le, metadata);
+    case TIFF_SHORT    : return ff_tadd_shorts_metadata(count, name, sep, gb, le, metadata);
+    case TIFF_BYTE:
+    case TIFF_UNDEFINED:
+    case TIFF_STRING   : return ff_tadd_string_metadata(count, name, gb, le, metadata);
+    case TIFF_SRATIONAL:
+    case TIFF_RATIONAL : return exif_add_rational_metadata(count, name, sep, gb, le, metadata);
+    case TIFF_SLONG    :
+    case TIFF_LONG     : return exif_add_long_metadata(count, name, sep, gb, le, metadata);
+    default            : return AVERROR_INVALIDDATA;
+    };
+}
+
+
+static int exif_decode_tag(AVCodecContext *avctx, GetByteContext *gbytes, int le, AVDictionary **metadata)
+{
+    int ret, cur_pos;
+    unsigned id, count;
+    enum TiffTypes type;
+
+    ret = ff_tread_tag(avctx, gbytes, le, &id, &type, &count, &cur_pos);
+
+    if (ret < 0) {
+        return ret;
+    }
+
+    // read count values and add it metadata
+    // store metadata or proceed with next IFD
+    if (ff_tis_ifd(id)) {
+        ret = ff_exif_decode_ifd(avctx, gbytes, le, metadata);
+    } else {
+        const char *name = exif_get_tag_name(id);
+        char *use_name   = (char*) name;
+
+        if (!use_name) {
+            use_name = av_malloc(7);
+            if (!use_name) {
+                return AVERROR(ENOMEM);
+            }
+            snprintf(use_name, 7, "0x%04X", id);
+        }
+
+        if (exif_add_metadata(count, type, use_name, NULL, gbytes, le, metadata)) {
+            avpriv_report_missing_feature(avctx, "Value type \"%d\" for EXIF tag %s (0x%X)", type, use_name, id);
+        }
+
+        if (!name) {
+            av_freep(&use_name);
+        }
+    }
+
+    bytestream2_seek(gbytes, cur_pos, SEEK_SET);
+
+    return ret;
+}
+
+
+int ff_exif_decode_ifd(AVCodecContext *avctx, GetByteContext *gbytes, int le, AVDictionary **metadata)
+{
+    int i, ret;
+    int entries;
+
+    entries = ff_tget_short(gbytes, le);
+
+    if (bytestream2_get_bytes_left(gbytes) < entries * 12) {
+        return AVERROR_INVALIDDATA;
+    }
+
+    for (i = 0; i < entries; i++) {
+        if ((ret = exif_decode_tag(avctx, gbytes, le, metadata)) < 0) {
+            return ret;
+        }
+    }
+
+    // return next IDF offset or 0x000000000 or a value < 0 for failure
+    return ff_tget_long(gbytes, le);
+}
diff --git a/libavcodec/exif.h b/libavcodec/exif.h
new file mode 100644
index 0000000..e4f37b5
--- /dev/null
+++ b/libavcodec/exif.h
@@ -0,0 +1,168 @@
+/*
+ * EXIF metadata parser
+ * Copyright (c) 2013 Thilo Borgmann <thilo.borgmann _at_ googlemail.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
+ * @author Thilo Borgmann <thilo.borgmann _at_ googlemail.com>
+ */
+
+#ifndef AVCODEC_EXIF_H
+#define AVCODEC_EXIF_H
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "tiff.h"
+
+#define EXIF_TAG_NAME_LENGTH 32
+
+struct exif_tag {
+    char      name[EXIF_TAG_NAME_LENGTH];
+    uint16_t  id;
+};
+
+static const struct exif_tag tag_list[] = { // JEITA CP-3451 EXIF specification:
+    {"GPSVersionID",               0x00}, // <- Table 12 GPS Attribute Information
+    {"GPSLatitudeRef",             0x01},
+    {"GPSLatitude",                0x02},
+    {"GPSLongitudeRef",            0x03},
+    {"GPSLongitude",               0x04},
+    {"GPSAltitudeRef",             0x05},
+    {"GPSAltitude",                0x06},
+    {"GPSTimeStamp",               0x07},
+    {"GPSSatellites",              0x08},
+    {"GPSStatus",                  0x09},
+    {"GPSMeasureMode",             0x0A},
+    {"GPSDOP",                     0x0B},
+    {"GPSSpeedRef",                0x0C},
+    {"GPSSpeed",                   0x0D},
+    {"GPSTrackRef",                0x0E},
+    {"GPSTrack",                   0x0F},
+    {"GPSImgDirectionRef",         0x10},
+    {"GPSImgDirection",            0x11},
+    {"GPSMapDatum",                0x12},
+    {"GPSDestLatitudeRef",         0x13},
+    {"GPSDestLatitude",            0x14},
+    {"GPSDestLongitudeRef",        0x15},
+    {"GPSDestLongitude",           0x16},
+    {"GPSDestBearingRef",          0x17},
+    {"GPSDestBearing",             0x18},
+    {"GPSDestDistanceRef",         0x19},
+    {"GPSDestDistance",            0x1A},
+    {"GPSProcessingMethod",        0x1B},
+    {"GPSAreaInformation",         0x1C},
+    {"GPSDateStamp",               0x1D},
+    {"GPSDifferential",            0x1E},
+    {"ImageWidth",                 0x100}, // <- Table 3 TIFF Rev. 6.0 Attribute Information Used in Exif
+    {"ImageLength",                0x101},
+    {"BitsPerSample",              0x102},
+    {"Compression",                0x103},
+    {"PhotometricInterpretation",  0x106},
+    {"Orientation",                0x112},
+    {"SamplesPerPixel",            0x115},
+    {"PlanarConfiguration",        0x11C},
+    {"YCbCrSubSampling",           0x212},
+    {"YCbCrPositioning",           0x213},
+    {"XResolution",                0x11A},
+    {"YResolution",                0x11B},
+    {"ResolutionUnit",             0x128},
+    {"StripOffsets",               0x111},
+    {"RowsPerStrip",               0x116},
+    {"StripByteCounts",            0x117},
+    {"JPEGInterchangeFormat",      0x201},
+    {"JPEGInterchangeFormatLength",0x202},
+    {"TransferFunction",           0x12D},
+    {"WhitePoint",                 0x13E},
+    {"PrimaryChromaticities",      0x13F},
+    {"YCbCrCoefficients",          0x211},
+    {"ReferenceBlackWhite",        0x214},
+    {"DateTime",                   0x132},
+    {"ImageDescription",           0x10E},
+    {"Make",                       0x10F},
+    {"Model",                      0x110},
+    {"Software",                   0x131},
+    {"Artist",                     0x13B},
+    {"Copyright",                  0x8298},
+    {"ExifVersion",                0x9000}, // <- Table 4 Exif IFD Attribute Information (1)
+    {"FlashpixVersion",            0xA000},
+    {"ColorSpace",                 0xA001},
+    {"ComponentsConfiguration",    0x9101},
+    {"CompressedBitsPerPixel",     0x9102},
+    {"PixelXDimension",            0xA002},
+    {"PixelYDimension",            0xA003},
+    {"MakerNote",                  0x927C},
+    {"UserComment",                0x9286},
+    {"RelatedSoundFile",           0xA004},
+    {"DateTimeOriginal",           0x9003},
+    {"DateTimeDigitized",          0x9004},
+    {"SubSecTime",                 0x9290},
+    {"SubSecTimeOriginal",         0x9291},
+    {"SubSecTimeDigitized",        0x9292},
+    {"ImageUniqueID",              0xA420},
+    {"ExposureTime",               0x829A}, // <- Table 5 Exif IFD Attribute Information (2)
+    {"FNumber",                    0x829D},
+    {"ExposureProgram",            0x8822},
+    {"SpectralSensitivity",        0x8824},
+    {"ISOSpeedRatings",            0x8827},
+    {"OECF",                       0x8828},
+    {"ShutterSpeedValue",          0x9201},
+    {"ApertureValue",              0x9202},
+    {"BrightnessValue",            0x9203},
+    {"ExposureBiasValue",          0x9204},
+    {"MaxApertureValue",           0x9205},
+    {"SubjectDistance",            0x9206},
+    {"MeteringMode",               0x9207},
+    {"LightSource",                0x9208},
+    {"Flash",                      0x9209},
+    {"FocalLength",                0x920A},
+    {"SubjectArea",                0x9214},
+    {"FlashEnergy",                0xA20B},
+    {"SpatialFrequencyResponse",   0xA20C},
+    {"FocalPlaneXResolution",      0xA20E},
+    {"FocalPlaneYResolution",      0xA20F},
+    {"FocalPlaneResolutionUnit",   0xA210},
+    {"SubjectLocation",            0xA214},
+    {"ExposureIndex",              0xA215},
+    {"SensingMethod",              0xA217},
+    {"FileSource",                 0xA300},
+    {"SceneType",                  0xA301},
+    {"CFAPattern",                 0xA302},
+    {"CustomRendered",             0xA401},
+    {"ExposureMode",               0xA402},
+    {"WhiteBalance",               0xA403},
+    {"DigitalZoomRatio",           0xA404},
+    {"FocalLengthIn35mmFilm",      0xA405},
+    {"SceneCaptureType",           0xA406},
+    {"GainControl",                0xA407},
+    {"Contrast",                   0xA408},
+    {"Saturation",                 0xA409},
+    {"Sharpness",                  0xA40A},
+    {"DeviceSettingDescription",   0xA40B},
+    {"SubjectDistanceRange",       0xA40C}
+//    {"InteroperabilityIndex",      0x1}, // <- Table 13 Interoperability IFD Attribute Information
+//    {"",                           0x0}
+};
+
+/** Recursively decodes all IFD's and
+ *  adds included TAGS into the metadata dictionary. */
+int ff_exif_decode_ifd(AVCodecContext *avctx, GetByteContext *gbytes, int le, AVDictionary **metadata);
+
+#endif /* AVCODEC_EXIF_H */
-- 
1.7.4.3



More information about the ffmpeg-devel mailing list