#include <time.h>
#include <ctype.h>
+typedef struct _XmpTag XmpTag;
+
/*
* Serializes a GValue into a string.
*/
/*
* Deserializes @str that is the gstreamer tag @gst_tag represented in
- * XMP as the @xmp_tag and adds the result to the @taglist.
+ * XMP as the @xmp_tag_value and adds the result to the @taglist.
*
* @pending_tags is passed so that compound xmp tags can search for its
* complements on the list and use them. Note that used complements should
* be freed and removed from the list.
* The list is of PendingXmpTag
*/
-typedef void (*XmpDeserializationFunc) (GstTagList * taglist,
- const gchar * gst_tag, const gchar * xmp_tag,
+typedef void (*XmpDeserializationFunc) (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag_value,
const gchar * str, GSList ** pending_tags);
+
+#define GST_XMP_TAG_TYPE_SIMPLE 0
+#define GST_XMP_TAG_TYPE_BAG 1
+#define GST_XMP_TAG_TYPE_SEQ 2
struct _XmpTag
{
const gchar *tag_name;
+ gint type;
+
XmpSerializationFunc serialize;
XmpDeserializationFunc deserialize;
};
-typedef struct _XmpTag XmpTag;
+
+static GstTagMergeMode
+xmp_tag_get_merge_mode (XmpTag * xmptag)
+{
+ switch (xmptag->type) {
+ case GST_XMP_TAG_TYPE_BAG:
+ case GST_XMP_TAG_TYPE_SEQ:
+ return GST_TAG_MERGE_APPEND;
+ case GST_XMP_TAG_TYPE_SIMPLE:
+ default:
+ return GST_TAG_MERGE_KEEP;
+ }
+}
+
+static const gchar *
+xmp_tag_get_type_name (XmpTag * xmptag)
+{
+ switch (xmptag->type) {
+ case GST_XMP_TAG_TYPE_SEQ:
+ return "rdf:Seq";
+ default:
+ g_assert_not_reached ();
+ case GST_XMP_TAG_TYPE_BAG:
+ return "rdf:Bag";
+ }
+}
struct _PendingXmpTag
{
};
typedef struct _PendingXmpTag PendingXmpTag;
+
/*
* A schema is a mapping of strings (the tag name in gstreamer) to a list of
* tags in xmp (XmpTag). We need a list because some tags are split into 2
static void
_gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
- const gchar * gst_tag, const gchar * xmp_tag,
+ const gchar * gst_tag, const gchar * xmp_tag, gint xmp_type,
XmpSerializationFunc serialization_func,
XmpDeserializationFunc deserialization_func)
{
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = xmp_tag;
+ xmpinfo->type = xmp_type;
xmpinfo->serialize = serialization_func;
xmpinfo->deserialize = deserialization_func;
}
static void
-deserialize_exif_gps_coordinate (GstTagList * taglist, const gchar * gst_tag,
- const gchar * str, gchar pos, gchar neg)
+deserialize_exif_gps_coordinate (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * str, gchar pos, gchar neg)
{
gdouble value = 0;
gint d = 0, m = 0, s = 0;
goto error;
}
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, gst_tag, value, NULL);
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag), gst_tag, value,
+ NULL);
return;
error:
}
static void
-deserialize_exif_latitude (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_latitude (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
- deserialize_exif_gps_coordinate (taglist, gst_tag, str, 'N', 'S');
+ deserialize_exif_gps_coordinate (xmptag, taglist, gst_tag, str, 'N', 'S');
}
static void
-deserialize_exif_longitude (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_longitude (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
- deserialize_exif_gps_coordinate (taglist, gst_tag, str, 'E', 'W');
+ deserialize_exif_gps_coordinate (xmptag, taglist, gst_tag, str, 'E', 'W');
}
static gchar *
}
static void
-deserialize_exif_altitude (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_altitude (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
const gchar *altitude_str = NULL;
const gchar *altituderef_str = NULL;
}
/* add to the taglist */
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag),
GST_TAG_GEO_LOCATION_ELEVATION, value, NULL);
/* clean up entry */
}
static void
-deserialize_exif_gps_speed (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_gps_speed (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
const gchar *speed_str = NULL;
const gchar *speedref_str = NULL;
}
/* add to the taglist */
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE,
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag),
GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, value, NULL);
/* clean up entry */
}
static void
-deserialize_exif_gps_direction (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags,
- const gchar * direction_tag, const gchar * directionref_tag)
+deserialize_exif_gps_direction (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags, const gchar * direction_tag,
+ const gchar * directionref_tag)
{
const gchar *dir_str = NULL;
const gchar *dirref_str = NULL;
}
/* add to the taglist */
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, gst_tag, value, NULL);
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag), gst_tag, value,
+ NULL);
/* clean up entry */
g_free (ptag->str);
}
static void
-deserialize_exif_gps_track (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_gps_track (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
- deserialize_exif_gps_direction (taglist, gst_tag, xmp_tag, str, pending_tags,
- "exif:GPSTrack", "exif:GPSTrackRef");
+ deserialize_exif_gps_direction (xmptag, taglist, gst_tag, xmp_tag, str,
+ pending_tags, "exif:GPSTrack", "exif:GPSTrackRef");
}
static void
-deserialize_exif_gps_img_direction (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_exif_gps_img_direction (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
- deserialize_exif_gps_direction (taglist, gst_tag, xmp_tag, str, pending_tags,
- "exif:GPSImgDirection", "exif:GPSImgDirectionRef");
+ deserialize_exif_gps_direction (xmptag, taglist, gst_tag, xmp_tag, str,
+ pending_tags, "exif:GPSImgDirection", "exif:GPSImgDirectionRef");
}
static void
-deserialize_xmp_rating (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_xmp_rating (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
guint value;
return;
}
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, gst_tag, value, NULL);
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag), gst_tag, value,
+ NULL);
}
static gchar *
}
static void
-deserialize_tiff_orientation (GstTagList * taglist, const gchar * gst_tag,
- const gchar * xmp_tag, const gchar * str, GSList ** pending_tags)
+deserialize_tiff_orientation (XmpTag * xmptag, GstTagList * taglist,
+ const gchar * gst_tag, const gchar * xmp_tag, const gchar * str,
+ GSList ** pending_tags)
{
guint value;
const gchar *orientation = NULL;
orientation = gst_tag_image_orientation_from_exif_value (value);
if (orientation == NULL)
return;
- gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, gst_tag, orientation, NULL);
+ gst_tag_list_add (taglist, xmp_tag_get_merge_mode (xmptag), gst_tag,
+ orientation, NULL);
}
*/
schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_ARTIST,
- "dc:creator", NULL, NULL);
+ "dc:creator", GST_XMP_TAG_TYPE_SEQ, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_COPYRIGHT,
- "dc:rights", NULL, NULL);
+ "dc:rights", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE, "dc:date",
- NULL, NULL);
+ GST_XMP_TAG_TYPE_SEQ, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DESCRIPTION,
- "dc:description", NULL, NULL);
+ "dc:description", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_KEYWORDS,
- "dc:subject", NULL, NULL);
+ "dc:subject", GST_XMP_TAG_TYPE_BAG, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_TITLE, "dc:title",
- NULL, NULL);
+ GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
/* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_VIDEO_CODEC,
- "dc:format", NULL, NULL);
+ "dc:format", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_add_schema ("dc", schema);
/* xap (xmp) schema */
schema = gst_xmp_schema_new ();
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_USER_RATING,
- "xmp:Rating", NULL, deserialize_xmp_rating);
+ "xmp:Rating", GST_XMP_TAG_TYPE_SIMPLE, NULL, deserialize_xmp_rating);
_gst_xmp_add_schema ("xmp", schema);
/* tiff */
_gst_xmp_schema_add_simple_mapping (schema,
- GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", NULL, NULL);
+ GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", GST_XMP_TAG_TYPE_SIMPLE, NULL,
+ NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DEVICE_MODEL,
- "tiff:Model", NULL, NULL);
+ "tiff:Model", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_IMAGE_ORIENTATION,
- "tiff:Orientation", serialize_tiff_orientation,
+ "tiff:Orientation", GST_XMP_TAG_TYPE_SIMPLE, serialize_tiff_orientation,
deserialize_tiff_orientation);
_gst_xmp_add_schema ("tiff", schema);
/* exif schema */
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE_TIME,
- "exif:DateTimeOriginal", NULL, NULL);
+ "exif:DateTimeOriginal", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema,
GST_TAG_GEO_LOCATION_LATITUDE, "exif:GPSLatitude",
- serialize_exif_latitude, deserialize_exif_latitude);
- _gst_xmp_schema_add_simple_mapping (schema,
- GST_TAG_GEO_LOCATION_LONGITUDE, "exif:GPSLongitude",
- serialize_exif_longitude, deserialize_exif_longitude);
+ GST_XMP_TAG_TYPE_SIMPLE, serialize_exif_latitude,
+ deserialize_exif_latitude);
+ _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_LONGITUDE,
+ "exif:GPSLongitude", GST_XMP_TAG_TYPE_SIMPLE, serialize_exif_longitude,
+ deserialize_exif_longitude);
/* compound exif tags */
array = g_ptr_array_sized_new (2);
xmpinfo->tag_name = "exif:GPSAltitude";
xmpinfo->serialize = serialize_exif_altitude;
xmpinfo->deserialize = deserialize_exif_altitude;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSAltitudeRef";
xmpinfo->serialize = serialize_exif_altituderef;
xmpinfo->deserialize = deserialize_exif_altitude;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema, GST_TAG_GEO_LOCATION_ELEVATION, array);
xmpinfo->tag_name = "exif:GPSSpeed";
xmpinfo->serialize = serialize_exif_gps_speed;
xmpinfo->deserialize = deserialize_exif_gps_speed;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSSpeedRef";
xmpinfo->serialize = serialize_exif_gps_speedref;
xmpinfo->deserialize = deserialize_exif_gps_speed;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, array);
xmpinfo->tag_name = "exif:GPSTrack";
xmpinfo->serialize = serialize_exif_gps_direction;
xmpinfo->deserialize = deserialize_exif_gps_track;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSTrackRef";
xmpinfo->serialize = serialize_exif_gps_directionref;
xmpinfo->deserialize = deserialize_exif_gps_track;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, array);
xmpinfo->tag_name = "exif:GPSImgDirection";
xmpinfo->serialize = serialize_exif_gps_direction;
xmpinfo->deserialize = deserialize_exif_gps_img_direction;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
xmpinfo = g_slice_new (XmpTag);
xmpinfo->tag_name = "exif:GPSImgDirectionRef";
xmpinfo->serialize = serialize_exif_gps_directionref;
xmpinfo->deserialize = deserialize_exif_gps_img_direction;
+ xmpinfo->type = GST_XMP_TAG_TYPE_SIMPLE;
g_ptr_array_add (array, xmpinfo);
_gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, array);
/* photoshop schema */
_gst_xmp_schema_add_simple_mapping (schema,
- GST_TAG_GEO_LOCATION_COUNTRY, "photoshop:Country", NULL, NULL);
+ GST_TAG_GEO_LOCATION_COUNTRY, "photoshop:Country",
+ GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_CITY,
- "photoshop:City", NULL, NULL);
+ "photoshop:City", GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_add_schema ("photoshop", schema);
/* iptc4xmpcore schema */
_gst_xmp_schema_add_simple_mapping (schema,
- GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location", NULL, NULL);
+ GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location",
+ GST_XMP_TAG_TYPE_SIMPLE, NULL, NULL);
_gst_xmp_add_schema ("Iptc4xmpCore", schema);
return NULL;
const gchar * v, GSList ** pending_tags)
{
GType tag_type;
+ GstTagMergeMode merge_mode;
if (xmptag && xmptag->deserialize) {
- xmptag->deserialize (list, tag, xmptag->tag_name, v, pending_tags);
+ xmptag->deserialize (xmptag, list, tag, xmptag->tag_name, v, pending_tags);
return;
}
+ merge_mode = xmp_tag_get_merge_mode (xmptag);
tag_type = gst_tag_get_type (tag);
/* add gstreamer tag depending on type */
switch (tag_type) {
case G_TYPE_STRING:{
- gst_tag_list_add (list, GST_TAG_MERGE_APPEND, tag, v, NULL);
+ gst_tag_list_add (list, merge_mode, tag, v, NULL);
break;
}
default:
}
if (datetime) {
- gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, datetime, NULL);
+ gst_tag_list_add (list, merge_mode, tag, datetime, NULL);
gst_date_time_unref (datetime);
}
date = g_date_new ();
g_date_set_parse (date, v);
if (g_date_valid (date)) {
- gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag,
+ gst_tag_list_add (list, merge_mode, tag,
date, NULL);
} else {
GST_WARNING ("unparsable date: '%s'", v);
/* poor mans straw */
sscanf (v, "%04d-%02d-%02dT", &y, &m, &d);
date = g_date_new_dmy (d, m, y);
- gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, date, NULL);
+ gst_tag_list_add (list, merge_mode, tag, date, NULL);
g_date_free (date);
} else {
GST_WARNING ("unhandled type for %s from xmp", tag);
string_open_tag (data, xmp_tag->tag_name);
/* fast path for single valued tag */
- if (ct == 1) {
+ if (ct == 1 || xmp_tag->type == GST_XMP_TAG_TYPE_SIMPLE) {
if (xmp_tag->serialize) {
s = xmp_tag->serialize (gst_tag_list_get_value_index (list, tag, 0));
} else {
GST_WARNING ("unhandled type for %s to xmp", tag);
}
} else {
- string_open_tag (data, "rdf:Bag");
+ const gchar *typename;
+
+ typename = xmp_tag_get_type_name (xmp_tag);
+
+ string_open_tag (data, typename);
for (i = 0; i < ct; i++) {
GST_DEBUG ("mapping %s[%u/%u] to xmp", tag, i, ct);
if (xmp_tag->serialize) {
GST_WARNING ("unhandled type for %s to xmp", tag);
}
}
- string_close_tag (data, "rdf:Bag");
+ string_close_tag (data, typename);
}
string_close_tag (data, xmp_tag->tag_name);