From e7c6df131bbcbe19fa35b78a588239954dde39fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 17 Aug 2006 16:52:06 +0000 Subject: [PATCH] gst-libs/gst/tag/gstvorbistag.c: Serialise unknown vorbis comments into GST_TAG_EXTENDED_COMMENT tags and deserialise... Original commit message from CVS: * gst-libs/gst/tag/gstvorbistag.c: (gst_vorbis_tag_add), (gst_tag_to_vorbis_comments): Serialise unknown vorbis comments into GST_TAG_EXTENDED_COMMENT tags and deserialise them properly as well (#351768). Add some more gtk-doc blurbs and also some g_return_if_fail(). * tests/check/libs/tag.c: (GST_START_TEST), (back_to_vorbis_comments), (taglists_are_equal), (tag_suite): More tests. --- ChangeLog | 12 ++ gst-libs/gst/tag/gstvorbistag.c | 92 ++++++++-- tests/check/libs/tag.c | 295 +++++++++++++++++++++++++++++++- 3 files changed, 381 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2aca2fed84..b68e37d5e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2006-08-17 Tim-Philipp Müller + + * gst-libs/gst/tag/gstvorbistag.c: (gst_vorbis_tag_add), + (gst_tag_to_vorbis_comments): + Serialise unknown vorbis comments into GST_TAG_EXTENDED_COMMENT + tags and deserialise them properly as well (#351768). + Add some more gtk-doc blurbs and also some g_return_if_fail(). + + * tests/check/libs/tag.c: (GST_START_TEST), + (back_to_vorbis_comments), (taglists_are_equal), (tag_suite): + More tests. + 2006-08-17 Wim Taymans * ext/ogg/Makefile.am: diff --git a/gst-libs/gst/tag/gstvorbistag.c b/gst-libs/gst/tag/gstvorbistag.c index 841e94da7a..916d9c7fd7 100644 --- a/gst-libs/gst/tag/gstvorbistag.c +++ b/gst-libs/gst/tag/gstvorbistag.c @@ -130,14 +130,43 @@ gst_tag_to_vorbis_tag (const gchar * gst_tag) } +/** + * gst_vorbis_tag_add: + * @list: a #GstTagList + * @tag: a vorbiscomment tag string (key in key=value), must be valid UTF-8 + * @val: a vorbiscomment value string (value in key=value), must be valid UTF-8 + * + * Convenience function using gst_tag_from_vorbis_tag(), parsing + * a vorbis comment string into the right type and adding it to the + * given taglist @list. + * + * Unknown vorbiscomment tags will be added to the tag list in form + * of a #GST_TAG_EXTENDED_COMMENT (since 0.10.10 at least). + */ void gst_vorbis_tag_add (GstTagList * list, const gchar * tag, const gchar * value) { - const gchar *gst_tag = gst_tag_from_vorbis_tag (tag); + const gchar *gst_tag; GType tag_type; - if (gst_tag == NULL) + g_return_if_fail (list != NULL); + g_return_if_fail (tag != NULL); + g_return_if_fail (value != NULL); + + g_return_if_fail (g_utf8_validate (tag, -1, NULL)); + g_return_if_fail (g_utf8_validate (value, -1, NULL)); + g_return_if_fail (strchr (tag, '=') == NULL); + + gst_tag = gst_tag_from_vorbis_tag (tag); + if (gst_tag == NULL) { + gchar *ext_comment; + + ext_comment = g_strdup_printf ("%s=%s", tag, value); + gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_EXTENDED_COMMENT, + ext_comment, NULL); + g_free (ext_comment); return; + } tag_type = gst_tag_get_type (gst_tag); switch (tag_type) { @@ -177,22 +206,18 @@ gst_vorbis_tag_add (GstTagList * list, const gchar * tag, const gchar * value) if (strcmp (tag, "LANGUAGE") == 0) { const gchar *s = strchr (value, '['); - /* FIXME: gsttaglist.h says our language tag contains ISO-639-1 - * codes, which are 2 letter codes. The code below extracts 3-letter - * identifiers, which would be ISO-639-2. Mixup? Oversight? Wrong core - * docs? What do files in the wild contain? (tpm) */ + /* Accept both ISO-639-1 and ISO-639-2 codes */ if (s && strchr (s, ']') == s + 4) { valid = g_strndup (s + 1, 3); + } else if (s && strchr (s, ']') == s + 3) { + valid = g_strndup (s + 1, 2); + } else if (strlen (value) != 2 && strlen (value) != 3) { + GST_WARNING ("doesn't contain an ISO-639 language code: %s", value); } } if (!valid) { - if (!g_utf8_validate (value, -1, (const gchar **) &valid)) { - valid = g_strndup (value, valid - value); - GST_DEBUG ("Invalid vorbis comment tag, truncated it to %s", valid); - } else { - valid = g_strdup (value); - } + valid = g_strdup (value); } gst_tag_list_add (list, GST_TAG_MERGE_APPEND, gst_tag, valid, NULL); g_free (valid); @@ -322,15 +347,32 @@ typedef struct } MyForEach; +/** + * gst_tag_to_vorbis_comments: + * @list: a #GstTagList + * @tag: a GStreamer tag identifier, such as #GST_TAG_ARTIST + * + * Creates a new tag list that contains the information parsed out of a + * vorbiscomment packet. + * + * Returns: A #GList of newly-allowcated key=value strings. Free with + * g_list_foreach (list, (GFunc) g_free, NULL) plus g_list_free (list) + */ GList * gst_tag_to_vorbis_comments (const GstTagList * list, const gchar * tag) { + const gchar *vorbis_tag = NULL; GList *l = NULL; guint i; - const gchar *vorbis_tag = gst_tag_to_vorbis_tag (tag); - if (!vorbis_tag) - return NULL; + g_return_val_if_fail (list != NULL, NULL); + g_return_val_if_fail (tag != NULL, NULL); + + if (strcmp (tag, GST_TAG_EXTENDED_COMMENT) != 0) { + vorbis_tag = gst_tag_to_vorbis_tag (tag); + if (!vorbis_tag) + return NULL; + } for (i = 0; i < gst_tag_list_get_tag_size (list, tag); i++) { GType tag_type = gst_tag_get_type (tag); @@ -346,11 +388,25 @@ gst_tag_to_vorbis_comments (const GstTagList * list, const gchar * tag) break; } case G_TYPE_STRING:{ - gchar *str; + gchar *str = NULL; if (!gst_tag_list_get_string_index (list, tag, i, &str)) g_return_val_if_reached (NULL); - result = g_strdup_printf ("%s=%s", vorbis_tag, str); + + /* special case: GST_TAG_EXTENDED_COMMENT */ + if (vorbis_tag == NULL) { + gchar *key = NULL, *val = NULL; + + if (gst_tag_parse_extended_comment (str, &key, NULL, &val, TRUE)) { + result = g_strdup_printf ("%s=%s", key, val); + g_free (key); + g_free (val); + } else { + GST_WARNING ("Not a valid extended comment string: %s", str); + } + } else { + result = g_strdup_printf ("%s=%s", vorbis_tag, str); + } g_free (str); break; } @@ -404,6 +460,8 @@ write_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data) data->data_count += strlen (result); data->entries = g_list_prepend (data->entries, result); } + + g_list_free (comments); } /** diff --git a/tests/check/libs/tag.c b/tests/check/libs/tag.c index 67624e2010..4feaac0a2e 100644 --- a/tests/check/libs/tag.c +++ b/tests/check/libs/tag.c @@ -133,6 +133,297 @@ GST_START_TEST (test_parse_extended_comment) GST_END_TEST; +#define ASSERT_TAG_LIST_HAS_STRING(list,field,string) \ + { \ + gboolean got_match = FALSE; \ + guint i, size; \ + \ + fail_unless (gst_tag_list_get_tag_size (list,field) > 0); \ + size = gst_tag_list_get_tag_size (list,field); \ + for (i = 0; i < size; ++i) { \ + gchar *___s = NULL; \ + \ + fail_unless (gst_tag_list_get_string_index (list, field, i, &___s)); \ + fail_unless (___s != NULL); \ + if (g_str_equal (___s, string)) { \ + got_match = TRUE; \ + g_free (___s); \ + break; \ + } \ + g_free (___s); \ + } \ + fail_unless (got_match); \ + } + +#define ASSERT_TAG_LIST_HAS_UINT(list,field,num) \ + { \ + guint ___n; \ + \ + fail_unless (gst_tag_list_get_tag_size (list,field) > 0); \ + fail_unless (gst_tag_list_get_tag_size (list,field) == 1); \ + fail_unless (gst_tag_list_get_uint_index (list, field, 0, &___n)); \ + fail_unless_equals_int (___n, num); \ + } + +GST_START_TEST (test_muscibrainz_tag_registration) +{ + GstTagList *list; + + gst_tag_register_musicbrainz_tags (); + + list = gst_tag_list_new (); + + /* musicbrainz tags aren't registered yet */ + gst_vorbis_tag_add (list, "MUSICBRAINZ_TRACKID", "123456"); + gst_vorbis_tag_add (list, "MUSICBRAINZ_ARTISTID", "234567"); + gst_vorbis_tag_add (list, "MUSICBRAINZ_ALBUMID", "345678"); + gst_vorbis_tag_add (list, "MUSICBRAINZ_ALBUMARTISTID", "4567890"); + gst_vorbis_tag_add (list, "MUSICBRAINZ_TRMID", "5678901"); + gst_vorbis_tag_add (list, "MUSICBRAINZ_SORTNAME", "Five, 678901"); + + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_TRACKID, "123456"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_ARTISTID, "234567"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_ALBUMID, "345678"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_ALBUMARTISTID, + "4567890"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_TRMID, "5678901"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_MUSICBRAINZ_SORTNAME, + "Five, 678901"); + + gst_tag_list_free (list); +} + +GST_END_TEST; + +/* is there an easier way to compare two structures / tagslists? */ +static gboolean +taglists_are_equal (const GstTagList * list_1, const GstTagList * list_2) +{ + GstCaps *c_list_1 = gst_caps_new_empty (); + GstCaps *c_list_2 = gst_caps_new_empty (); + gboolean ret; + + gst_caps_append_structure (c_list_1, + gst_structure_copy ((GstStructure *) list_1)); + gst_caps_append_structure (c_list_2, + gst_structure_copy ((GstStructure *) list_2)); + + ret = gst_caps_is_equal (c_list_2, c_list_1); + + gst_caps_unref (c_list_1); + gst_caps_unref (c_list_2); +} + +GST_START_TEST (test_vorbis_tags) +{ + GstTagList *list; + + list = gst_tag_list_new (); + + /* NULL pointers aren't allowed */ + ASSERT_CRITICAL (gst_vorbis_tag_add (NULL, "key", "value")); + ASSERT_CRITICAL (gst_vorbis_tag_add (list, NULL, "value")); + ASSERT_CRITICAL (gst_vorbis_tag_add (list, "key", NULL)); + + /* must be UTF-8 */ + ASSERT_CRITICAL (gst_vorbis_tag_add (list, "key", "v\777lue")); + ASSERT_CRITICAL (gst_vorbis_tag_add (list, "k\777y", "value")); + + /* key can't have a '=' in it */ + ASSERT_CRITICAL (gst_vorbis_tag_add (list, "k=y", "value")); + ASSERT_CRITICAL (gst_vorbis_tag_add (list, "key=", "value")); + + /* should be allowed in values though */ + gst_vorbis_tag_add (list, "keeey", "va=ue"); + + /* add some tags */ + gst_vorbis_tag_add (list, "TITLE", "Too"); + gst_vorbis_tag_add (list, "ALBUM", "Aoo"); + gst_vorbis_tag_add (list, "ARTIST", "Alboo"); + gst_vorbis_tag_add (list, "PERFORMER", "Perfoo"); + gst_vorbis_tag_add (list, "COPYRIGHT", "Copyfoo"); + gst_vorbis_tag_add (list, "DESCRIPTION", "Descoo"); + gst_vorbis_tag_add (list, "LICENSE", "Licoo"); + gst_vorbis_tag_add (list, "ORGANIZATION", "Orgoo"); + gst_vorbis_tag_add (list, "GENRE", "Goo"); + gst_vorbis_tag_add (list, "CONTACT", "Coo"); + gst_vorbis_tag_add (list, "COMMENT", "Stroodle is good"); + gst_vorbis_tag_add (list, "COMMENT", "Peroxysulfid stroodles the brain"); + + gst_vorbis_tag_add (list, "TRACKNUMBER", "5"); + gst_vorbis_tag_add (list, "TRACKTOTAL", "77"); + gst_vorbis_tag_add (list, "DISCNUMBER", "1"); + gst_vorbis_tag_add (list, "DISCTOTAL", "2"); + gst_vorbis_tag_add (list, "DATE", "1954-12-31"); + + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_TITLE, "Too"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_ALBUM, "Aoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_ARTIST, "Alboo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_PERFORMER, "Perfoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_COPYRIGHT, "Copyfoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_DESCRIPTION, "Descoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LICENSE, "Licoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_ORGANIZATION, "Orgoo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_GENRE, "Goo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_CONTACT, "Coo"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_COMMENT, + "Peroxysulfid stroodles the brain"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_COMMENT, "Stroodle is good"); + + ASSERT_TAG_LIST_HAS_UINT (list, GST_TAG_TRACK_NUMBER, 5); + ASSERT_TAG_LIST_HAS_UINT (list, GST_TAG_TRACK_COUNT, 77); + ASSERT_TAG_LIST_HAS_UINT (list, GST_TAG_ALBUM_VOLUME_NUMBER, 1); + ASSERT_TAG_LIST_HAS_UINT (list, GST_TAG_ALBUM_VOLUME_COUNT, 2); + + { + GDate *date = NULL; + + fail_unless (gst_tag_list_get_date (list, GST_TAG_DATE, &date)); + fail_unless (date != NULL); + fail_unless (g_date_get_day (date) == 31); + fail_unless (g_date_get_month (date) == G_DATE_DECEMBER); + fail_unless (g_date_get_year (date) == 1954); + + g_date_free (date); + } + + /* unknown vorbis comments should go into a GST_TAG_EXTENDED_COMMENT */ + gst_vorbis_tag_add (list, "CoEdSub_ID", "98172AF-973-10-B"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_EXTENDED_COMMENT, + "CoEdSub_ID=98172AF-973-10-B"); + gst_vorbis_tag_add (list, "RuBuWuHash", "1337BA42F91"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_EXTENDED_COMMENT, + "RuBuWuHash=1337BA42F91"); + +#if 0 + /* TODO: test these as well */ + { + GST_TAG_TRACK_GAIN, "REPLAYGAIN_TRACK_GAIN"}, { + GST_TAG_TRACK_PEAK, "REPLAYGAIN_TRACK_PEAK"}, { + GST_TAG_ALBUM_GAIN, "REPLAYGAIN_ALBUM_GAIN"}, { + GST_TAG_ALBUM_PEAK, "REPLAYGAIN_ALBUM_PEAK"}, { + GST_TAG_LANGUAGE_CODE, "LANGUAGE"}, +#endif + /* make sure we can convert back and forth without loss */ + { + GstTagList *new_list, *even_newer_list; + GstBuffer *buf, *buf2; + gchar *vendor_id = NULL; + + buf = gst_tag_list_to_vorbiscomment_buffer (list, + (const guint8 *) "\003vorbis", 7, "libgstunittest"); + fail_unless (buf != NULL); + new_list = gst_tag_list_from_vorbiscomment_buffer (buf, + (const guint8 *) "\003vorbis", 7, &vendor_id); + fail_unless (new_list != NULL); + fail_unless (vendor_id != NULL); + g_free (vendor_id); + vendor_id = NULL; + + GST_LOG ("new_list = %" GST_PTR_FORMAT, new_list); + fail_unless (taglists_are_equal (list, new_list)); + + buf2 = gst_tag_list_to_vorbiscomment_buffer (new_list, + (const guint8 *) "\003vorbis", 7, "libgstunittest"); + fail_unless (buf2 != NULL); + even_newer_list = gst_tag_list_from_vorbiscomment_buffer (buf2, + (const guint8 *) "\003vorbis", 7, &vendor_id); + fail_unless (even_newer_list != NULL); + fail_unless (vendor_id != NULL); + g_free (vendor_id); + vendor_id = NULL; + + GST_LOG ("even_newer_list = %" GST_PTR_FORMAT, even_newer_list); + fail_unless (taglists_are_equal (new_list, even_newer_list)); + + gst_tag_list_free (new_list); + gst_tag_list_free (even_newer_list); + gst_buffer_unref (buf); + gst_buffer_unref (buf2); + } + + /* there can only be one language per taglist ... */ + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "fr"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "fr"); + + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "[fr]"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "fr"); + + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "French [fr]"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "fr"); + + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "[eng] English"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "eng"); + + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "eng"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "eng"); + + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "[eng]"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "eng"); + + /* free-form *sigh* */ + gst_tag_list_free (list); + list = gst_tag_list_new (); + gst_vorbis_tag_add (list, "LANGUAGE", "English"); + ASSERT_TAG_LIST_HAS_STRING (list, GST_TAG_LANGUAGE_CODE, "English"); + + gst_tag_list_free (list); +} + +GST_END_TEST; + +GST_START_TEST (test_id3_tags) +{ + guint i; + + fail_unless (gst_tag_id3_genre_count () > 0); + + for (i = 0; i < gst_tag_id3_genre_count (); ++i) { + const gchar *genre; + + genre = gst_tag_id3_genre_get (i); + fail_unless (genre != NULL); + } + + { + /* TODO: GstTagList *gst_tag_list_new_from_id3v1 (const guint8 *data) */ + } + + /* gst_tag_from_id3_tag */ + fail_unless (gst_tag_from_id3_tag ("TALB") != NULL); + ASSERT_CRITICAL (gst_tag_from_id3_tag (NULL)); + fail_unless (gst_tag_from_id3_tag ("R2D2") == NULL); + + /* gst_tag_from_id3_user_tag */ + ASSERT_CRITICAL (gst_tag_from_id3_user_tag (NULL, "foo")); + ASSERT_CRITICAL (gst_tag_from_id3_user_tag ("foo", NULL)); + fail_unless (gst_tag_from_id3_user_tag ("R2D2", "R2D2") == NULL); + + /* gst_tag_to_id3_tag */ + ASSERT_CRITICAL (gst_tag_to_id3_tag (NULL)); + fail_unless (gst_tag_to_id3_tag ("R2D2") == NULL); + fail_unless (gst_tag_to_id3_tag (GST_TAG_ARTIST) != NULL); + + + fail_unless (GST_TYPE_TAG_IMAGE_TYPE != 0); + fail_unless (g_type_name (GST_TYPE_TAG_IMAGE_TYPE) != NULL); +} + +GST_END_TEST; + + static Suite * tag_suite (void) { @@ -140,8 +431,10 @@ tag_suite (void) TCase *tc_chain = tcase_create ("general"); suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_muscibrainz_tag_registration); tcase_add_test (tc_chain, test_parse_extended_comment); - + tcase_add_test (tc_chain, test_vorbis_tags); + tcase_add_test (tc_chain, test_id3_tags); return s; }