From cd9bb9a674d86b8b1c350562d5c9648717bf0bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 3 Jun 2008 19:29:06 +0000 Subject: [PATCH] gst-libs/gst/tag/: API: add gst_tag_image_data_to_image_buffer() Original commit message from CVS: * gst-libs/gst/tag/gstid3tag.c: (gst_tag_list_add_id3_image): * gst-libs/gst/tag/tag.h: (GST_TAG_IMAGE_TYPE_NONE), * gst-libs/gst/tag/tags.c: (register_tag_image_type_enum), (gst_tag_image_type_get_type), (gst_tag_image_type_is_valid), (gst_tag_image_data_to_image_buffer): Add two utility functions to avoid code duplication (#512333): API: add gst_tag_image_data_to_image_buffer() API: add gst_tag_list_add_id3_image() --- ChangeLog | 11 ++++ gst-libs/gst/tag/gstid3tag.c | 55 ++++++++++++++++ gst-libs/gst/tag/tag.h | 15 ++++- gst-libs/gst/tag/tags.c | 124 ++++++++++++++++++++++++++++++++++- 4 files changed, 203 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0dcee283d2..ff89caa3fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-06-03 Tim-Philipp Müller + + * gst-libs/gst/tag/gstid3tag.c: (gst_tag_list_add_id3_image): + * gst-libs/gst/tag/tag.h: (GST_TAG_IMAGE_TYPE_NONE), + * gst-libs/gst/tag/tags.c: (register_tag_image_type_enum), + (gst_tag_image_type_get_type), (gst_tag_image_type_is_valid), + (gst_tag_image_data_to_image_buffer): + Add two utility functions to avoid code duplication (#512333): + API: add gst_tag_image_data_to_image_buffer() + API: add gst_tag_list_add_id3_image() + 2008-06-03 Sebastian Dröge * win32/common/libgstaudio.def: diff --git a/gst-libs/gst/tag/gstid3tag.c b/gst-libs/gst/tag/gstid3tag.c index b32b8fc047..2ab433f0ea 100644 --- a/gst-libs/gst/tag/gstid3tag.c +++ b/gst-libs/gst/tag/gstid3tag.c @@ -428,3 +428,58 @@ gst_tag_id3_genre_get (const guint id) return NULL; return genres[id]; } + +/** + * gst_tag_list_add_id3_image: + * @tag_list: a tag list + * @image_data: the (encoded) image + * @image_data_len: the length of the encoded image data at @image_data + * @id3_picture_type: picture type as per the ID3 (v2.4.0) specification for + * the APIC frame (0 = unknown/other) + * + * Adds an image from an ID3 APIC frame (or similar, such as used in FLAC) + * to the given tag list. Also see gst_tag_image_data_to_image_buffer() for + * more information on image tags in GStreamer. + * + * Returns: %TRUE if the image was processed, otherwise %FALSE + * + * Since: 0.10.20 + */ +gboolean +gst_tag_list_add_id3_image (GstTagList * tag_list, const guint8 * image_data, + guint image_data_len, guint id3_picture_type) +{ + GstTagImageType tag_image_type; + const gchar *tag_name; + GstBuffer *image; + + g_return_val_if_fail (GST_IS_TAG_LIST (tag_list), FALSE); + g_return_val_if_fail (image_data != NULL, FALSE); + g_return_val_if_fail (image_data_len > 0, FALSE); + + if (id3_picture_type == 0x01 || id3_picture_type == 0x02) { + /* file icon for preview. Don't add image-type to caps, since there + * is only supposed to be one of these, and the type is already indicated + * via the special tag */ + tag_name = GST_TAG_PREVIEW_IMAGE; + tag_image_type = GST_TAG_IMAGE_TYPE_NONE; + } else { + tag_name = GST_TAG_IMAGE; + + /* Remap the ID3v2 APIC type our ImageType enum */ + if (id3_picture_type >= 0x3 && id3_picture_type <= 0x14) + tag_image_type = (GstTagImageType) (id3_picture_type - 2); + else + tag_image_type = GST_TAG_IMAGE_TYPE_UNDEFINED; + } + + image = gst_tag_image_data_to_image_buffer (image_data, image_data_len, + tag_image_type); + + if (image == NULL) + return FALSE; + + gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, image, NULL); + gst_buffer_unref (image); + return TRUE; +} diff --git a/gst-libs/gst/tag/tag.h b/gst-libs/gst/tag/tag.h index 48bc549653..ec680d2576 100644 --- a/gst-libs/gst/tag/tag.h +++ b/gst-libs/gst/tag/tag.h @@ -126,6 +126,9 @@ G_BEGIN_DECLS /** * GstTagImageType: + * @GST_TAG_IMAGE_TYPE_NONE : No image type. Can be used to + * tell functions such as gst_tag_image_data_to_image_buffer() that no + * image type should be set. (Since: 0.10.20) * @GST_TAG_IMAGE_TYPE_UNDEFINED : Undefined/other image type * @GST_TAG_IMAGE_TYPE_FRONT_COVER : Cover (front) * @GST_TAG_IMAGE_TYPE_BACK_COVER : Cover (back) @@ -152,7 +155,8 @@ G_BEGIN_DECLS * Since: 0.10.9 */ typedef enum { - GST_TAG_IMAGE_TYPE_UNDEFINED, + GST_TAG_IMAGE_TYPE_NONE = -1, + GST_TAG_IMAGE_TYPE_UNDEFINED = 0, GST_TAG_IMAGE_TYPE_FRONT_COVER, GST_TAG_IMAGE_TYPE_BACK_COVER, GST_TAG_IMAGE_TYPE_LEAFLET_PAGE, @@ -209,6 +213,11 @@ G_CONST_RETURN gchar * gst_tag_from_id3_user_tag (const gchar * const gchar * id3_user_tag); G_CONST_RETURN gchar * gst_tag_to_id3_tag (const gchar * gst_tag); +gboolean gst_tag_list_add_id3_image (GstTagList * tag_list, + const guint8 * image_data, + guint image_data_len, + guint id3_picture_type); + /* other tag-related functions */ gboolean gst_tag_parse_extended_comment (const gchar * ext_comment, @@ -221,6 +230,10 @@ gchar * gst_tag_freeform_string_to_utf8 (const gchar * data, gint size, const gchar ** env_vars); +GstBuffer * gst_tag_image_data_to_image_buffer (const guint8 * image_data, + guint image_data_len, + GstTagImageType image_type); + /* FIXME 0.11: replace with a more general gst_tag_library_init() */ void gst_tag_register_musicbrainz_tags (void); diff --git a/gst-libs/gst/tag/tags.c b/gst-libs/gst/tag/tags.c index acd72addf7..68aed1ac6b 100644 --- a/gst-libs/gst/tag/tags.c +++ b/gst-libs/gst/tag/tags.c @@ -1,6 +1,6 @@ /* GStreamer non-core tag registration and tag utility functions * Copyright (C) 2005 Ross Burton - * Copyright (C) 2006 Tim-Philipp Müller + * Copyright (C) 2006-2008 Tim-Philipp Müller * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,6 +23,7 @@ #endif #include +#include #include #include "tag.h" @@ -144,6 +145,9 @@ register_tag_image_type_enum (GType * id) }; *id = g_enum_register_static ("GstTagImageType", image_types); + + /* work around thread-safety issue with class creation in GLib */ + g_type_class_ref (*id); } GType @@ -156,6 +160,19 @@ gst_tag_image_type_get_type (void) return id; } +static inline gboolean +gst_tag_image_type_is_valid (GstTagImageType type) +{ + GEnumClass *klass; + gboolean res; + + klass = g_type_class_ref (gst_tag_image_type_get_type ()); + res = (g_enum_get_value (klass, type) != NULL); + g_type_class_unref (klass); + + return res; +} + /** * gst_tag_parse_extended_comment: * @ext_comment: an extended comment string, see #GST_TAG_EXTENDED_COMMENT @@ -326,3 +343,108 @@ beach: g_free (utf8); return NULL; } + +/** + * gst_tag_image_data_to_image_buffer: + * @image_data: the (encoded) image + * @image_data_len: the length of the encoded image data at @image_data + * @image_type: type of the image, or #GST_TAG_IMAGE_TYPE_UNDEFINED. Pass + * #GST_TAG_IMAGE_TYPE_NONE if no image type should be set at all (e.g. + * for preview images) + * + * Helper function for tag-reading plugins to create a #GstBuffer suitable to + * add to a #GstTagList as an image tag (such as #GST_TAG_IMAGE or + * #GST_TAG_PREVIEW_IMAGE) from the encoded image data and an (optional) image + * type. + * + * Background: cover art and other images in tags are usually stored as a + * blob of binary image data, often accompanied by a MIME type or some other + * content type string (e.g. 'png', 'jpeg', 'jpg'). Sometimes there is also an + * 'image type' to indicate what kind of image this is (e.g. front cover, + * back cover, artist, etc.). The image data may also be an URI to the image + * rather than the image itself. + * + * In GStreamer, image tags are #GstBuffers containing the raw image + * data, with the buffer caps describing the content type of the image + * (e.g. image/jpeg, image/png, text/uri-list). The buffer caps may contain + * an additional 'image-type' field of #GST_TYPE_TAG_IMAGE_TYPE to describe + * the type of image (front cover, back cover etc.). #GST_TAG_PREVIEW_IMAGE + * tags should not carry an image type, their type is already indicated via + * the special tag name. + * + * This function will do various checks and typefind the encoded image + * data (we can't trust the declared mime type). + * + * Returns: a newly-allocated image buffer for use in tag lists, or NULL + * + * Since: 0.10.20 + */ +GstBuffer * +gst_tag_image_data_to_image_buffer (const guint8 * image_data, + guint image_data_len, GstTagImageType image_type) +{ + const gchar *name; + GstBuffer *image; + GstCaps *caps; + + g_return_val_if_fail (image_data != NULL, NULL); + g_return_val_if_fail (image_data_len > 0, NULL); + g_return_val_if_fail (gst_tag_image_type_is_valid (image_type), NULL); + + GST_DEBUG ("image data len: %u bytes", image_data_len); + + /* allocate space for a NUL terminator for an uri too */ + image = gst_buffer_try_new_and_alloc (image_data_len + 1); + if (image == NULL) { + GST_WARNING ("failed to allocate buffer of %d for image", image_data_len); + return NULL; + } + + memcpy (GST_BUFFER_DATA (image), image_data, image_data_len); + GST_BUFFER_DATA (image)[image_data_len] = '\0'; + GST_BUFFER_SIZE (image) = image_data_len; + + /* Find GStreamer media type, can't trust declared type */ + caps = gst_type_find_helper_for_buffer (NULL, image, NULL); + + if (caps == NULL) + goto no_type; + + GST_DEBUG ("Found GStreamer media type: %" GST_PTR_FORMAT, caps); + + /* sanity check: make sure typefound/declared caps are either URI or image */ + name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); + + if (!g_str_has_prefix (name, "image/") && + !g_str_has_prefix (name, "video/") && + !g_str_equal (name, "text/uri-list")) { + GST_DEBUG ("Unexpected image type '%s', ignoring image frame", name); + goto error; + } + + if (image_type != GST_TAG_IMAGE_TYPE_NONE) { + GST_LOG ("Setting image type: %d", image_type); + caps = gst_caps_make_writable (caps); + gst_caps_set_simple (caps, "image-type", GST_TYPE_TAG_IMAGE_TYPE, + image_type, NULL); + } + + gst_buffer_set_caps (image, caps); + gst_caps_unref (caps); + return image; + +/* ERRORS */ +no_type: + { + GST_DEBUG ("Could not determine GStreamer media type, ignoring image"); + /* fall through */ + } +error: + { + if (image) + gst_buffer_unref (image); + if (caps) + gst_caps_unref (caps); + return NULL; + } +}