gst_plugins_base/gst-libs/gst/tag/gstvorbistag.c
branchRCL_3
changeset 29 567bb019e3e3
parent 0 0e761a78d257
child 30 7e817e7e631c
--- a/gst_plugins_base/gst-libs/gst/tag/gstvorbistag.c	Wed Mar 31 22:03:18 2010 +0300
+++ b/gst_plugins_base/gst-libs/gst/tag/gstvorbistag.c	Tue Aug 31 15:30:33 2010 +0300
@@ -62,7 +62,7 @@
   {GST_TAG_COPYRIGHT, "COPYRIGHT"},
   {GST_TAG_LICENSE, "LICENSE"},
   {GST_TAG_LICENSE_URI, "LICENSE"},
-  {GST_TAG_LOCATION, "LOCATION"},
+  {GST_TAG_GEO_LOCATION_NAME, "LOCATION"},
   {GST_TAG_ORGANIZATION, "ORGANIZATION"},
   {GST_TAG_DESCRIPTION, "DESCRIPTION"},
   {GST_TAG_GENRE, "GENRE"},
@@ -90,7 +90,10 @@
   {GST_TAG_LANGUAGE_CODE, "LANGUAGE"},
   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "MUSICBRAINZ_DISCID"},
   {GST_TAG_CDDA_CDDB_DISCID, "DISCID"},
-  /* some incidence that this makes sense:
+  /* For the apparent de-facto standard for coverart in vorbis comments, see:
+   * http://www.hydrogenaudio.org/forums/lofiversion/index.php/t48386.html */
+  {GST_TAG_PREVIEW_IMAGE, "COVERART"},
+  /* some evidence that "BPM" is used elsewhere:
    * http://mail.kde.org/pipermail/amarok/2006-May/000090.html
    */
   {GST_TAG_BEATS_PER_MINUTE, "BPM"},
@@ -317,6 +320,62 @@
   }
 }
 
+static void
+gst_vorbis_tag_add_coverart (GstTagList * tags, gchar * img_data_base64,
+    gint base64_len)
+{
+  GstBuffer *img;
+  gsize img_len;
+  guchar *out;
+  guint save = 0;
+  gint state = 0;
+
+  if (base64_len < 2)
+    goto not_enough_data;
+
+  /* img_data_base64 points to a temporary copy of the base64 encoded data, so
+   * it's safe to do inpace decoding here
+   * TODO: glib 2.20 and later provides g_base64_decode_inplace, so change this
+   * to use glib's API instead once it's in wider use:
+   *  http://bugzilla.gnome.org/show_bug.cgi?id=564728
+   *  http://svn.gnome.org/viewvc/glib?view=revision&revision=7807 */
+  out = (guchar *) img_data_base64;
+  img_len = g_base64_decode_step (img_data_base64, base64_len,
+      out, &state, &save);
+
+  if (img_len == 0)
+    goto decode_failed;
+
+  img = gst_tag_image_data_to_image_buffer (out, img_len,
+      GST_TAG_IMAGE_TYPE_NONE);
+
+  if (img == NULL)
+    goto convert_failed;
+
+  gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
+      GST_TAG_PREVIEW_IMAGE, img, NULL);
+
+  gst_buffer_unref (img);
+  return;
+
+/* ERRORS */
+not_enough_data:
+  {
+    GST_WARNING ("COVERART tag with too little base64-encoded data");
+    return;
+  }
+decode_failed:
+  {
+    GST_WARNING ("Couldn't decode base64 image data from COVERART tag");
+    return;
+  }
+convert_failed:
+  {
+    GST_WARNING ("Couldn't extract image or image type from COVERART tag");
+    return;
+  }
+}
+
 /**
  * gst_tag_list_from_vorbiscomment_buffer:
  * @buffer: buffer to convert
@@ -353,7 +412,7 @@
   guint cur_size;
   guint iterations;
   guint8 *data;
-  guint size;
+  guint size, value_len;
   GstTagList *list;
 
   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
@@ -365,14 +424,19 @@
 
   if (size < 11 || size <= id_data_length + 4)
     goto error;
+
   if (id_data_length > 0 && memcmp (data, id_data, id_data_length) != 0)
     goto error;
+
   ADVANCE (id_data_length);
+
   if (vendor_string)
     *vendor_string = g_strndup (cur, cur_size);
+
   ADVANCE (cur_size);
   iterations = cur_size;
   cur_size = 0;
+
   while (iterations) {
     ADVANCE (cur_size);
     iterations--;
@@ -384,11 +448,19 @@
     }
     *value = '\0';
     value++;
-    if (!g_utf8_validate (value, -1, NULL)) {
+    value_len = strlen (value);
+    if (value_len == 0 || !g_utf8_validate (value, value_len, NULL)) {
       g_free (cur);
       continue;
     }
-    gst_vorbis_tag_add (list, cur, value);
+    /* we'll just ignore COVERARTMIME and typefind the image data */
+    if (g_ascii_strcasecmp (cur, "COVERARTMIME") == 0) {
+      continue;
+    } else if (g_ascii_strcasecmp (cur, "COVERART") == 0) {
+      gst_vorbis_tag_add_coverart (list, value, value_len);
+    } else {
+      gst_vorbis_tag_add (list, cur, value);
+    }
     g_free (cur);
   }
 
