gst_plugins_base/gst-libs/gst/tag/gstvorbistag.c
branchRCL_3
changeset 30 7e817e7e631c
parent 29 567bb019e3e3
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
    60   {GST_TAG_PERFORMER, "PERFORMER"},
    60   {GST_TAG_PERFORMER, "PERFORMER"},
    61   {GST_TAG_COMPOSER, "COMPOSER"},
    61   {GST_TAG_COMPOSER, "COMPOSER"},
    62   {GST_TAG_COPYRIGHT, "COPYRIGHT"},
    62   {GST_TAG_COPYRIGHT, "COPYRIGHT"},
    63   {GST_TAG_LICENSE, "LICENSE"},
    63   {GST_TAG_LICENSE, "LICENSE"},
    64   {GST_TAG_LICENSE_URI, "LICENSE"},
    64   {GST_TAG_LICENSE_URI, "LICENSE"},
    65   {GST_TAG_GEO_LOCATION_NAME, "LOCATION"},
    65   {GST_TAG_LOCATION, "LOCATION"},
    66   {GST_TAG_ORGANIZATION, "ORGANIZATION"},
    66   {GST_TAG_ORGANIZATION, "ORGANIZATION"},
    67   {GST_TAG_DESCRIPTION, "DESCRIPTION"},
    67   {GST_TAG_DESCRIPTION, "DESCRIPTION"},
    68   {GST_TAG_GENRE, "GENRE"},
    68   {GST_TAG_GENRE, "GENRE"},
    69   {GST_TAG_DATE, "DATE"},
    69   {GST_TAG_DATE, "DATE"},
    70   {GST_TAG_CONTACT, "CONTACT"},
    70   {GST_TAG_CONTACT, "CONTACT"},
    88   {GST_TAG_TITLE_SORTNAME, "TITLESORT"},
    88   {GST_TAG_TITLE_SORTNAME, "TITLESORT"},
    89   {GST_TAG_TITLE_SORTNAME, "TITLESORTORDER"},
    89   {GST_TAG_TITLE_SORTNAME, "TITLESORTORDER"},
    90   {GST_TAG_LANGUAGE_CODE, "LANGUAGE"},
    90   {GST_TAG_LANGUAGE_CODE, "LANGUAGE"},
    91   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "MUSICBRAINZ_DISCID"},
    91   {GST_TAG_CDDA_MUSICBRAINZ_DISCID, "MUSICBRAINZ_DISCID"},
    92   {GST_TAG_CDDA_CDDB_DISCID, "DISCID"},
    92   {GST_TAG_CDDA_CDDB_DISCID, "DISCID"},
    93   /* For the apparent de-facto standard for coverart in vorbis comments, see:
    93   /* some incidence that this makes sense:
    94    * http://www.hydrogenaudio.org/forums/lofiversion/index.php/t48386.html */
       
    95   {GST_TAG_PREVIEW_IMAGE, "COVERART"},
       
    96   /* some evidence that "BPM" is used elsewhere:
       
    97    * http://mail.kde.org/pipermail/amarok/2006-May/000090.html
    94    * http://mail.kde.org/pipermail/amarok/2006-May/000090.html
    98    */
    95    */
    99   {GST_TAG_BEATS_PER_MINUTE, "BPM"},
    96   {GST_TAG_BEATS_PER_MINUTE, "BPM"},
   100   {NULL, NULL}
    97   {NULL, NULL}
   101 };
    98 };
   318       break;
   315       break;
   319     }
   316     }
   320   }
   317   }
   321 }
   318 }
   322 
   319 
   323 static void
       
   324 gst_vorbis_tag_add_coverart (GstTagList * tags, gchar * img_data_base64,
       
   325     gint base64_len)
       
   326 {
       
   327   GstBuffer *img;
       
   328   gsize img_len;
       
   329   guchar *out;
       
   330   guint save = 0;
       
   331   gint state = 0;
       
   332 
       
   333   if (base64_len < 2)
       
   334     goto not_enough_data;
       
   335 
       
   336   /* img_data_base64 points to a temporary copy of the base64 encoded data, so
       
   337    * it's safe to do inpace decoding here
       
   338    * TODO: glib 2.20 and later provides g_base64_decode_inplace, so change this
       
   339    * to use glib's API instead once it's in wider use:
       
   340    *  http://bugzilla.gnome.org/show_bug.cgi?id=564728
       
   341    *  http://svn.gnome.org/viewvc/glib?view=revision&revision=7807 */
       
   342   out = (guchar *) img_data_base64;
       
   343   img_len = g_base64_decode_step (img_data_base64, base64_len,
       
   344       out, &state, &save);
       
   345 
       
   346   if (img_len == 0)
       
   347     goto decode_failed;
       
   348 
       
   349   img = gst_tag_image_data_to_image_buffer (out, img_len,
       
   350       GST_TAG_IMAGE_TYPE_NONE);
       
   351 
       
   352   if (img == NULL)
       
   353     goto convert_failed;
       
   354 
       
   355   gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
       
   356       GST_TAG_PREVIEW_IMAGE, img, NULL);
       
   357 
       
   358   gst_buffer_unref (img);
       
   359   return;
       
   360 
       
   361 /* ERRORS */
       
   362 not_enough_data:
       
   363   {
       
   364     GST_WARNING ("COVERART tag with too little base64-encoded data");
       
   365     return;
       
   366   }
       
   367 decode_failed:
       
   368   {
       
   369     GST_WARNING ("Couldn't decode base64 image data from COVERART tag");
       
   370     return;
       
   371   }
       
   372 convert_failed:
       
   373   {
       
   374     GST_WARNING ("Couldn't extract image or image type from COVERART tag");
       
   375     return;
       
   376   }
       
   377 }
       
   378 
       
   379 /**
   320 /**
   380  * gst_tag_list_from_vorbiscomment_buffer:
   321  * gst_tag_list_from_vorbiscomment_buffer:
   381  * @buffer: buffer to convert
   322  * @buffer: buffer to convert
   382  * @id_data: identification data at start of stream
   323  * @id_data: identification data at start of stream
   383  * @id_data_length: length of identification data
   324  * @id_data_length: length of identification data
   410 }G_STMT_END
   351 }G_STMT_END
   411   gchar *cur, *value;
   352   gchar *cur, *value;
   412   guint cur_size;
   353   guint cur_size;
   413   guint iterations;
   354   guint iterations;
   414   guint8 *data;
   355   guint8 *data;
   415   guint size, value_len;
   356   guint size;
   416   GstTagList *list;
   357   GstTagList *list;
   417 
   358 
   418   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
   359   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
   419   g_return_val_if_fail (id_data != NULL || id_data_length == 0, NULL);
   360   g_return_val_if_fail (id_data != NULL || id_data_length == 0, NULL);
   420 
   361 
   422   size = GST_BUFFER_SIZE (buffer);
   363   size = GST_BUFFER_SIZE (buffer);
   423   list = gst_tag_list_new ();
   364   list = gst_tag_list_new ();
   424 
   365 
   425   if (size < 11 || size <= id_data_length + 4)
   366   if (size < 11 || size <= id_data_length + 4)
   426     goto error;
   367     goto error;
   427 
       
   428   if (id_data_length > 0 && memcmp (data, id_data, id_data_length) != 0)
   368   if (id_data_length > 0 && memcmp (data, id_data, id_data_length) != 0)
   429     goto error;
   369     goto error;
   430 
       
   431   ADVANCE (id_data_length);
   370   ADVANCE (id_data_length);
   432 
       
   433   if (vendor_string)
   371   if (vendor_string)
   434     *vendor_string = g_strndup (cur, cur_size);
   372     *vendor_string = g_strndup (cur, cur_size);
   435 
       
   436   ADVANCE (cur_size);
   373   ADVANCE (cur_size);
   437   iterations = cur_size;
   374   iterations = cur_size;
   438   cur_size = 0;
   375   cur_size = 0;
   439 
       
   440   while (iterations) {
   376   while (iterations) {
   441     ADVANCE (cur_size);
   377     ADVANCE (cur_size);
   442     iterations--;
   378     iterations--;
   443     cur = g_strndup (cur, cur_size);
   379     cur = g_strndup (cur, cur_size);
   444     value = strchr (cur, '=');
   380     value = strchr (cur, '=');
   446       g_free (cur);
   382       g_free (cur);
   447       continue;
   383       continue;
   448     }
   384     }
   449     *value = '\0';
   385     *value = '\0';
   450     value++;
   386     value++;
   451     value_len = strlen (value);
   387     if (!g_utf8_validate (value, -1, NULL)) {
   452     if (value_len == 0 || !g_utf8_validate (value, value_len, NULL)) {
       
   453       g_free (cur);
   388       g_free (cur);
   454       continue;
   389       continue;
   455     }
   390     }
   456     /* we'll just ignore COVERARTMIME and typefind the image data */
   391     gst_vorbis_tag_add (list, cur, value);
   457     if (g_ascii_strcasecmp (cur, "COVERARTMIME") == 0) {
       
   458       continue;
       
   459     } else if (g_ascii_strcasecmp (cur, "COVERART") == 0) {
       
   460       gst_vorbis_tag_add_coverart (list, value, value_len);
       
   461     } else {
       
   462       gst_vorbis_tag_add (list, cur, value);
       
   463     }
       
   464     g_free (cur);
   392     g_free (cur);
   465   }
   393   }
   466 
   394 
   467   return list;
   395   return list;
   468 
   396 
   469 error:
   397 error:
   470   gst_tag_list_free (list);
   398   gst_tag_list_free (list);
   471   return NULL;
   399   return NULL;
   472 #undef ADVANCE
   400 #undef ADVANCE
   473 }
   401 }
   474 
       
   475 typedef struct
   402 typedef struct
   476 {
   403 {
   477   guint count;
   404   guint count;
   478   guint data_count;
   405   guint data_count;
   479   GList *entries;
   406   GList *entries;
   480 }
   407 }
   481 MyForEach;
   408 MyForEach;
   482 
       
   483 static GList *
       
   484 gst_tag_to_coverart (const GValue * image_value)
       
   485 {
       
   486   gchar *coverart_data, *data_result, *mime_result;
       
   487   const gchar *mime_type;
       
   488   GstStructure *mime_struct;
       
   489   GstBuffer *buffer;
       
   490   GList *l = NULL;
       
   491 
       
   492   g_return_val_if_fail (image_value != NULL, NULL);
       
   493 
       
   494   buffer = gst_value_get_buffer (image_value);
       
   495   g_return_val_if_fail (gst_caps_is_fixed (buffer->caps), NULL);
       
   496   mime_struct = gst_caps_get_structure (buffer->caps, 0);
       
   497   mime_type = gst_structure_get_name (mime_struct);
       
   498 
       
   499   if (strcmp (mime_type, "text/uri-list") == 0) {
       
   500     /* URI reference */
       
   501     coverart_data = g_strndup ((gchar *) buffer->data, buffer->size);
       
   502   } else {
       
   503     coverart_data = g_base64_encode (buffer->data, buffer->size);
       
   504   }
       
   505 
       
   506   data_result = g_strdup_printf ("COVERART=%s", coverart_data);
       
   507   mime_result = g_strdup_printf ("COVERARTMIME=%s", mime_type);
       
   508   g_free (coverart_data);
       
   509 
       
   510   l = g_list_append (l, data_result);
       
   511   l = g_list_append (l, mime_result);
       
   512 
       
   513   return l;
       
   514 }
       
   515 
   409 
   516 /**
   410 /**
   517  * gst_tag_to_vorbis_comments:
   411  * gst_tag_to_vorbis_comments:
   518  * @list: a #GstTagList
   412  * @list: a #GstTagList
   519  * @tag: a GStreamer tag identifier, such as #GST_TAG_ARTIST
   413  * @tag: a GStreamer tag identifier, such as #GST_TAG_ARTIST
   535   GList *l = NULL;
   429   GList *l = NULL;
   536   guint i;
   430   guint i;
   537 
   431 
   538   g_return_val_if_fail (list != NULL, NULL);
   432   g_return_val_if_fail (list != NULL, NULL);
   539   g_return_val_if_fail (tag != NULL, NULL);
   433   g_return_val_if_fail (tag != NULL, NULL);
   540 
       
   541   /* Special case: cover art is split into two tags to store data and
       
   542    * MIME-type. Even if the tag list contains multiple entries, there is
       
   543    * no reasonable way to save more than one.
       
   544    * If both, preview image and image, are present we prefer the
       
   545    * image tag.
       
   546    */
       
   547   if ((strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0 &&
       
   548           gst_tag_list_get_tag_size (list, GST_TAG_IMAGE) == 0) ||
       
   549       strcmp (tag, GST_TAG_IMAGE) == 0) {
       
   550     return gst_tag_to_coverart (gst_tag_list_get_value_index (list, tag, 0));
       
   551   }
       
   552 
   434 
   553   if (strcmp (tag, GST_TAG_EXTENDED_COMMENT) != 0) {
   435   if (strcmp (tag, GST_TAG_EXTENDED_COMMENT) != 0) {
   554     vorbis_tag = gst_tag_to_vorbis_tag (tag);
   436     vorbis_tag = gst_tag_to_vorbis_tag (tag);
   555     if (!vorbis_tag)
   437     if (!vorbis_tag)
   556       return NULL;
   438       return NULL;