--- /dev/null
+From 6e0213e903e1afe4fc65add8f5e1595057e646a7 Mon Sep 17 00:00:00 2001
+From: Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+Date: Tue, 19 Apr 2011 11:00:24 -0300
+Subject: [PATCH] tag: xmp: Add support for reading struct tags
+
+Adds a context variable that controls if the parsing is on
+'top level' tags or inside a struct tag.
+---
+ gst-libs/gst/tag/gstxmptag.c | 99 ++++++++++++++++++++++++++++++++++--------
+ 1 files changed, 81 insertions(+), 18 deletions(-)
+
+diff --git a/gst-libs/gst/tag/gstxmptag.c b/gst-libs/gst/tag/gstxmptag.c
+index 1a6e3c1..296804a 100644
+--- a/gst-libs/gst/tag/gstxmptag.c
++++ b/gst-libs/gst/tag/gstxmptag.c
+@@ -170,7 +170,6 @@ xmp_tag_get_type_name (XmpTag * xmptag)
+
+ struct _PendingXmpTag
+ {
+- const gchar *gst_tag;
+ XmpTag *xmp_tag;
+ gchar *str;
+ };
+@@ -1045,11 +1044,14 @@ struct _GstXmpNamespaceMap
+ /* parsing */
+
+ static void
+-read_one_tag (GstTagList * list, const gchar * tag, XmpTag * xmptag,
++read_one_tag (GstTagList * list, XmpTag * xmptag,
+ const gchar * v, GSList ** pending_tags)
+ {
+ GType tag_type;
+ GstTagMergeMode merge_mode;
++ const gchar *tag = xmptag->gst_tag;
++
++ g_return_if_fail (tag != NULL);
+
+ if (xmptag && xmptag->deserialize) {
+ xmptag->deserialize (xmptag, list, tag, xmptag->tag_name, v, pending_tags);
+@@ -1236,10 +1238,12 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+ gboolean in_tag;
+ gchar *part, *pp;
+ guint i;
+- const gchar *last_tag = NULL;
+ XmpTag *last_xmp_tag = NULL;
+ GSList *pending_tags = NULL;
+
++ /* Used for strucuture xmp tags */
++ XmpTag *context_tag = NULL;
++
+ GstXmpNamespaceMap ns_map[] = {
+ {"dc", NULL},
+ {"exif", NULL},
+@@ -1255,6 +1259,8 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+ g_return_val_if_fail (GST_BUFFER_SIZE (buffer) > 0, NULL);
+
++ GST_LOG ("Starting xmp parsing");
++
+ xps = (const gchar *) GST_BUFFER_DATA (buffer);
+ len = GST_BUFFER_SIZE (buffer);
+ xpe = &xps[len + 1];
+@@ -1354,19 +1360,41 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+ }
+ }
+ } else {
+- const gchar *gst_tag;
+ XmpTag *xmp_tag = NULL;
+ /* FIXME: eventualy rewrite ns
+ * find ':'
+ * check if ns before ':' is in ns_map and ns_map[i].gstreamer_ns!=NULL
+ * do 2 stage filter in tag_matches
+ */
+- gst_tag = _gst_xmp_tag_get_mapping_reverse (as, &xmp_tag);
+- if (gst_tag) {
++ if (context_tag) {
++ GSList *iter;
++
++ for (iter = context_tag->children; iter;
++ iter = g_slist_next (iter)) {
++ XmpTag *child = iter->data;
++
++ GST_DEBUG ("Looking at child tag %s : %s", child->tag_name,
++ as);
++ if (strcmp (child->tag_name, as) == 0) {
++ xmp_tag = child;
++ break;
++ }
++ }
++
++ } else {
++ GST_LOG ("Looking for tag: %s", as);
++ _gst_xmp_tag_get_mapping_reverse (as, &xmp_tag);
++ }
++ if (xmp_tag) {
+ PendingXmpTag *ptag;
+
++ GST_DEBUG ("Found xmp tag: %s -> %s", xmp_tag->tag_name,
++ xmp_tag->gst_tag);
++
++ /* we shouldn't find a xmp structure here */
++ g_assert (xmp_tag->gst_tag != NULL);
++
+ ptag = g_slice_new (PendingXmpTag);
+- ptag->gst_tag = gst_tag;
+ ptag->xmp_tag = xmp_tag;
+ ptag->str = g_strdup (v);
+
+@@ -1393,15 +1421,42 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+
+ /* skip rdf tags for now */
+ if (strncmp (part, "rdf:", 4)) {
+- const gchar *parttag;
++ /* if we're inside some struct, we look only on its children */
++ if (context_tag) {
++ GSList *iter;
++
++ /* check if this is the closing of the context */
++ if (part[0] == '/'
++ && strcmp (part + 1, context_tag->tag_name) == 0) {
++ GST_DEBUG ("Closing context tag %s", part);
++ context_tag = NULL;
++ } else {
++
++ for (iter = context_tag->children; iter;
++ iter = g_slist_next (iter)) {
++ XmpTag *child = iter->data;
+
+- parttag = _gst_xmp_tag_get_mapping_reverse (part, &last_xmp_tag);
+- if (parttag) {
+- last_tag = parttag;
++ GST_DEBUG ("Looking at child tag %s : %s", child->tag_name,
++ part);
++ if (strcmp (child->tag_name, part) == 0) {
++ last_xmp_tag = child;
++ break;
++ }
++ }
++ }
++
++ } else {
++ GST_LOG ("Looking for tag: %s", part);
++ _gst_xmp_tag_get_mapping_reverse (part, &last_xmp_tag);
++ if (last_xmp_tag && last_xmp_tag->type == GstXmpTagTypeStruct) {
++ context_tag = last_xmp_tag;
++ last_xmp_tag = NULL;
++ }
+ }
+ }
+ }
+ }
++ GST_LOG ("Next cycle");
+ /* next cycle */
+ ne++;
+ if (ne < xp2) {
+@@ -1421,15 +1476,23 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+ if (ns[0] != '\n' && &ns[1] <= ne) {
+ /* only log non-newline nodes, we still have to parse them */
+ GST_INFO ("txt: %s", part);
+- if (last_tag) {
++ if (last_xmp_tag) {
+ PendingXmpTag *ptag;
+
+- ptag = g_slice_new (PendingXmpTag);
+- ptag->gst_tag = last_tag;
+- ptag->xmp_tag = last_xmp_tag;
+- ptag->str = g_strdup (part);
++ GST_DEBUG ("Found tag %s -> %s", last_xmp_tag->tag_name,
++ last_xmp_tag->gst_tag);
++
++ if (last_xmp_tag->type == GstXmpTagTypeStruct) {
++ g_assert (context_tag == NULL); /* we can't handle struct nesting currently */
+
+- pending_tags = g_slist_append (pending_tags, ptag);
++ context_tag = last_xmp_tag;
++ } else {
++ ptag = g_slice_new (PendingXmpTag);
++ ptag->xmp_tag = last_xmp_tag;
++ ptag->str = g_strdup (part);
++
++ pending_tags = g_slist_append (pending_tags, ptag);
++ }
+ }
+ }
+ /* next cycle */
+@@ -1444,7 +1507,7 @@ gst_tag_list_from_xmp_buffer (const GstBuffer * buffer)
+
+ pending_tags = g_slist_delete_link (pending_tags, pending_tags);
+
+- read_one_tag (list, ptag->gst_tag, ptag->xmp_tag, ptag->str, &pending_tags);
++ read_one_tag (list, ptag->xmp_tag, ptag->str, &pending_tags);
+
+ g_free (ptag->str);
+ g_slice_free (PendingXmpTag, ptag);