xmp: Add a new layer of indirection (GstXmpSchema)

Instead of storing all tags in a single hashtable, store them
grouped by schema in a GstXmpSchema, and add those to the toplevel
hashtable.
This commit is contained in:
Thiago Santos 2010-08-02 09:56:21 -03:00
parent 2760c96dfc
commit b9573cc75d

View file

@ -78,39 +78,62 @@ struct _PendingXmpTag
typedef struct _PendingXmpTag PendingXmpTag; typedef struct _PendingXmpTag PendingXmpTag;
/* /*
* Mappings from gstreamer tags to xmp tags * 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
* The mapping here are from a gstreamer tag (as a GQuark) * when serialized into xmp.
* into a GSList of GArray of XmpTag. * e.g. GST_TAG_GEO_LOCATION_ELEVATION needs to be mapped into 2 complementary
* * tags in the exif's schema. One of them stores the absolute elevation,
* There might be multiple xmp tags that a single gstreamer tag can be * and the other one stores if it is above of below sea level.
* mapped to. For example, GST_TAG_DATE might be mapped into dc:date
* or exif:DateTimeOriginal, hence the first list, to be able to store
* alternative mappings of the same gstreamer tag.
*
* Some other tags, like GST_TAG_GEO_LOCATION_ELEVATION needs to be
* mapped into 2 complementary tags in the exif's schema. One of them
* stores the absolute elevation, and the other one stores if it is
* above of below sea level. That's why we need a GArray as the item
* of each GSList in the mapping.
*/ */
static GHashTable *__xmp_tag_map; typedef GHashTable GstXmpSchema;
#define gst_xmp_schema_lookup g_hash_table_lookup
#define gst_xmp_schema_insert g_hash_table_insert
static GstXmpSchema *
gst_xmp_schema_new ()
{
return g_hash_table_new (g_direct_hash, g_direct_equal);
}
/*
* Mappings from schema names into the schema group of tags (GstXmpSchema)
*/
static GHashTable *__xmp_schemas;
static void static void
_xmp_tag_add_mapping (const gchar * gst_tag, GPtrArray * array) _gst_xmp_add_schema (const gchar * name, GstXmpSchema * schema)
{ {
GQuark key; GQuark key;
GSList *list = NULL;
key = g_quark_from_string (gst_tag); key = g_quark_from_string (name);
list = g_hash_table_lookup (__xmp_tag_map, GUINT_TO_POINTER (key)); if (g_hash_table_lookup (__xmp_schemas, GUINT_TO_POINTER (key))) {
list = g_slist_append (list, (gpointer) array); GST_WARNING ("Schema %s already exists, ignoring", name);
g_hash_table_insert (__xmp_tag_map, GUINT_TO_POINTER (key), list); g_assert_not_reached ();
return;
}
g_hash_table_insert (__xmp_schemas, GUINT_TO_POINTER (key), schema);
} }
static void static void
_xmp_tag_add_simple_mapping (const gchar * gst_tag, const gchar * xmp_tag, _gst_xmp_schema_add_mapping (GstXmpSchema * schema, const gchar * gst_tag,
GPtrArray * array)
{
GQuark key;
key = g_quark_from_string (gst_tag);
if (gst_xmp_schema_lookup (schema, GUINT_TO_POINTER (key))) {
GST_WARNING ("Tag %s already present for the schema", gst_tag);
g_assert_not_reached ();
return;
}
gst_xmp_schema_insert (schema, GUINT_TO_POINTER (key), array);
}
static void
_gst_xmp_schema_add_simple_mapping (GstXmpSchema * schema,
const gchar * gst_tag, const gchar * xmp_tag,
XmpSerializationFunc serialization_func, XmpSerializationFunc serialization_func,
XmpDeserializationFunc deserialization_func) XmpDeserializationFunc deserialization_func)
{ {
@ -125,7 +148,7 @@ _xmp_tag_add_simple_mapping (const gchar * gst_tag, const gchar * xmp_tag,
array = g_ptr_array_sized_new (1); array = g_ptr_array_sized_new (1);
g_ptr_array_add (array, xmpinfo); g_ptr_array_add (array, xmpinfo);
_xmp_tag_add_mapping (gst_tag, array); _gst_xmp_schema_add_mapping (schema, gst_tag, array);
} }
/* /*
@ -133,46 +156,47 @@ _xmp_tag_add_simple_mapping (const gchar * gst_tag, const gchar * xmp_tag,
* appended, and the API is not public, so we shouldn't * appended, and the API is not public, so we shouldn't
* have our lists modified during usage * have our lists modified during usage
*/ */
static GSList * static GPtrArray *
_xmp_tag_get_mapping (const gchar * gst_tag) _xmp_tag_get_mapping (const gchar * gst_tag)
{ {
GSList *ret = NULL; GPtrArray *ret = NULL;
GHashTableIter iter;
GQuark key = g_quark_from_string (gst_tag); GQuark key = g_quark_from_string (gst_tag);
gpointer iterkey, value;
ret = (GSList *) g_hash_table_lookup (__xmp_tag_map, GUINT_TO_POINTER (key)); g_hash_table_iter_init (&iter, __xmp_schemas);
while (!ret && g_hash_table_iter_next (&iter, &iterkey, &value)) {
GstXmpSchema *schema = (GstXmpSchema *) value;
ret = (GPtrArray *) gst_xmp_schema_lookup (schema, GUINT_TO_POINTER (key));
}
return ret; return ret;
} }
/* finds the gst tag that maps to this xmp tag */ /* finds the gst tag that maps to this xmp tag in this schema */
static const gchar * static const gchar *
_xmp_tag_get_mapping_reverse (const gchar * xmp_tag, XmpTag ** _xmp_tag) _gst_xmp_schema_get_mapping_reverse (GstXmpSchema * schema,
const gchar * xmp_tag, XmpTag ** _xmp_tag)
{ {
GHashTableIter iter; GHashTableIter iter;
gpointer key, value; gpointer key, value;
const gchar *ret = NULL; const gchar *ret = NULL;
GSList *walk;
gint index; gint index;
/* Iterate over the hashtable */ /* Iterate over the hashtable */
g_hash_table_iter_init (&iter, __xmp_tag_map); g_hash_table_iter_init (&iter, schema);
while (!ret && g_hash_table_iter_next (&iter, &key, &value)) { while (!ret && g_hash_table_iter_next (&iter, &key, &value)) {
GSList *list = (GSList *) value; GPtrArray *array = (GPtrArray *) value;
/* each entry might contain multiple mappigns */ /* each mapping might contain complementary tags */
for (walk = list; walk; walk = g_slist_next (walk)) { for (index = 0; index < array->len; index++) {
GPtrArray *array = (GPtrArray *) walk->data; XmpTag *xmpinfo = (XmpTag *) g_ptr_array_index (array, index);
/* each mapping might contain complementary tags */ if (strcmp (xmpinfo->tag_name, xmp_tag) == 0) {
for (index = 0; index < array->len; index++) { *_xmp_tag = xmpinfo;
XmpTag *xmpinfo = (XmpTag *) g_ptr_array_index (array, index); ret = g_quark_to_string (GPOINTER_TO_UINT (key));
goto out;
if (strcmp (xmpinfo->tag_name, xmp_tag) == 0) {
*_xmp_tag = xmpinfo;
ret = g_quark_to_string (GPOINTER_TO_UINT (key));
goto out;
}
} }
} }
} }
@ -181,6 +205,23 @@ out:
return ret; return ret;
} }
/* finds the gst tag that maps to this xmp tag (searches on all schemas) */
static const gchar *
_gst_xmp_tag_get_mapping_reverse (const gchar * xmp_tag, XmpTag ** _xmp_tag)
{
GHashTableIter iter;
gpointer key, value;
const gchar *ret = NULL;
/* Iterate over the hashtable */
g_hash_table_iter_init (&iter, __xmp_schemas);
while (!ret && g_hash_table_iter_next (&iter, &key, &value)) {
ret = _gst_xmp_schema_get_mapping_reverse ((GstXmpSchema *) value, xmp_tag,
_xmp_tag);
}
return ret;
}
/* utility functions/macros */ /* utility functions/macros */
#define METERS_PER_SECOND_TO_KILOMETERS_PER_HOUR (3.6) #define METERS_PER_SECOND_TO_KILOMETERS_PER_HOUR (3.6)
@ -700,42 +741,57 @@ _init_xmp_tag_map ()
{ {
GPtrArray *array; GPtrArray *array;
XmpTag *xmpinfo; XmpTag *xmpinfo;
GstXmpSchema *schema;
__xmp_tag_map = g_hash_table_new (g_direct_hash, g_direct_equal); __xmp_schemas = g_hash_table_new (g_direct_hash, g_direct_equal);
/* add the maps */ /* add the maps */
/* dublic code metadata /* dublic code metadata
* http://dublincore.org/documents/dces/ * http://dublincore.org/documents/dces/
*/ */
_xmp_tag_add_simple_mapping (GST_TAG_ARTIST, "dc:creator", NULL, NULL); schema = gst_xmp_schema_new ();
_xmp_tag_add_simple_mapping (GST_TAG_COPYRIGHT, "dc:rights", NULL, NULL); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_ARTIST,
_xmp_tag_add_simple_mapping (GST_TAG_DATE, "dc:date", NULL, NULL); "dc:creator", NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_DATE_TIME, "exif:DateTimeOriginal", NULL, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_COPYRIGHT,
NULL); "dc:rights", NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_DESCRIPTION, "dc:description", NULL, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE, "dc:date",
NULL); NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_KEYWORDS, "dc:subject", NULL, NULL); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DESCRIPTION,
_xmp_tag_add_simple_mapping (GST_TAG_TITLE, "dc:title", NULL, NULL); "dc:description", NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_KEYWORDS,
"dc:subject", NULL, NULL);
_gst_xmp_schema_add_simple_mapping (schema, GST_TAG_TITLE, "dc:title",
NULL, NULL);
/* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */ /* FIXME: we probably want GST_TAG_{,AUDIO_,VIDEO_}MIME_TYPE */
_xmp_tag_add_simple_mapping (GST_TAG_VIDEO_CODEC, "dc:format", NULL, NULL); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_VIDEO_CODEC,
"dc:format", NULL, NULL);
_gst_xmp_add_schema ("dc", schema);
/* xap (xmp) schema */ /* xap (xmp) schema */
_xmp_tag_add_simple_mapping (GST_TAG_USER_RATING, "xmp:Rating", NULL, schema = gst_xmp_schema_new ();
deserialize_xmp_rating); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_USER_RATING,
"xmp:Rating", NULL, deserialize_xmp_rating);
_gst_xmp_add_schema ("xmp", schema);
/* tiff */ /* tiff */
_xmp_tag_add_simple_mapping (GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", NULL, _gst_xmp_schema_add_simple_mapping (schema,
NULL); GST_TAG_DEVICE_MANUFACTURER, "tiff:Make", NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_DEVICE_MODEL, "tiff:Model", NULL, NULL); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DEVICE_MODEL,
_xmp_tag_add_simple_mapping (GST_TAG_IMAGE_ORIENTATION, "tiff:Orientation", "tiff:Model", NULL, NULL);
serialize_tiff_orientation, deserialize_tiff_orientation); _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_IMAGE_ORIENTATION,
"tiff:Orientation", serialize_tiff_orientation,
deserialize_tiff_orientation);
_gst_xmp_add_schema ("tiff", schema);
/* exif schema */ /* exif schema */
_xmp_tag_add_simple_mapping (GST_TAG_GEO_LOCATION_LATITUDE, _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_DATE_TIME,
"exif:GPSLatitude", serialize_exif_latitude, deserialize_exif_latitude); "exif:DateTimeOriginal", NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_GEO_LOCATION_LONGITUDE, _gst_xmp_schema_add_simple_mapping (schema,
"exif:GPSLongitude", serialize_exif_longitude, GST_TAG_GEO_LOCATION_LATITUDE, "exif:GPSLatitude",
deserialize_exif_longitude); 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);
/* compound exif tags */ /* compound exif tags */
array = g_ptr_array_sized_new (2); array = g_ptr_array_sized_new (2);
@ -749,7 +805,7 @@ _init_xmp_tag_map ()
xmpinfo->serialize = serialize_exif_altituderef; xmpinfo->serialize = serialize_exif_altituderef;
xmpinfo->deserialize = deserialize_exif_altitude; xmpinfo->deserialize = deserialize_exif_altitude;
g_ptr_array_add (array, xmpinfo); g_ptr_array_add (array, xmpinfo);
_xmp_tag_add_mapping (GST_TAG_GEO_LOCATION_ELEVATION, array); _gst_xmp_schema_add_mapping (schema, GST_TAG_GEO_LOCATION_ELEVATION, array);
array = g_ptr_array_sized_new (2); array = g_ptr_array_sized_new (2);
xmpinfo = g_slice_new (XmpTag); xmpinfo = g_slice_new (XmpTag);
@ -762,7 +818,8 @@ _init_xmp_tag_map ()
xmpinfo->serialize = serialize_exif_gps_speedref; xmpinfo->serialize = serialize_exif_gps_speedref;
xmpinfo->deserialize = deserialize_exif_gps_speed; xmpinfo->deserialize = deserialize_exif_gps_speed;
g_ptr_array_add (array, xmpinfo); g_ptr_array_add (array, xmpinfo);
_xmp_tag_add_mapping (GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, array); _gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, array);
array = g_ptr_array_sized_new (2); array = g_ptr_array_sized_new (2);
xmpinfo = g_slice_new (XmpTag); xmpinfo = g_slice_new (XmpTag);
@ -775,7 +832,8 @@ _init_xmp_tag_map ()
xmpinfo->serialize = serialize_exif_gps_directionref; xmpinfo->serialize = serialize_exif_gps_directionref;
xmpinfo->deserialize = deserialize_exif_gps_track; xmpinfo->deserialize = deserialize_exif_gps_track;
g_ptr_array_add (array, xmpinfo); g_ptr_array_add (array, xmpinfo);
_xmp_tag_add_mapping (GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, array); _gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, array);
array = g_ptr_array_sized_new (2); array = g_ptr_array_sized_new (2);
xmpinfo = g_slice_new (XmpTag); xmpinfo = g_slice_new (XmpTag);
@ -788,17 +846,21 @@ _init_xmp_tag_map ()
xmpinfo->serialize = serialize_exif_gps_directionref; xmpinfo->serialize = serialize_exif_gps_directionref;
xmpinfo->deserialize = deserialize_exif_gps_img_direction; xmpinfo->deserialize = deserialize_exif_gps_img_direction;
g_ptr_array_add (array, xmpinfo); g_ptr_array_add (array, xmpinfo);
_xmp_tag_add_mapping (GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, array); _gst_xmp_schema_add_mapping (schema,
GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, array);
_gst_xmp_add_schema ("exif", schema);
/* photoshop schema */ /* photoshop schema */
_xmp_tag_add_simple_mapping (GST_TAG_GEO_LOCATION_COUNTRY, _gst_xmp_schema_add_simple_mapping (schema,
"photoshop:Country", NULL, NULL); GST_TAG_GEO_LOCATION_COUNTRY, "photoshop:Country", NULL, NULL);
_xmp_tag_add_simple_mapping (GST_TAG_GEO_LOCATION_CITY, "photoshop:City", _gst_xmp_schema_add_simple_mapping (schema, GST_TAG_GEO_LOCATION_CITY,
NULL, NULL); "photoshop:City", NULL, NULL);
_gst_xmp_add_schema ("photoshop", schema);
/* iptc4xmpcore schema */ /* iptc4xmpcore schema */
_xmp_tag_add_simple_mapping (GST_TAG_GEO_LOCATION_SUBLOCATION, _gst_xmp_schema_add_simple_mapping (schema,
"Iptc4xmpCore:Location", NULL, NULL); GST_TAG_GEO_LOCATION_SUBLOCATION, "Iptc4xmpCore:Location", NULL, NULL);
_gst_xmp_add_schema ("Iptc4xmpCore", schema);
return NULL; return NULL;
} }
@ -1135,7 +1197,7 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
* check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL * check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL
* do 2 stage filter in tag_matches * do 2 stage filter in tag_matches
*/ */
gst_tag = _xmp_tag_get_mapping_reverse (as, &xmp_tag); gst_tag = _gst_xmp_tag_get_mapping_reverse (as, &xmp_tag);
if (gst_tag) { if (gst_tag) {
PendingXmpTag *ptag; PendingXmpTag *ptag;
@ -1169,7 +1231,7 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
if (strncmp (part, "rdf:", 4)) { if (strncmp (part, "rdf:", 4)) {
const gchar *parttag; const gchar *parttag;
parttag = _xmp_tag_get_mapping_reverse (part, &last_xmp_tag); parttag = _gst_xmp_tag_get_mapping_reverse (part, &last_xmp_tag);
if (parttag) { if (parttag) {
last_tag = parttag; last_tag = parttag;
} }
@ -1326,14 +1388,9 @@ write_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
GString *data = user_data; GString *data = user_data;
GPtrArray *xmp_tag_array = NULL; GPtrArray *xmp_tag_array = NULL;
char *s; char *s;
GSList *xmptaglist;
/* map gst-tag to xmp tag */ /* map gst-tag to xmp tag */
xmptaglist = _xmp_tag_get_mapping (tag); xmp_tag_array = _xmp_tag_get_mapping (tag);
if (xmptaglist) {
/* FIXME - we are always chosing the first tag mapped on the list */
xmp_tag_array = (GPtrArray *) xmptaglist->data;
}
if (!xmp_tag_array) { if (!xmp_tag_array) {
GST_WARNING ("no mapping for %s to xmp", tag); GST_WARNING ("no mapping for %s to xmp", tag);
@ -1451,3 +1508,6 @@ gst_tag_list_to_xmp_buffer (const GstTagList * list, gboolean read_only)
return buffer; return buffer;
} }
#undef gst_xmp_schema_lookup
#undef gst_xmp_schema_insert