@@ -399,6 +471,7 @@
   return NULL;
 #undef ADVANCE
 }
+
 typedef struct
 {
   guint count;
@@ -407,6 +480,39 @@
 }
 MyForEach;
 
+static GList *
+gst_tag_to_coverart (const GValue * image_value)
+{
+  gchar *coverart_data, *data_result, *mime_result;
+  const gchar *mime_type;
+  GstStructure *mime_struct;
+  GstBuffer *buffer;
+  GList *l = NULL;
+
+  g_return_val_if_fail (image_value != NULL, NULL);
+
+  buffer = gst_value_get_buffer (image_value);
+  g_return_val_if_fail (gst_caps_is_fixed (buffer->caps), NULL);
+  mime_struct = gst_caps_get_structure (buffer->caps, 0);
+  mime_type = gst_structure_get_name (mime_struct);
+
+  if (strcmp (mime_type, "text/uri-list") == 0) {
+    /* URI reference */
+    coverart_data = g_strndup ((gchar *) buffer->data, buffer->size);
+  } else {
+    coverart_data = g_base64_encode (buffer->data, buffer->size);
+  }
+
+  data_result = g_strdup_printf ("COVERART=%s", coverart_data);
+  mime_result = g_strdup_printf ("COVERARTMIME=%s", mime_type);
+  g_free (coverart_data);
+
+  l = g_list_append (l, data_result);
+  l = g_list_append (l, mime_result);
+
+  return l;
+}
+
 /**
  * gst_tag_to_vorbis_comments:
  * @list: a #GstTagList
@@ -432,6 +538,18 @@
   g_return_val_if_fail (list != NULL, NULL);
   g_return_val_if_fail (tag != NULL, NULL);
 
+  /* Special case: cover art is split into two tags to store data and
+   * MIME-type. Even if the tag list contains multiple entries, there is
+   * no reasonable way to save more than one.
+   * If both, preview image and image, are present we prefer the
+   * image tag.
+   */
+  if ((strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0 &&
+          gst_tag_list_get_tag_size (list, GST_TAG_IMAGE) == 0) ||
+      strcmp (tag, GST_TAG_IMAGE) == 0) {
+    return gst_tag_to_coverart (gst_tag_list_get_value_index (list, tag, 0));
+  }
+
   if (strcmp (tag, GST_TAG_EXTENDED_COMMENT) != 0) {
     vorbis_tag = gst_tag_to_vorbis_tag (tag);
     if (!vorbis_tag)