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 |
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; |