/* 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 "gstcairobuffer.h" #include "gstcairobuffer-private.h" #include "gstcairoformat-private.h" /** * SECTION:gstcairobuffer * @title: Buffers * @short_description: using video data * * This section describes how to modify buffers when using the GStreamer * Cairo library. * * If at all possible, modifying buffers should be done using the Cairo * library and without accessing the buffer data directly. This allows * high performance and ensures portability of elements. * * Rendering is done by acquiring the #cairo_surface_t for all the buffers * involved with gst_cairo_create_surface() and then using the Cairo API * for modifications. Afterwards, all surfaces should be discarded. Do not * keep a reference to surfaces. If you want to keep references, please * keep the buffer around and use gst_cairo_create_surface() again later. * * To create buffers for use in your element, use * gst_cairo_pad_alloc_buffer(). This way, you'll automatically take * advantage of hardware acceleration provided by the sink elements. * * If you are writing sink or source elements, the GStreamer Cairo library * provides a way to create buffers using gst_cairo_buffer_new_similar() and * surfaces using gst_cairo_create_similar_surface(). These functions are * similar to cairo_surface_create_similar() in that they take extra steps * to provide buffers or surfaces that are as compatible as possible with * the provided surface and can take full advatange of hardware acceleration. */ G_DEFINE_TYPE (GstCairoBuffer, gst_cairo_buffer, GST_TYPE_BUFFER) static void gst_cairo_buffer_finalize (GstMiniObject * object) { GstCairoBuffer *cbuffer = GST_CAIRO_BUFFER (object); gst_cairo_format_free (cbuffer->format); cairo_surface_destroy (cbuffer->surface); GST_MINI_OBJECT_CLASS (gst_cairo_buffer_parent_class)->finalize (object); } static void gst_cairo_buffer_class_init (GstCairoBufferClass * klass) { GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (klass); mini_object_class->finalize = gst_cairo_buffer_finalize; } static void gst_cairo_buffer_init (GstCairoBuffer * buffer) { } /** * gst_cairo_buffer_is_fixed: * @buffer: a buffer * * Checks if a buffer is fixed. Non-fixed buffers can change the format in * use. See gst_cairo_buffer_set_format() for examples of where this is * relevant. * * Returns: %TRUE if the format of the buffer can still be changed **/ gboolean gst_cairo_buffer_is_fixed (GstBuffer * buffer) { g_return_val_if_fail (GST_IS_BUFFER (buffer), TRUE); return !GST_IS_CAIRO_BUFFER (buffer) || GST_CAIRO_BUFFER (buffer)->fixed; } /** * gst_cairo_buffer_set_format: * @buffer: a buffer that is not fixed * @format: the format to use on @buffer * * Sets the format to use for a non-fixed buffer. This function is supposed * to be used in buffer allocation functions (see * gst_pad_set_bufferalloc_function()) to update the format used by the * buffer. This allows forwarding allocations of cairo buffers through a * pipeline even when the caps change. **/ void gst_cairo_buffer_set_format (GstBuffer * buffer, const GstCairoFormat * format) { GstCairoBuffer *cbuffer; g_return_if_fail (!gst_cairo_buffer_is_fixed (buffer)); g_return_if_fail (format != NULL); g_return_if_fail (gst_cairo_format_is_native (format)); cbuffer = GST_CAIRO_BUFFER (buffer); gst_cairo_format_free (cbuffer->format); cbuffer->format = gst_cairo_format_copy (format); GST_BUFFER_SIZE (cbuffer) = 4 * gst_cairo_format_get_width (format) * gst_cairo_format_get_height (format); } /** * gst_cairo_create_surface: * @buffer: buffer to create surface from * @format: format of the @buffer * * Creates a cairo surface from the given @buffer. The @buffer must conform * to @format. You may only modify the surface and use it with * cairo_create() if the @buffer is writable. Using it as a source for * drawing operations is fine in all cases. * * Returns: A new surface referencing the contents of @buffer. * Use cairo_surface_destroy() after use. **/ cairo_surface_t * gst_cairo_create_surface (GstBuffer * buffer, const GstCairoFormat * format) { cairo_surface_t *result; g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); g_return_val_if_fail (format != NULL, NULL); if (GST_IS_CAIRO_BUFFER (buffer)) { GstCairoBuffer *cbuffer = GST_CAIRO_BUFFER (buffer); if (cbuffer->fixed) { result = cairo_surface_reference (cbuffer->surface); } else { result = cairo_surface_create_similar (cbuffer->surface, cairo_surface_get_content (cbuffer->surface), cbuffer->format->width, cbuffer->format->height); cairo_surface_destroy (cbuffer->surface); cbuffer->surface = cairo_surface_reference (result); cbuffer->fixed = TRUE; } /* sanity checks */ g_return_val_if_fail (gst_cairo_format_get_surface_type (format) == 0 || gst_cairo_format_get_surface_type (format) == cairo_surface_get_type (result), result); } else { static cairo_user_data_key_t buffer_free; guint8 *data = GST_BUFFER_DATA (buffer); char *planes[4]; int strides[4]; guint i; g_return_val_if_fail (gst_cairo_format_get_surface_type (format) == CAIRO_SURFACE_TYPE_IMAGE, NULL); for (i = 0; i < format->n_planes; i++) { planes[i] = (char *) (data + gst_cairo_format_get_offset (format, i)); strides[i] = gst_cairo_format_get_stride (format, i); } result = cairo_image_surface_create_planar (gst_cairo_format_get_cairo_format (format), gst_cairo_format_get_color_space (format), format->width, format->height, format->n_planes, planes, strides); gst_buffer_ref (buffer); cairo_surface_set_user_data (result, &buffer_free, buffer, (cairo_destroy_func_t) gst_buffer_unref); } return result; } /** * gst_cairo_buffer_new: * @format: format to create a buffer for * * This is a convenience function for fallback code. Creates a buffer * of the correct size for the given format. * * This function does never create any accelerated buffer. It * always creates an image surface. * * Returns: a new buffer **/ GstBuffer * gst_cairo_buffer_new (const GstCairoFormat * format) { GstBuffer *buffer; gsize size; g_return_val_if_fail (format != NULL, NULL); g_return_val_if_fail (gst_cairo_format_get_surface_type (format) == CAIRO_SURFACE_TYPE_IMAGE, NULL); /* Cairo guarantees that new surfaces will always be zeroed. We keep * this promise as to not introduce weird unexplainable rendering * errors. * If people want to avoid zeroing the data, they can malloc the buffer * themselves. */ size = gst_cairo_format_get_buffer_size (format); buffer = gst_buffer_new (); GST_BUFFER_MALLOCDATA (buffer) = g_malloc0 (size); GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer); GST_BUFFER_SIZE (buffer) = size; return buffer; } /** * gst_cairo_buffer_new_similar: * @surface: surface to create a buffer for * @format: format to create a buffer for * * Tries to create a buffer best suited for the given @format and output * on @surface. This function tries to achieve highest possible performance * when later using the surface returned from gst_cairo_create_surface() * for rendering the buffer. * * Usually, a special buffer will be created that will do all drawing * operations using the same acceleration method as @surface. However, if this * can not be achieved because things like the @format or compile-time options * do not allow it, a fallback path is taken and a regular buffer will be * created. * * Returns: a new buffer **/ GstBuffer * gst_cairo_buffer_new_similar (cairo_surface_t * surface, const GstCairoFormat * format) { GstCairoBuffer *buffer; g_return_val_if_fail (surface != NULL, NULL); g_return_val_if_fail (format != NULL, NULL); g_return_val_if_fail (gst_cairo_format_get_surface_type (format) == CAIRO_SURFACE_TYPE_IMAGE || gst_cairo_format_get_surface_type (format) == cairo_surface_get_type (surface), NULL); if (!gst_cairo_format_is_native (format)) return gst_cairo_buffer_new (format); buffer = (GstCairoBuffer *) g_type_create_instance (GST_TYPE_CAIRO_BUFFER); buffer->surface = cairo_surface_reference (surface); gst_cairo_buffer_set_format ((GstBuffer *) buffer, format); return (GstBuffer *) buffer; }