/* GStreamer * Copyright (C) 2009 Benjamin Otte * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "gstcairocaps.h" #include #include #include "gstcairocapsany.h" /** * SECTION:gstcairocaps * @title: Caps * @short_description: caps negotiation and format handling * * This section describes how to handle #GstCaps inside an element that * uses this library. * * Fixed caps as passed to a setcaps function are converted into a * #GstCairoFormat using gst_cairo_format_new(), which is a simple and * fast wrapper around a static caps. It is suggested that you do not store * things like framerate, width or height, but use a #GstCairoFormat instead. * These formats are also required when converting a #GstBuffer into a * #cairo_surface_t. * * The GStreamer Cairo library also provides some utility function that * help during caps negotiation. Those are listed here, too. The most * important one certainly is gst_cairo_caps_expand() that eases caps * transformations. */ /** * gst_cairo_caps_any: * * Returns a #GstCaps that references all formats that can be handled by * this library. The returned caps will not be writable. Use * gst_caps_make_writable() if you want to change them. * The caps returned by this function can change between versions * of this library as new formats are added. * * Returns: a #GstCaps, use gst_caps_unref() to get rid of it. The caps will * not be writable. **/ GstCaps * gst_cairo_caps_any (void) { static gsize any = 0; if (g_once_init_enter (&any)) { GstCaps *caps = gst_caps_from_string (GST_CAIRO_CAPS_ANY); g_once_init_leave (&any, GPOINTER_TO_SIZE (caps)); } return gst_caps_copy (GSIZE_TO_POINTER (any)); } static gboolean gst_cairo_structure_is_native (const GstStructure * check) { static GQuark native_name = 0; if (!native_name) native_name = g_quark_from_static_string ("video/x-cairo"); return gst_structure_get_name_id (check) == native_name; } static void gst_cairo_value_init_from_surface_types (GValue * value, const cairo_surface_type_t * types) { GValue append = { 0, }; if (types == NULL || types[0] == 0) { g_value_init (value, G_TYPE_INT); /* set to 0 automatically */ return; } g_value_init (value, GST_TYPE_LIST); g_value_init (&append, G_TYPE_INT); gst_value_list_append_value (value, &append); while (*types != 0) { g_value_set_int (&append, *types); gst_value_list_append_value (value, &append); types++; } g_value_unset (&append); } static GstCaps * gst_cairo_caps_set_surface_types_by_value (GstCaps * caps, const GValue * value, gboolean expand) { guint i; for (i = 0; i < gst_caps_get_size (caps); i++) { GstStructure *check = gst_caps_get_structure (caps, i); if (!gst_cairo_structure_is_native (check)) continue; if (expand) { GValue unioned = { 0, }; gst_value_union (&unioned, gst_structure_get_value (check, "surface-type"), value); gst_structure_set_value (check, "surface-type", &unioned); g_value_unset (&unioned); } else { gst_structure_set_value (check, "surface-type", value); } } return caps; } static GstCaps * gst_cairo_caps_get_default (void) { static gsize default_ = 0; if (g_once_init_enter (&default_)) { GstCaps *caps = gst_caps_make_writable (gst_cairo_caps_any ()); GValue value = { 0, }; g_value_init (&value, G_TYPE_INT); caps = gst_cairo_caps_set_surface_types_by_value (caps, &value, FALSE); g_value_unset (&value); g_once_init_leave (&default_, GPOINTER_TO_SIZE (caps)); } return gst_caps_ref (GSIZE_TO_POINTER (default_)); } static GstCaps * gst_cairo_caps_default_for_value (const GValue * supported_types) { GstCaps *caps = gst_cairo_caps_get_default (); caps = gst_caps_make_writable (caps); gst_cairo_caps_set_surface_types_by_value (caps, supported_types, FALSE); return caps; } /** * gst_cairo_caps_default: * @types: An array of all supported types ending with * %CAIRO_SURFACE_TYPE_IMAGE or %NULL for just the default type. * * Returns a #GstCaps that references all formats that can be created by * gst_cairo_buffer_new(). This does not include native formats that require * special elements to handle them. If you write a sink element, you want to * use these caps instead of gst_cairo_caps_any(). For elements that don't * produce buffers themselves * The returned caps might not be writable. Use gst_caps_make_writable() if * you want to change them. * The caps returned by this function can change between versions * of this library as new formats are added. * * Returns: a #GstCaps, use gst_caps_unref() to get rid of it. The caps will * not be writable. **/ GstCaps * gst_cairo_caps_default (const cairo_surface_type_t * types) { GValue value = { 0, }; GstCaps *caps; if (types == NULL) return gst_cairo_caps_get_default (); gst_cairo_value_init_from_surface_types (&value, types); caps = gst_cairo_caps_default_for_value (&value); g_value_unset (&value); return caps; } /** * gst_cairo_caps_expand: * @caps: a valid cairo caps to expand * @expand: parts of the caps that should be expanded * * Expands the given @caps so that all options set in @expand are expanded * to all their options. This function is useful in caps negotiation when * your element is transforming the Cairo data given to it, like in a * colorspace or scaling element. * * Returns: a new caps with the expanded fields. **/ GstCaps * gst_cairo_caps_expand (const GstCaps * caps, GstCairoFormatOption expand) { g_return_val_if_fail (GST_IS_CAPS (caps), NULL); return gst_cairo_caps_expand_for_surface_types (caps, expand, NULL); } /** * gst_cairo_caps_expand_for_surface_types: * @caps: a valid cairo caps to expand * @expand: parts of the caps that should be expanded * @types: An array of all supported types ending with * %CAIRO_SURFACE_TYPE_IMAGE or %NULL for just the default type. * * This function is a special version of gst_cairo_caps_expand() that * when @expand includes %GST_CAIRO_FORMAT_FORMAT expands to all of the * @types given instead of just the default. * * Returns: a new caps with the expanded fields. **/ GstCaps * gst_cairo_caps_expand_for_surface_types (const GstCaps * caps, GstCairoFormatOption expand, const cairo_surface_type_t * types) { GstCaps *expanded; GValue types_value = { 0, }; guint i; g_return_val_if_fail (GST_IS_CAPS (caps), NULL); gst_cairo_value_init_from_surface_types (&types_value, types); /* start with a copy of the current caps - we prefer unchanged caps */ expanded = gst_caps_copy (caps); for (i = 0; i < gst_caps_get_size (caps); i++) { GstStructure *expand_me = gst_caps_get_structure (caps, i); GstCaps *copy; if (expand & GST_CAIRO_FORMAT_FORMAT) { if (gst_cairo_structure_is_native (expand_me)) { GValue intersect = { 0, }; const GValue *structure_types; structure_types = gst_structure_get_value (expand_me, "surface-type"); if (gst_value_intersect (&intersect, structure_types, &types_value)) { copy = gst_cairo_caps_default_for_value (&types_value); copy = gst_cairo_caps_set_surface_types_by_value (copy, structure_types, TRUE); g_value_unset (&intersect); } else { copy = gst_caps_copy_nth (caps, i); } } else { copy = gst_cairo_caps_default_for_value (&types_value); } } else { copy = gst_caps_copy_nth (caps, i); } if (expand & GST_CAIRO_FORMAT_WIDTH) { gst_caps_set_simple (copy, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); } else { gst_caps_set_value (copy, "width", gst_structure_get_value (expand_me, "width")); } if (expand & GST_CAIRO_FORMAT_HEIGHT) { gst_caps_set_simple (copy, "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); } else { gst_caps_set_value (copy, "height", gst_structure_get_value (expand_me, "height")); } if (expand & GST_CAIRO_FORMAT_FRAMERATE) { gst_caps_set_simple (copy, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); } else { gst_caps_set_value (copy, "framerate", gst_structure_get_value (expand_me, "framerate")); } if (expand & GST_CAIRO_FORMAT_PAR) { guint j; for (j = 0; j < gst_caps_get_size (copy); j++) { gst_structure_remove_field (gst_caps_get_structure (copy, j), "pixel-aspect-ratio"); } } else { const GValue *value = gst_structure_get_value (expand_me, "pixel-aspect-ratio"); if (value) gst_caps_set_value (copy, "pixel-aspect-ratio", value); } gst_caps_append (expanded, copy); } g_value_unset (&types_value); return expanded; }