| /* GStreamer non-core tag registration and tag utility functions |
| * Copyright (C) 2005 Ross Burton <ross@burtonini.com> |
| * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst-i18n-plugin.h> |
| #include <gst/base/gsttypefindhelper.h> |
| #include <gst/gst.h> |
| #include "tag.h" |
| #include "id3v2.h" |
| |
| #include <string.h> |
| |
| /** |
| * SECTION:gsttag |
| * @title: Tags |
| * @short_description: additional tag definitions for plugins and applications |
| * @see_also: #GstTagList |
| * |
| * Contains additional standardized GStreamer tag definitions for plugins |
| * and applications, and functions to register them with the GStreamer |
| * tag system. |
| * |
| */ |
| |
| #ifndef GST_DISABLE_GST_DEBUG |
| #define GST_CAT_DEFAULT gst_tag_ensure_debug_category() |
| |
| static GstDebugCategory * |
| gst_tag_ensure_debug_category (void) |
| { |
| static gsize cat_gonce = 0; |
| |
| if (g_once_init_enter (&cat_gonce)) { |
| GstDebugCategory *cat = NULL; |
| |
| GST_DEBUG_CATEGORY_INIT (cat, "tag-tags", 0, "GstTag helper functions"); |
| |
| g_once_init_leave (&cat_gonce, (gsize) cat); |
| } |
| |
| return (GstDebugCategory *) cat_gonce; |
| } |
| #endif /* GST_DISABLE_GST_DEBUG */ |
| |
| static gpointer |
| gst_tag_register_tags_internal (gpointer unused) |
| { |
| #ifdef ENABLE_NLS |
| GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, |
| LOCALEDIR); |
| bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); |
| bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
| #endif |
| |
| /* musicbrainz tags */ |
| gst_tag_register_static (GST_TAG_MUSICBRAINZ_TRACKID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("track ID"), _("MusicBrainz track ID"), NULL); |
| gst_tag_register_static (GST_TAG_MUSICBRAINZ_ARTISTID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("artist ID"), _("MusicBrainz artist ID"), NULL); |
| gst_tag_register_static (GST_TAG_MUSICBRAINZ_ALBUMID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("album ID"), _("MusicBrainz album ID"), NULL); |
| gst_tag_register_static (GST_TAG_MUSICBRAINZ_ALBUMARTISTID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, |
| _("album artist ID"), _("MusicBrainz album artist ID"), NULL); |
| gst_tag_register_static (GST_TAG_MUSICBRAINZ_TRMID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("track TRM ID"), _("MusicBrainz TRM ID"), NULL); |
| |
| /* CDDA tags */ |
| gst_tag_register_static (GST_TAG_CDDA_CDDB_DISCID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, "discid", "CDDB discid for metadata retrieval", |
| gst_tag_merge_use_first); |
| |
| gst_tag_register_static (GST_TAG_CDDA_CDDB_DISCID_FULL, GST_TAG_FLAG_META, |
| G_TYPE_STRING, "discid full", |
| "CDDB discid for metadata retrieval (full)", gst_tag_merge_use_first); |
| |
| gst_tag_register_static (GST_TAG_CDDA_MUSICBRAINZ_DISCID, GST_TAG_FLAG_META, |
| G_TYPE_STRING, "musicbrainz-discid", |
| "Musicbrainz discid for metadata retrieval", gst_tag_merge_use_first); |
| |
| gst_tag_register_static (GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL, |
| GST_TAG_FLAG_META, G_TYPE_STRING, "musicbrainz-discid-full", |
| "Musicbrainz discid for metadata retrieval (full)", |
| gst_tag_merge_use_first); |
| |
| /* photography tags */ |
| gst_tag_register_static (GST_TAG_CAPTURING_SHUTTER_SPEED, GST_TAG_FLAG_META, |
| GST_TYPE_FRACTION, _("capturing shutter speed"), |
| _("Shutter speed used when capturing an image, in seconds"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_FOCAL_RATIO, GST_TAG_FLAG_META, |
| G_TYPE_DOUBLE, _("capturing focal ratio"), |
| _("Focal ratio (f-number) used when capturing the image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_FOCAL_LENGTH, GST_TAG_FLAG_META, |
| G_TYPE_DOUBLE, _("capturing focal length"), |
| _("Focal length of the lens used capturing the image, in mm"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_FOCAL_LENGTH_35_MM, |
| GST_TAG_FLAG_META, G_TYPE_DOUBLE, |
| _("capturing 35 mm equivalent focal length"), |
| _("35 mm equivalent focal length of the lens used capturing the image, " |
| "in mm"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_DIGITAL_ZOOM_RATIO, |
| GST_TAG_FLAG_META, G_TYPE_DOUBLE, _("capturing digital zoom ratio"), |
| _("Digital zoom ratio used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_ISO_SPEED, GST_TAG_FLAG_META, |
| G_TYPE_INT, _("capturing iso speed"), |
| _("The ISO speed used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_EXPOSURE_PROGRAM, |
| GST_TAG_FLAG_META, G_TYPE_STRING, _("capturing exposure program"), |
| _("The exposure program used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_EXPOSURE_MODE, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing exposure mode"), |
| _("The exposure mode used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_EXPOSURE_COMPENSATION, |
| GST_TAG_FLAG_META, G_TYPE_DOUBLE, _("capturing exposure compensation"), |
| _("The exposure compensation used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_SCENE_CAPTURE_TYPE, |
| GST_TAG_FLAG_META, G_TYPE_STRING, _("capturing scene capture type"), |
| _("The scene capture mode used when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_GAIN_ADJUSTMENT, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing gain adjustment"), |
| _("The overall gain adjustment applied on an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_WHITE_BALANCE, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing white balance"), |
| _("The white balance mode set when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_CONTRAST, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing contrast"), |
| _("The direction of contrast processing applied " |
| "when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_SATURATION, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing saturation"), |
| _("The direction of saturation processing applied when " |
| "capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_SHARPNESS, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing sharpness"), |
| _("The direction of sharpness processing applied " |
| "when capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_FLASH_FIRED, GST_TAG_FLAG_META, |
| G_TYPE_BOOLEAN, _("capturing flash fired"), |
| _("If the flash fired while capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_FLASH_MODE, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing flash mode"), |
| _("The selected flash mode while capturing an image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_METERING_MODE, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing metering mode"), |
| _("The metering mode used while determining exposure for capturing an" |
| " image"), NULL); |
| |
| gst_tag_register_static (GST_TAG_CAPTURING_SOURCE, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("capturing source"), |
| _("The source or type of device used for the capture"), NULL); |
| |
| gst_tag_register_static (GST_TAG_IMAGE_HORIZONTAL_PPI, GST_TAG_FLAG_META, |
| G_TYPE_DOUBLE, _("image horizontal ppi"), |
| _("Media (image/video) intended horizontal pixel density in ppi"), NULL); |
| |
| gst_tag_register_static (GST_TAG_IMAGE_VERTICAL_PPI, GST_TAG_FLAG_META, |
| G_TYPE_DOUBLE, _("image vertical ppi"), |
| _("Media (image/video) intended vertical pixel density in ppi"), NULL); |
| |
| gst_tag_register_static (GST_TAG_ID3V2_FRAME, GST_TAG_FLAG_META, |
| GST_TYPE_SAMPLE, _("ID3v2 frame"), _("unparsed id3v2 tag frame"), |
| gst_tag_merge_use_first); |
| |
| gst_tag_register_static (GST_TAG_MUSICAL_KEY, GST_TAG_FLAG_META, |
| G_TYPE_STRING, _("musical-key"), _("Initial key in which the " |
| "sound starts"), gst_tag_merge_use_first); |
| |
| return NULL; |
| } |
| |
| /* FIXME 0.11: rename this to gst_tag_init() or gst_tag_register_tags() or |
| * even better: make tags auto-register themselves, either by defining them |
| * to a wrapper func that does the initing, or by adding tag factories so |
| * that the core can load+register tags automatically when needed. */ |
| |
| /** |
| * gst_tag_register_musicbrainz_tags: |
| * |
| * Registers additional musicbrainz-specific tags with the GStreamer tag |
| * system. Plugins and applications that use these tags should call this |
| * function before using them. Can be called multiple times. |
| */ |
| void |
| gst_tag_register_musicbrainz_tags (void) |
| { |
| static GOnce mb_once = G_ONCE_INIT; |
| |
| g_once (&mb_once, gst_tag_register_tags_internal, NULL); |
| } |
| |
| 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 |
| * @key: (out) (nullable): |
| * return location for the comment description key, or NULL |
| * @lang: (out) (nullable): |
| * return location for the comment ISO-639 language code, or NULL |
| * @value: (out): return location for the actual comment string, or NULL |
| * @fail_if_no_key: whether to fail if strings are not in key=value form |
| * |
| * Convenience function to parse a GST_TAG_EXTENDED_COMMENT string and |
| * separate it into its components. |
| * |
| * If successful, @key, @lang and/or @value will be set to newly allocated |
| * strings that you need to free with g_free() when done. @key and @lang |
| * may also be set to NULL by this function if there is no key or no language |
| * code in the extended comment string. |
| * |
| * Returns: TRUE if the string could be parsed, otherwise FALSE |
| */ |
| gboolean |
| gst_tag_parse_extended_comment (const gchar * ext_comment, gchar ** key, |
| gchar ** lang, gchar ** value, gboolean fail_if_no_key) |
| { |
| const gchar *div, *bop, *bcl; |
| |
| g_return_val_if_fail (ext_comment != NULL, FALSE); |
| g_return_val_if_fail (g_utf8_validate (ext_comment, -1, NULL), FALSE); |
| |
| if (key) |
| *key = NULL; |
| if (lang) |
| *lang = NULL; |
| |
| div = strchr (ext_comment, '='); |
| bop = strchr (ext_comment, '['); |
| bcl = strchr (ext_comment, ']'); |
| |
| if (div == NULL) { |
| if (fail_if_no_key) |
| return FALSE; |
| if (value) |
| *value = g_strdup (ext_comment); |
| return TRUE; |
| } |
| |
| if (bop != NULL && bop < div) { |
| if (bcl < bop || bcl > div) |
| return FALSE; |
| if (key) |
| *key = g_strndup (ext_comment, bop - ext_comment); |
| if (lang) |
| *lang = g_strndup (bop + 1, bcl - bop - 1); |
| } else { |
| if (key) |
| *key = g_strndup (ext_comment, div - ext_comment); |
| } |
| |
| if (value) |
| *value = g_strdup (div + 1); |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_tag_freeform_string_to_utf8: |
| * @data: (array length=size) (element-type gchar): string data |
| * @size: length of string data, or -1 if the string is NUL-terminated |
| * @env_vars: (array zero-terminated=1) |
| * a NULL-terminated string array of environment variable names, or NULL |
| * |
| * Convenience function to read a string with unknown character encoding. If |
| * the string is already in UTF-8 encoding, it will be returned right away. |
| * If not it tries to detect byte-order-mark for UTF-16/32 cases and use that. |
| * Otherwise, the environment will be searched for a number of environment |
| * variables (whose names are specified in the NULL-terminated string array |
| * @env_vars) containing a list of character encodings to try/use. If none |
| * are specified, the current locale will be tried. If that also doesn't work, |
| * WINDOWS-1252/ISO-8859-1 is assumed (which will almost always succeed). |
| * |
| * Returns: a newly-allocated string in UTF-8 encoding, or NULL |
| */ |
| gchar * |
| gst_tag_freeform_string_to_utf8 (const gchar * data, gint size, |
| const gchar ** env_vars) |
| { |
| const gchar *cur_loc = NULL; |
| |
| gsize bytes_read; |
| |
| gchar *utf8 = NULL; |
| |
| g_return_val_if_fail (data != NULL, NULL); |
| |
| if (size < 0) |
| size = strlen (data); |
| |
| /* chop off trailing string terminators to make sure utf8_validate doesn't |
| * get to see them (since that would make the utf8 check fail) */ |
| while (size > 0 && data[size - 1] == '\0') |
| --size; |
| |
| /* Should we try the charsets specified |
| * via environment variables FIRST ? */ |
| if (g_utf8_validate (data, size, NULL)) { |
| utf8 = g_strndup (data, size); |
| GST_LOG ("String '%s' is valid UTF-8 already", utf8); |
| goto beach; |
| } |
| |
| /* check for and use byte-order-mark for UTF-16/32 cases */ |
| if (size >= 2) { |
| const gchar *c = NULL; |
| gint prefix, ssize; |
| |
| if (size >= 4) { |
| prefix = 4; |
| ssize = GST_ROUND_DOWN_4 (size - 4); |
| switch (GST_READ_UINT32_BE (data)) { |
| case 0x0000FEFF: |
| c = "UTF-32BE"; |
| break; |
| case 0xFFFE0000: |
| c = "UTF-32LE"; |
| break; |
| default: |
| break; |
| } |
| } |
| if (!c) { |
| prefix = 2; |
| ssize = GST_ROUND_DOWN_2 (size - 2); |
| switch (GST_READ_UINT16_BE (data)) { |
| case 0xFEFF: |
| c = "UTF-16BE"; |
| break; |
| case 0xFFFE: |
| c = "UTF-16LE"; |
| break; |
| default: |
| break; |
| } |
| } |
| if (c) { |
| GST_LOG ("Trying to convert freeform string to UTF-8 from '%s'", c); |
| if ((utf8 = |
| g_convert (data + prefix, ssize, "UTF-8", c, &bytes_read, NULL, |
| NULL))) { |
| if (bytes_read == ssize) |
| goto beach; |
| g_free (utf8); |
| utf8 = NULL; |
| } |
| } |
| } |
| |
| while (env_vars && *env_vars != NULL) { |
| const gchar *env = NULL; |
| |
| /* Try charsets specified via the environment */ |
| env = g_getenv (*env_vars); |
| if (env != NULL && *env != '\0') { |
| gchar **c, **csets; |
| |
| csets = g_strsplit (env, G_SEARCHPATH_SEPARATOR_S, -1); |
| |
| for (c = csets; c && *c; ++c) { |
| GST_LOG ("Trying to convert freeform string to UTF-8 from '%s'", *c); |
| if ((utf8 = |
| g_convert (data, size, "UTF-8", *c, &bytes_read, NULL, NULL))) { |
| if (bytes_read == size) { |
| g_strfreev (csets); |
| goto beach; |
| } |
| g_free (utf8); |
| utf8 = NULL; |
| } |
| } |
| |
| g_strfreev (csets); |
| } |
| ++env_vars; |
| } |
| |
| /* Try current locale (if not UTF-8) */ |
| if (!g_get_charset (&cur_loc)) { |
| GST_LOG ("Trying to convert freeform string using locale ('%s')", cur_loc); |
| if ((utf8 = g_locale_to_utf8 (data, size, &bytes_read, NULL, NULL))) { |
| if (bytes_read == size) { |
| goto beach; |
| } |
| g_free (utf8); |
| utf8 = NULL; |
| } |
| } |
| |
| /* Try Windows-1252 (which is a superset of ISO 8859-1 that uses a control |
| * character range in ISO 8859-1 for more printable characters) */ |
| { |
| GError *err = NULL; |
| |
| GST_LOG ("Trying to convert freeform string using Windows-1252/ISO-8859-1 " |
| "fallback"); |
| utf8 = g_convert (data, size, "UTF-8", "WINDOWS-1252", &bytes_read, NULL, |
| &err); |
| if (err != NULL) { |
| /* fallback in case iconv implementation doesn't support windows-1252 |
| * for some reason */ |
| if (err->code == G_CONVERT_ERROR_NO_CONVERSION) { |
| g_free (utf8); |
| utf8 = g_convert (data, size, "UTF-8", "ISO-8859-1", &bytes_read, |
| NULL, NULL); |
| } |
| g_error_free (err); |
| } |
| |
| if (utf8 != NULL && bytes_read == size) |
| goto beach; |
| } |
| |
| g_free (utf8); |
| return NULL; |
| |
| beach: |
| |
| g_strchomp (utf8); |
| if (utf8 && utf8[0] != '\0') { |
| GST_LOG ("Returning '%s'", utf8); |
| return utf8; |
| } |
| |
| g_free (utf8); |
| return NULL; |
| } |
| |
| /** |
| * gst_tag_image_data_to_image_sample: |
| * @image_data: (array length=image_data_len): 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 #GstSample 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 #GstSample<!-- -->s containing the raw image |
| * data, with the sample caps describing the content type of the image |
| * (e.g. image/jpeg, image/png, text/uri-list). The sample info 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 sample for use in tag lists, or NULL |
| */ |
| GstSample * |
| gst_tag_image_data_to_image_sample (const guint8 * image_data, |
| guint image_data_len, GstTagImageType image_type) |
| { |
| const gchar *name; |
| GstBuffer *image; |
| GstSample *sample; |
| GstCaps *caps; |
| GstMapInfo info; |
| GstStructure *image_info = NULL; |
| |
| 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_new_and_alloc (image_data_len + 1); |
| if (image == NULL) |
| goto alloc_failed; |
| |
| gst_buffer_map (image, &info, GST_MAP_WRITE); |
| memcpy (info.data, image_data, image_data_len); |
| info.data[image_data_len] = '\0'; |
| gst_buffer_unmap (image, &info); |
| |
| /* 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; |
| } |
| |
| /* Decrease size by 1 if we don't have an URI list |
| * to keep the original size of the image |
| */ |
| if (!g_str_equal (name, "text/uri-list")) |
| gst_buffer_set_size (image, image_data_len); |
| |
| if (image_type != GST_TAG_IMAGE_TYPE_NONE) { |
| GST_LOG ("Setting image type: %d", image_type); |
| image_info = gst_structure_new ("GstTagImageInfo", |
| "image-type", GST_TYPE_TAG_IMAGE_TYPE, image_type, NULL); |
| } |
| sample = gst_sample_new (image, caps, NULL, image_info); |
| gst_buffer_unref (image); |
| gst_caps_unref (caps); |
| |
| return sample; |
| |
| /* 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; |
| } |
| alloc_failed: |
| { |
| GST_WARNING ("failed to allocate buffer of %d for image", image_data_len); |
| gst_buffer_unref (image); |
| return NULL; |
| } |
| |
| } |