Commit 48c3369e authored by Debarshi Ray's avatar Debarshi Ray Committed by Carlos Garnacho

tracker-extract: Add GExiv2 based extractor for RAW files

Unlike the other extraction modules, this one is licensed under GPLv2+
to match Exiv2.

https://bugzilla.gnome.org/show_bug.cgi?id=787589
parent c8bd9805
libtracker-extract, libtracker-miners-common and tracker-extract are licensed under the LGPLv2.1+. See the file
COPYING.LGPL.
libtracker-extract, libtracker-miners-common and tracker-extract (except tracker-extract-raw.c) are licensed under the
LGPLv2.1+. See the file COPYING.LGPL.
miners/apps, miners/fs (barring a few exceptions that are LGPLv2.1+) and miners/rss are licensed under the GPLv2+. See
the file COPYING.GPL.
......@@ -226,6 +226,7 @@ LIBOSINFO_REQUIRED=0.2.9
EXEMPI_REQUIRED=2.1.0
TAGLIB_REQUIRED=1.6
LIBGRSS_REQUIRED=0.7
GEXIV2_REQUIRED="any version"
GSTREAMER_REQUIRED=0.10.31
GUPNP_DLNA_REQUIRED=0.9.4
LIBPNG_REQUIRED=1.2
......@@ -966,6 +967,40 @@ fi
AM_CONDITIONAL(HAVE_LIBXML2, test "x$have_libxml2" = "xyes")
##################################################################
# Check for tracker-extract: gexiv2
##################################################################
AC_ARG_ENABLE(gexiv2,
AS_HELP_STRING([--enable-gexiv2],
[enable extractor for RAW metadata [[default=auto]]]),
[enable_gexiv2=$enableval],
[enable_gexiv2=auto])
if test "x$enable_gexiv2" != "xno" ; then
PKG_CHECK_MODULES(GEXIV2,
[gexiv2],
[have_gexiv2=yes],
[have_gexiv2=no])
AC_SUBST(GEXIV2_CFLAGS)
AC_SUBST(GEXIV2_LIBS)
if test "x$have_gexiv2" = "xyes"; then
AC_DEFINE(HAVE_GEXIV2, [], [Define if we have gexiv2])
fi
else
have_gexiv2="no (disabled)"
fi
if test "x$enable_gexiv2" = "xyes"; then
if test "x$have_gexiv2" != "xyes"; then
AC_MSG_ERROR([Could not find gexiv2.])
fi
fi
AM_CONDITIONAL(HAVE_GEXIV2, test "x$have_gexiv2" = "xyes")
####################################################################
# Check for tracker-extract: gstreamer/etc
####################################################################
......@@ -1858,6 +1893,7 @@ Metadata Extractors:
Support XPS: $have_libgxps
Support GIF: $have_libgif (xmp: $have_exempi)
Support JPEG: $have_libjpeg (xmp: $have_exempi, exif: $have_libexif, iptc: $have_libiptcdata)
Support RAW: $have_gexiv2
Support TIFF: $have_libtiff (xmp: $have_exempi, exif: yes, iptc: $have_libiptcdata)
Support Vorbis (ogg/etc): $have_libvorbis
Support Flac: $have_libflac
......
......@@ -31,6 +31,7 @@ avutil = dependency('libavutil', version: '>= 0.8.4', required: false)
dbus = dependency('dbus-1', version: '> 1.3.1')
exempi = dependency('exempi-2.0', version: '> 2.1.0', required: false)
flac = dependency('flac', version: '> 1.2.1', required: false)
gexiv2 = dependency('gexiv2', required: false)
gio = dependency('gio-2.0', version: '>' + glib_required)
gio_unix = dependency('gio-unix-2.0', version: '>' + glib_required)
glib = dependency('glib-2.0', version: '>' + glib_required)
......@@ -381,6 +382,7 @@ if have_tracker_extract
' Support GIF: @0@ (xmp: @1@)'.format(libgif.found().to_string(), exempi.found().to_string()),
' Support JPEG: @0@ (xmp: @1@, exif: @2@, iptc: @3@)'.format(
libjpeg.found().to_string(), exempi.found().to_string(), libexif.found().to_string(), libiptcdata.found().to_string()),
' Support RAW: ' + gexiv2.found().to_string(),
' Support TIFF: @0@ (xmp: @1@, exif: @2@, iptc: @3@)'.format(
libtiff.found().to_string(), exempi.found().to_string(), libexif.found().to_string(), libiptcdata.found().to_string()),
' Support Vorbis (ogg/etc): ' + libvorbis.found().to_string(),
......
[ExtractorRule]
ModulePath=libextract-raw.so
MimeTypes=image/x-dcraw;image/x-adobe-dng;image/x-canon-cr2;image/x-canon-crw;image/x-epson-erf;image/x-fuji-raf;image/x-minolta-mrw;image/x-nikon-nef;image/x-olympus-orf;image/x-panasonic-raw;image/x-pentax-pef;image/x-raw;image/x-samsung-raw;image/x-sigma-x3f;image/x-sony-arw;image/x-sony-srf;image/x-sony-sr2;
FallbackRdfTypes=nfo:Image;nmm:Photo;
......@@ -35,6 +35,7 @@ rules_files = \
10-pdf.rule \
10-png.rule \
10-ps.rule \
10-raw.rule \
10-svg.rule \
10-tiff.rule \
10-vorbis.rule \
......@@ -120,6 +121,11 @@ extractmodules_LTLIBRARIES += libextract-pdf.la
rules_DATA += 10-pdf.rule
endif
if HAVE_GEXIV2
extractmodules_LTLIBRARIES += libextract-raw.la
rules_DATA += 10-raw.rule
endif
if HAVE_GSTREAMER
extractmodules_LTLIBRARIES += libextract-gstreamer.la
rules_DATA += 10-svg.rule 15-gstreamer-guess.rule 90-gstreamer-image-generic.rule 90-gstreamer-audio-generic.rule 90-gstreamer-video-generic.rule
......@@ -269,7 +275,6 @@ libextract_png_la_LIBADD = \
$(TRACKER_EXTRACT_MODULES_LIBS) \
$(LIBPNG_LIBS)
# PS
libextract_ps_la_SOURCES = tracker-extract-ps.c
libextract_ps_la_CFLAGS = $(TRACKER_EXTRACT_MODULES_CFLAGS)
......@@ -363,6 +368,19 @@ libextract_dvi_la_LIBADD = \
$(BUILD_LIBS) \
$(TRACKER_EXTRACT_MODULES_LIBS)
# GEXIV2
libextract_raw_la_SOURCES = tracker-extract-raw.c
libextract_raw_la_CFLAGS = \
$(TRACKER_EXTRACT_MODULES_CFLAGS) \
$(GEXIV2_CFLAGS)
libextract_raw_la_LDFLAGS = $(module_flags)
libextract_raw_la_LIBADD = \
$(top_builddir)/src/libtracker-extract/libtracker-extract.la \
$(top_builddir)/src/libtracker-miners-common/libtracker-miners-common.la \
$(BUILD_LIBS) \
$(TRACKER_EXTRACT_MODULES_LIBS) \
$(GEXIV2_LIBS)
# GStreamer
libextract_gstreamer_la_SOURCES = \
tracker-extract-gstreamer.c \
......
......@@ -28,6 +28,10 @@ if flac.found()
modules += [['extract-flac', 'tracker-extract-flac.c', '10-flac.rule', [flac, tracker_miners_common_dep]]]
endif
if gexiv2.found()
modules += [['extract-raw', 'tracker-extract-raw.c', '10-raw.rule', [gexiv2, tracker_common_dep]]]
endif
if libgif.found()
modules += [['extract-gif', 'tracker-extract-gif.c', '10-gif.rule', [libgif, tracker_miners_common_dep]]]
endif
......
/*
* Copyright (C) 2017-2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <string.h>
#include <gexiv2/gexiv2.h>
#include <libtracker-extract/tracker-extract.h>
#include <libtracker-miners-common/tracker-common.h>
#include <libtracker-sparql/tracker-sparql.h>
#include "tracker-main.h"
#define CMS_PER_INCH 2.54
#define EXIF_DATE_FORMAT "%Y:%m:%d %H:%M:%S"
enum {
EXIF_FLASH_NONE = 0x0000,
EXIF_FLASH_FIRED_MISSING_STROBE = 0x0005,
EXIF_FLASH_DID_NOT_FIRE_COMPULSORY_ON = 0x0008,
EXIF_FLASH_DID_NOT_FIRE_COMPULSORY_OFF = 0x0010,
EXIF_FLASH_DID_NOT_FIRE_AUTO = 0x0018,
EXIF_FLASH_DID_NOT_FIRE_AUTO_RED_EYE_REDUCTION = 0x0058,
};
enum {
EXIF_METERING_MODE_UNKNOWN = 0,
EXIF_METERING_MODE_AVERAGE = 1,
EXIF_METERING_MODE_CENTER_WEIGHTED_AVERAGE = 2,
EXIF_METERING_MODE_SPOT = 3,
EXIF_METERING_MODE_MULTISPOT = 4,
EXIF_METERING_MODE_PATTERN = 5,
EXIF_METERING_MODE_PARTIAL = 6,
EXIF_METERING_MODE_OTHER = 255,
};
typedef struct {
gchar *artist;
gchar *copyright;
gchar *description;
gchar *document_name;
gchar *flash;
gchar *gps_altitude;
gchar *gps_direction;
gchar *gps_latitude;
gchar *gps_longitude;
gchar *make;
gchar *metering_mode;
gchar *model;
gchar *time;
gchar *time_original;
gchar *user_comment;
gchar *white_balance;
gchar *x_resolution;
gchar *y_resolution;
gdouble exposure_time;
gdouble fnumber;
gdouble focal_length;
gdouble iso_speed_ratings;
gint resolution_unit;
} RawExifData;
static void
raw_exif_data_free (RawExifData *ed)
{
g_return_if_fail (ed != NULL);
g_free (ed->artist);
g_free (ed->copyright);
g_free (ed->description);
g_free (ed->document_name);
g_free (ed->flash);
g_free (ed->gps_altitude);
g_free (ed->gps_direction);
g_free (ed->gps_latitude);
g_free (ed->gps_longitude);
g_free (ed->make);
g_free (ed->metering_mode);
g_free (ed->model);
g_free (ed->time);
g_free (ed->time_original);
g_free (ed->user_comment);
g_free (ed->white_balance);
g_free (ed->x_resolution);
g_free (ed->y_resolution);
g_free (ed);
}
static RawExifData *
raw_exif_data_new (void)
{
RawExifData *ed;
ed = g_new0 (RawExifData, 1);
ed->exposure_time = -1.0;
ed->fnumber = -1.0;
ed->focal_length = -1.0;
ed->iso_speed_ratings = -1.0;
ed->resolution_unit = -1;
return ed;
}
static gchar *
convert_exiv2_orientation_to_nfo (GExiv2Orientation orientation)
{
gchar *retval = NULL;
switch (orientation) {
case GEXIV2_ORIENTATION_NORMAL:
retval = g_strdup ("nfo:orientation-top");
break;
case GEXIV2_ORIENTATION_HFLIP:
retval = g_strdup ("nfo:orientation-top-mirror");
break;
case GEXIV2_ORIENTATION_ROT_180:
retval = g_strdup ("nfo:orientation-bottom");
break;
case GEXIV2_ORIENTATION_VFLIP:
retval = g_strdup ("nfo:orientation-bottom-mirror");
break;
case GEXIV2_ORIENTATION_ROT_90_HFLIP:
retval = g_strdup ("nfo:orientation-left-mirror");
break;
case GEXIV2_ORIENTATION_ROT_90:
retval = g_strdup ("nfo:orientation-right");
break;
case GEXIV2_ORIENTATION_ROT_90_VFLIP:
retval = g_strdup ("nfo:orientation-right-mirror");
break;
case GEXIV2_ORIENTATION_ROT_270:
retval = g_strdup ("nfo:orientation-left");
break;
default:
retval = g_strdup ("nfo:orientation-top");
break;
}
return retval;
}
static gchar *
parse_flash (gushort flash_value)
{
gchar *flash = NULL;
switch (flash_value) {
case EXIF_FLASH_NONE:
case EXIF_FLASH_FIRED_MISSING_STROBE:
case EXIF_FLASH_DID_NOT_FIRE_COMPULSORY_ON:
case EXIF_FLASH_DID_NOT_FIRE_COMPULSORY_OFF:
case EXIF_FLASH_DID_NOT_FIRE_AUTO:
case EXIF_FLASH_DID_NOT_FIRE_AUTO_RED_EYE_REDUCTION:
flash = g_strdup ("nmm:flash-off");
break;
default:
flash = g_strdup ("nmm:flash-on");
break;
}
return flash;
}
static gchar *
parse_metering_mode (gushort metering_mode_value)
{
gchar *metering_mode = NULL;
switch (metering_mode_value) {
case EXIF_METERING_MODE_AVERAGE:
metering_mode = g_strdup ("nmm:metering-mode-average");
break;
case EXIF_METERING_MODE_CENTER_WEIGHTED_AVERAGE:
metering_mode = g_strdup ("nmm:metering-mode-center-weighted-average");
break;
case EXIF_METERING_MODE_SPOT:
metering_mode = g_strdup ("nmm:metering-mode-spot");
break;
case EXIF_METERING_MODE_MULTISPOT:
metering_mode = g_strdup ("nmm:metering-mode-multispot");
break;
case EXIF_METERING_MODE_PATTERN:
metering_mode = g_strdup ("nmm:metering-mode-pattern");
break;
case EXIF_METERING_MODE_PARTIAL:
metering_mode = g_strdup ("nmm:metering-mode-partial");
break;
case EXIF_METERING_MODE_UNKNOWN:
case EXIF_METERING_MODE_OTHER:
default:
metering_mode = g_strdup ("nmm:metering-mode-other");
break;
}
return metering_mode;
}
static gchar *
parse_white_balance (gushort white_balance_value)
{
gchar *white_balance = NULL;
if (white_balance_value == 0)
white_balance = g_strdup ("nmm:white-balance-auto");
else
white_balance = g_strdup ("nmm:white-balance-manual");
return white_balance;
}
static RawExifData *
parse_exif_data (GExiv2Metadata *metadata)
{
RawExifData *ed = NULL;
gchar *time = NULL;
gchar *time_original = NULL;
gdouble gps_altitude;
gdouble gps_latitude;
gdouble gps_longitude;
gint exposure_time_den;
gint exposure_time_nom;
glong flash = G_MAXLONG;
glong metering_mode = G_MAXLONG;
glong white_balance = G_MAXLONG;
ed = raw_exif_data_new ();
if (!gexiv2_metadata_has_exif (metadata))
goto out;
ed->document_name = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.DocumentName");
time = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.DateTime");
if (time != NULL)
ed->time = tracker_date_format_to_iso8601 (time, EXIF_DATE_FORMAT);
time_original = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.DateTimeOriginal");
if (time_original == NULL)
time_original = gexiv2_metadata_get_tag_string (metadata, "Exif.Photo.DateTimeOriginal");
if (time_original != NULL)
ed->time_original = tracker_date_format_to_iso8601 (time_original, EXIF_DATE_FORMAT);
ed->artist = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.Artist");
ed->user_comment = gexiv2_metadata_get_tag_string (metadata, "Exif.Photo.UserComment");
ed->description = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.ImageDescription");
ed->make = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.Make");
ed->model = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.Model");
if (gexiv2_metadata_get_exposure_time (metadata, &exposure_time_nom, &exposure_time_den))
ed->exposure_time = (gdouble) exposure_time_nom / (double) exposure_time_den;
ed->fnumber = gexiv2_metadata_get_fnumber (metadata);
if (gexiv2_metadata_has_tag (metadata, "Exif.Image.Flash"))
flash = gexiv2_metadata_get_tag_long (metadata, "Exif.Image.Flash");
else if (gexiv2_metadata_has_tag (metadata, "Exif.Photo.Flash"))
flash = gexiv2_metadata_get_tag_long (metadata, "Exif.Photo.Flash");
if (flash != G_MAXLONG)
ed->flash = parse_flash ((gushort) flash);
ed->focal_length = gexiv2_metadata_get_focal_length (metadata);
if (gexiv2_metadata_has_tag (metadata, "Exif.Photo.ISOSpeedRatings"))
ed->iso_speed_ratings = (gdouble) gexiv2_metadata_get_iso_speed (metadata);
if (gexiv2_metadata_has_tag (metadata, "Exif.Image.MeteringMode"))
metering_mode = gexiv2_metadata_get_tag_long (metadata, "Exif.Image.MeteringMode");
else if (gexiv2_metadata_has_tag (metadata, "Exif.Photo.MeteringMode"))
metering_mode = gexiv2_metadata_get_tag_long (metadata, "Exif.Photo.MeteringMode");
if (metering_mode != G_MAXLONG)
ed->metering_mode = parse_metering_mode ((gushort) metering_mode);
if (gexiv2_metadata_has_tag (metadata, "Exif.Photo.WhiteBalance"))
white_balance = gexiv2_metadata_get_tag_long (metadata, "Exif.Photo.WhiteBalance");
if (white_balance != G_MAXLONG)
ed->white_balance = parse_white_balance ((gushort) white_balance);
ed->copyright = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.Copyright");
if (gexiv2_metadata_has_tag (metadata, "Exif.Image.ResolutionUnit"))
ed->resolution_unit = (gint) gexiv2_metadata_get_tag_long (metadata, "Exif.Image.ResolutionUnit");
ed->x_resolution = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.XResolution");
ed->y_resolution = gexiv2_metadata_get_tag_string (metadata, "Exif.Image.YResolution");
if (gexiv2_metadata_get_gps_altitude (metadata, &gps_altitude))
ed->gps_altitude = g_strdup_printf ("%f", gps_altitude);
if (gexiv2_metadata_get_gps_latitude (metadata, &gps_latitude))
ed->gps_latitude = g_strdup_printf ("%f", gps_latitude);
if (gexiv2_metadata_get_gps_longitude (metadata, &gps_longitude))
ed->gps_longitude = g_strdup_printf ("%f", gps_longitude);
ed->gps_direction = gexiv2_metadata_get_tag_string (metadata, "Exif.GPSInfo.GPSImgDirection");
out:
g_free (time);
g_free (time_original);
return ed;
}
G_MODULE_EXPORT gboolean
tracker_extract_get_metadata (TrackerExtractInfo *info)
{
GError *error;
GFile *file;
GExiv2Metadata *metadata = NULL;
GExiv2Orientation orientation;
RawExifData *ed = NULL;
TrackerResource *resource = NULL;
gboolean retval = FALSE;
const gchar *time_content_created;
gchar *filename = NULL;
gchar *nfo_orientation = NULL;
gchar *uri = NULL;
gint height;
gint width;
metadata = gexiv2_metadata_new ();
file = tracker_extract_info_get_file (info);
filename = g_file_get_path (file);
error = NULL;
if (!gexiv2_metadata_open_path (metadata, filename, &error)) {
g_warning ("Could not open %s for reading metadata: %s", filename, error->message);
g_error_free (error);
goto out;
}
resource = tracker_resource_new (NULL);
tracker_resource_add_uri (resource, "rdf:type", "nfo:Image");
tracker_resource_add_uri (resource, "rdf:type", "nmm:Photo");
width = gexiv2_metadata_get_pixel_width (metadata);
tracker_resource_set_int (resource, "nfo:width", width);
height = gexiv2_metadata_get_pixel_height (metadata);
tracker_resource_set_int (resource, "nfo:height", height);
orientation = gexiv2_metadata_get_orientation (metadata);
nfo_orientation = convert_exiv2_orientation_to_nfo (orientation);
tracker_resource_set_uri (resource, "nfo:orientation", nfo_orientation);
ed = parse_exif_data (metadata);
if (ed->make != NULL || ed->model != NULL) {
TrackerResource *equipment;
equipment = tracker_extract_new_equipment (ed->make, ed->model);
tracker_resource_add_relation (resource, "nfo:equipment", equipment);
g_object_unref (equipment);
}
uri = g_file_get_uri (file);
tracker_guarantee_resource_title_from_file (resource, "nie:title", ed->document_name, uri, NULL);
if (ed->copyright != NULL)
tracker_resource_set_string (resource, "nie:copyright", ed->copyright);
if (ed->white_balance != NULL)
tracker_resource_set_uri (resource, "nmm:whiteBalance", ed->white_balance);
if (ed->fnumber != -1.0)
tracker_resource_set_double (resource, "nmm:fnumber", ed->fnumber);
if (ed->flash != NULL)
tracker_resource_set_uri (resource, "nmm:flash", ed->flash);
if (ed->focal_length != -1.0)
tracker_resource_set_double (resource, "nmm:focalLength", ed->focal_length);
if (ed->artist != NULL) {
TrackerResource *artist;
artist = tracker_extract_new_contact (ed->artist);
tracker_resource_add_relation (resource, "nco:contributor", artist);
g_object_unref (artist);
}
if (ed->exposure_time != -1.0)
tracker_resource_set_double (resource, "nmm:exposureTime", ed->exposure_time);
if (ed->iso_speed_ratings != -1.0)
tracker_resource_set_double (resource, "nmm:isoSpeed", ed->iso_speed_ratings);
time_content_created = tracker_coalesce_strip (2, ed->time, ed->time_original);
tracker_guarantee_resource_date_from_file_mtime (resource, "nie:contentCreated", time_content_created, uri);
if (ed->description != NULL)
tracker_resource_set_string (resource, "nie:description", ed->description);
if (ed->metering_mode != NULL)
tracker_resource_set_uri (resource, "nmm:meteringMode", ed->metering_mode);
if (ed->user_comment != NULL)
tracker_guarantee_resource_utf8_string (resource, "nie:comment", ed->user_comment);
if (ed->gps_altitude != NULL || ed->gps_latitude != NULL || ed->gps_longitude != NULL) {
TrackerResource *location;
location = tracker_extract_new_location (NULL,
NULL,
NULL,
NULL,
ed->gps_altitude,
ed->gps_latitude,
ed->gps_longitude);
tracker_resource_add_relation (resource, "slo:location", location);
g_object_unref (location);
}
if (ed->gps_direction != NULL)
tracker_resource_set_string (resource, "nfo:heading", ed->gps_direction);
if (ed->x_resolution != NULL) {
gdouble value;
if (ed->resolution_unit == EXIF_RESOLUTION_UNIT_PER_CENTIMETER)
value = g_strtod (ed->x_resolution, NULL) * CMS_PER_INCH;
else
value = g_strtod (ed->x_resolution, NULL);
tracker_resource_set_double (resource, "nfo:horizontalResolution", value);
}
if (ed->y_resolution != NULL) {
gdouble value;
if (ed->resolution_unit == EXIF_RESOLUTION_UNIT_PER_CENTIMETER)
value = g_strtod (ed->y_resolution, NULL) * CMS_PER_INCH;
else
value = g_strtod (ed->y_resolution, NULL);
tracker_resource_set_double (resource, "nfo:verticalResolution", value);
}
tracker_extract_info_set_resource (info, resource);
retval = TRUE;
out:
g_clear_object (&metadata);
g_clear_object (&resource);
g_clear_pointer (&ed, (GDestroyNotify) raw_exif_data_free);
g_free (filename);
g_free (nfo_orientation);
g_free (uri);
return retval;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment