From 78193dfe71fc3c186df4b6fd5f504bd05ae7980f Mon Sep 17 00:00:00 2001 From: =?utf8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?utf8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Sat, 25 Aug 2012 02:26:29 +0400 Subject: [PATCH] matroskademux: Support recursive SimpleTags Fixes #682644 Depends on #682615 --- gst/matroska/matroska-read-common.c | 144 +++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 4 deletions(-) diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index c211b17..00dddea 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -1779,7 +1779,7 @@ gst_matroska_read_common_parse_info (GstMatroskaReadCommon * common, static GstFlowReturn gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * - common, GstEbmlRead * ebml, GstTagList ** p_taglist) + common, GstEbmlRead * ebml, GstTagList ** p_taglist, gchar * parent) { /* FIXME: check if there are more useful mappings */ static const struct @@ -1921,10 +1921,33 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * GST_MATROSKA_TAG_ID_LICENSE, GST_TAG_LICENSE}, { /* The license applied to the content (like Creative Commons variants). */ GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE} }; + static const struct + { + const gchar *matroska_tagname; + const gchar *gstreamer_tagname; + } + child_tag_conv[] = { + { + "TITLE/SORT_WITH=", GST_TAG_TITLE_SORTNAME}, { + "ARTIST/SORT_WITH=", GST_TAG_ARTIST_SORTNAME}, { + /* ALBUM-stuff is handled elsewhere */ + "COMPOSER/SORT_WITH=", GST_TAG_TITLE_SORTNAME}, { + "ORIGINAL/URL=", GST_TAG_LOCATION}, { + /* EMAIL, PHONE, FAX all can be mapped to GST_TAG_CONTACT, there is special + * code for that later. + */ + "TITLE/URL=", GST_TAG_HOMEPAGE}, { + "ARTIST/URL=", GST_TAG_HOMEPAGE}, { + "COPYRIGHT/URL=", GST_TAG_COPYRIGHT_URI}, { + "LICENSE/URL=", GST_TAG_LICENSE_URI}, { + "LICENSE/URL=", GST_TAG_LICENSE_URI} + }; GstFlowReturn ret; guint32 id; gchar *value = NULL; gchar *tag = NULL; + gchar *name_with_parent = NULL; + GstTagList *child_taglist = NULL; DEBUG_ELEMENT_START (common, ebml, "SimpleTag"); @@ -1933,6 +1956,11 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * return ret; } + if (parent) + child_taglist = *p_taglist; + else + child_taglist = gst_tag_list_new_empty (); + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { /* read all sub-entries */ @@ -1945,6 +1973,11 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * tag = NULL; ret = gst_ebml_read_ascii (ebml, &id, &tag); GST_DEBUG_OBJECT (common, "TagName: %s", GST_STR_NULL (tag)); + g_free (name_with_parent); + if (parent != NULL) + name_with_parent = g_strdup_printf ("%s/%s", parent, tag); + else + name_with_parent = g_strdup (tag); break; case GST_MATROSKA_ID_TAGSTRING: @@ -1954,11 +1987,23 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * GST_DEBUG_OBJECT (common, "TagString: %s", GST_STR_NULL (value)); break; + case GST_MATROSKA_ID_SIMPLETAG: + /* Recursive SimpleTag */ + /* This implementation requires tag name of _this_ tag to be known + * in order to read its children. It's not in the spec, just the way + * the code is written. + */ + if (name_with_parent != NULL) { + ret = gst_matroska_read_common_parse_metadata_id_simple_tag (common, + ebml, &child_taglist, name_with_parent); + break; + } + /* fall-through */ + default: ret = gst_matroska_read_common_parse_skip (common, ebml, "SimpleTag", id); break; - /* fall-through */ case GST_MATROSKA_ID_TAGLANGUAGE: case GST_MATROSKA_ID_TAGDEFAULT: @@ -1970,7 +2015,14 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * DEBUG_ELEMENT_STOP (common, ebml, "SimpleTag", ret); - if (tag && value) { + if (parent && tag && value) { + /* Don't bother mapping children tags - parent will do that */ + gchar *key_val; + /* TODO: read LANGUAGE sub-tag, and use "key[lc]=val" form */ + key_val = g_strdup_printf ("%s=%s", name_with_parent, value); + gst_tag_list_add (*p_taglist, GST_TAG_MERGE_APPEND, + GST_TAG_EXTENDED_COMMENT, key_val, NULL); + } else if (tag && value) { gboolean matched = FALSE; guint i; @@ -2017,8 +2069,92 @@ gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * } } + if (!parent) { + /* Map children tags. This only supports top-anchored mapping. That is, + * we start at toplevel tag (this tag), and see how its combinations + * with its children can be mapped. Which means that grandchildren + * are also combined here, with _this_ tag taken into consideration. + * If grandchildren can be combined only with children, that combination + * will not happen. + */ + gint child_tags_n = gst_tag_list_n_tags (child_taglist); + if (child_tags_n > 0) { + gint i; + for (i = 0; i < child_tags_n; i++) { + gint j; + const gchar *child_name = gst_tag_list_nth_tag_name (child_taglist, i); + guint taglen = gst_tag_list_get_tag_size (child_taglist, child_name); + for (j = 0; j < taglen; j++) { + gchar *val; + gboolean matched = FALSE; + gchar *val_pre, *val_post; + gint k; + + if (!gst_tag_list_get_string_index (child_taglist, child_name, + j, &val)) + continue; + if (!strchr (val, '=')) { + g_free (val); + continue; + } + val_post = g_strdup (strchr (val, '=') + 1); + val_pre = g_strdup (val); + *(strchr (val_pre, '=') + 1) = '\0'; + + for (k = 0; !matched && k < G_N_ELEMENTS (child_tag_conv); k++) { + const gchar *tagname_gst = child_tag_conv[k].gstreamer_tagname; + + const gchar *tagname_mkv = child_tag_conv[k].matroska_tagname; + + /* TODO: Once "key[lc]=value" form support is implemented, + * strip [lc] here. It can't be used in combined tags. + * If a tag is not combined, leave [lc] as it is. + */ + if (strcmp (tagname_mkv, val_pre) == 0) { + GValue dest = { 0, }; + GType dest_type = gst_tag_get_type (tagname_gst); + + g_value_init (&dest, dest_type); + if (gst_value_deserialize (&dest, val_post)) { + gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, + tagname_gst, &dest, NULL); + } else { + GST_WARNING_OBJECT (common, "Can't transform complex tag '%s' " + "to target type '%s'", val, g_type_name (dest_type)); + } + g_value_unset (&dest); + matched = TRUE; + } + } + if (!matched) { + gchar *last_slash = strrchr (val_pre, '/'); + if (last_slash) { + last_slash++; + if (strcmp (last_slash, "EMAIL=") == 0 || + strcmp (last_slash, "PHONE=") == 0 || + strcmp (last_slash, "ADDRESS=") == 0 || + strcmp (last_slash, "FAX=") == 0) { + gst_tag_list_add (*p_taglist, GST_TAG_MERGE_APPEND, + GST_TAG_CONTACT, val_post, NULL); + matched = TRUE; + } + } + } + if (!matched) + gst_tag_list_add (*p_taglist, GST_TAG_MERGE_APPEND, + GST_TAG_EXTENDED_COMMENT, val, NULL); + g_free (val_post); + g_free (val_pre); + g_free (val); + } + } + } + gst_tag_list_unref (child_taglist); + } + g_free (tag); g_free (value); + g_free (name_with_parent); return ret; } @@ -2188,7 +2324,7 @@ gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common, switch (id) { case GST_MATROSKA_ID_SIMPLETAG: ret = gst_matroska_read_common_parse_metadata_id_simple_tag (common, - ebml, &taglist); + ebml, &taglist, NULL); break; case GST_MATROSKA_ID_TARGETS: -- 2.7.4