/* * GStreamer * Copyright (C) 2015 Matthew Waters * * 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 #include #include #include /** * SECTION:gstglrenderbuffer * @short_description: memory subclass for GL renderbuffer objects * @see_also: #GstMemory, #GstAllocator * * GstGLRenderbuffer is a #GstGLBaseMemory subclass providing support for * OpenGL renderbuffers. * * #GstGLRenderbuffer is created or wrapped through gst_gl_base_memory_alloc() * with #GstGLRenderbufferAllocationParams. * * Since: 1.10 */ #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0)) #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1)) #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0)) #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0)) #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0)) static GstAllocator *_gl_renderbuffer_allocator; GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_RENDERBUFFER); #define GST_CAT_DEFAULT GST_CAT_GL_RENDERBUFFER G_DEFINE_TYPE (GstGLRenderbufferAllocator, gst_gl_renderbuffer_allocator, GST_TYPE_GL_BASE_MEMORY_ALLOCATOR); static guint _new_renderbuffer (GstGLContext * context, guint format, guint width, guint height) { const GstGLFuncs *gl = context->gl_vtable; guint rbo_id; gl->GenRenderbuffers (1, &rbo_id); gl->BindRenderbuffer (GL_RENDERBUFFER, rbo_id); gl->RenderbufferStorage (GL_RENDERBUFFER, format, width, height); gl->BindRenderbuffer (GL_RENDERBUFFER, 0); return rbo_id; } static gboolean _gl_rbo_create (GstGLRenderbuffer * gl_mem, GError ** error) { if (!gl_mem->renderbuffer_wrapped) { GstGLContext *context = gl_mem->mem.context; GLenum internal_format; GLenum tex_format; GLenum renderbuffer_type; tex_format = gst_gl_format_from_gl_texture_type (gl_mem->renderbuffer_type); renderbuffer_type = GL_UNSIGNED_BYTE; if (gl_mem->renderbuffer_type == GST_VIDEO_GL_TEXTURE_TYPE_RGB16) renderbuffer_type = GL_UNSIGNED_SHORT_5_6_5; internal_format = gst_gl_sized_gl_format_from_gl_format_type (context, tex_format, renderbuffer_type); gl_mem->renderbuffer_id = _new_renderbuffer (context, internal_format, gst_gl_renderbuffer_get_width (gl_mem), gst_gl_renderbuffer_get_height (gl_mem)); GST_CAT_TRACE (GST_CAT_GL_RENDERBUFFER, "Generating renderbuffer id:%u " "format:%u dimensions:%ux%u", gl_mem->renderbuffer_id, internal_format, gst_gl_renderbuffer_get_width (gl_mem), gst_gl_renderbuffer_get_height (gl_mem)); } return TRUE; } static void gst_gl_renderbuffer_init (GstGLRenderbuffer * mem, GstAllocator * allocator, GstMemory * parent, GstGLContext * context, GstVideoGLTextureType renderbuffer_type, GstAllocationParams * params, guint width, guint height, gpointer user_data, GDestroyNotify notify) { gsize size = gst_gl_texture_type_n_bytes (renderbuffer_type) * width * height; mem->renderbuffer_type = renderbuffer_type; mem->width = width; mem->height = height; gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context, params, size, user_data, notify); GST_CAT_DEBUG (GST_CAT_GL_RENDERBUFFER, "new GL renderbuffer context:%" GST_PTR_FORMAT " memory:%p format:%u dimensions:%ux%u ", context, mem, mem->renderbuffer_type, gst_gl_renderbuffer_get_width (mem), gst_gl_renderbuffer_get_height (mem)); } static gpointer _gl_rbo_map (GstGLRenderbuffer * gl_mem, GstMapInfo * info, gsize maxsize) { GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be mapped"); return NULL; } static void _gl_rbo_unmap (GstGLRenderbuffer * gl_mem, GstMapInfo * info) { } static GstMemory * _gl_rbo_copy (GstGLRenderbuffer * src, gssize offset, gssize size) { GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be copied"); return NULL; } static GstMemory * _gl_rbo_alloc (GstAllocator * allocator, gsize size, GstAllocationParams * params) { g_warning ("Use gst_gl_base_memory_alloc to allocate from this allocator"); return NULL; } static void _gl_rbo_destroy (GstGLRenderbuffer * gl_mem) { const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable; if (gl_mem->renderbuffer_id && !gl_mem->renderbuffer_wrapped) gl->DeleteRenderbuffers (1, &gl_mem->renderbuffer_id); } static GstGLRenderbuffer * _default_gl_rbo_alloc (GstGLRenderbufferAllocator * allocator, GstGLRenderbufferAllocationParams * params) { guint alloc_flags = params->parent.alloc_flags; GstGLRenderbuffer *mem; g_return_val_if_fail ((alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) == 0, NULL); mem = g_new0 (GstGLRenderbuffer, 1); if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) { mem->renderbuffer_id = GPOINTER_TO_UINT (params->parent.gl_handle); mem->renderbuffer_wrapped = TRUE; } gst_gl_renderbuffer_init (mem, GST_ALLOCATOR_CAST (allocator), NULL, params->parent.context, params->renderbuffer_type, params->parent.alloc_params, params->width, params->height, params->parent.user_data, params->parent.notify); return mem; } static void gst_gl_renderbuffer_allocator_class_init (GstGLRenderbufferAllocatorClass * klass) { GstGLBaseMemoryAllocatorClass *gl_base; GstAllocatorClass *allocator_class; gl_base = (GstGLBaseMemoryAllocatorClass *) klass; allocator_class = (GstAllocatorClass *) klass; gl_base->alloc = (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_rbo_alloc; gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_rbo_create; gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_rbo_destroy; allocator_class->alloc = _gl_rbo_alloc; } static void gst_gl_renderbuffer_allocator_init (GstGLRenderbufferAllocator * allocator) { GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); alloc->mem_type = GST_GL_RENDERBUFFER_ALLOCATOR_NAME; alloc->mem_map_full = (GstMemoryMapFullFunction) _gl_rbo_map; alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _gl_rbo_unmap; alloc->mem_copy = (GstMemoryCopyFunction) _gl_rbo_copy; GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); } /** * gst_gl_renderbuffer_get_width: * @gl_mem: a #GstGLRenderbuffer * * Returns: the configured width of @gl_mem * * Since: 1.10 */ gint gst_gl_renderbuffer_get_width (GstGLRenderbuffer * gl_mem) { g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); return gl_mem->width; } /** * gst_gl_renderbuffer_get_height: * @gl_mem: a #GstGLRenderbuffer * * Returns: the configured height of @gl_mem * * Since: 1.10 */ gint gst_gl_renderbuffer_get_height (GstGLRenderbuffer * gl_mem) { g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); return gl_mem->height; } /** * gst_gl_renderbuffer_get_type: * @gl_mem: a #GstGLRenderbuffer * * Returns: the #GstVideoGLTextureType of @gl_mem * * Since: 1.10 */ GstVideoGLTextureType gst_gl_renderbuffer_get_type (GstGLRenderbuffer * gl_mem) { g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); return gl_mem->renderbuffer_type; } /** * gst_gl_renderbuffer_get_id: * @gl_mem: a #GstGLRenderbuffer * * Returns: the OpenGL renderbuffer handle of @gl_mem * * Since: 1.10 */ guint gst_gl_renderbuffer_get_id (GstGLRenderbuffer * gl_mem) { g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); return gl_mem->renderbuffer_id; } /** * gst_gl_renderbuffer_init_once: * * Initializes the GL Base Texture allocator. It is safe to call this function * multiple times. This must be called before any other GstGLRenderbuffer operation. * * Since: 1.10 */ void gst_gl_renderbuffer_init_once (void) { static volatile gsize _init = 0; if (g_once_init_enter (&_init)) { gst_gl_base_memory_init_once (); GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_RENDERBUFFER, "glrenderbuffermemory", 0, "OpenGL Renderbuffer memory"); _gl_renderbuffer_allocator = g_object_new (GST_TYPE_GL_RENDERBUFFER_ALLOCATOR, NULL); GST_OBJECT_FLAG_SET (_gl_renderbuffer_allocator, GST_OBJECT_FLAG_MAY_BE_LEAKED); gst_allocator_register (GST_GL_RENDERBUFFER_ALLOCATOR_NAME, _gl_renderbuffer_allocator); g_once_init_leave (&_init, 1); } } /** * gst_is_gl_renderbuffer: * @mem:a #GstMemory * * Returns: whether the memory at @mem is a #GstGLRenderbuffer * * Since: 1.10 */ gboolean gst_is_gl_renderbuffer (GstMemory * mem) { return mem != NULL && mem->allocator != NULL && g_type_is_a (G_OBJECT_TYPE (mem->allocator), GST_TYPE_GL_RENDERBUFFER_ALLOCATOR); } G_DEFINE_BOXED_TYPE (GstGLRenderbufferAllocationParams, gst_gl_renderbuffer_allocation_params, (GBoxedCopyFunc) gst_gl_allocation_params_copy, (GBoxedFreeFunc) gst_gl_allocation_params_free); static void _gst_gl_rb_alloc_params_free_data (GstGLRenderbufferAllocationParams * params) { gst_gl_allocation_params_free_data (¶ms->parent); } static void _gst_gl_rb_alloc_params_copy_data (GstGLRenderbufferAllocationParams * src_vid, GstGLRenderbufferAllocationParams * dest_vid) { GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid; GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid; gst_gl_allocation_params_copy_data (src, dest); dest_vid->renderbuffer_type = src_vid->renderbuffer_type; dest_vid->width = src_vid->width; dest_vid->height = src_vid->height; } static gboolean _gst_gl_renderbuffer_allocation_params_init_full (GstGLRenderbufferAllocationParams * params, gsize struct_size, guint alloc_flags, GstGLAllocationParamsCopyFunc copy, GstGLAllocationParamsFreeFunc free, GstGLContext * context, GstAllocationParams * alloc_params, guint width, guint height, GstVideoGLTextureType renderbuffer_type, gpointer wrapped_data, gpointer gl_handle, gpointer user_data, GDestroyNotify notify) { g_return_val_if_fail (params != NULL, FALSE); g_return_val_if_fail (copy != NULL, FALSE); g_return_val_if_fail (free != NULL, FALSE); g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); memset (params, 0, sizeof (*params)); if (!gst_gl_allocation_params_init ((GstGLAllocationParams *) params, struct_size, alloc_flags, copy, free, context, 0, alloc_params, wrapped_data, gl_handle, user_data, notify)) return FALSE; params->renderbuffer_type = renderbuffer_type; params->width = width; params->height = height; return TRUE; } /** * gst_gl_renderbuffer_allocation_params_new: * @context: a #GstGLContext * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture * @width: the width of the renderbuffer * @height: the height of the renderbuffer * @renderbuffer_type: the #GstVideoGLTextureType for the created textures * * Returns: a new #GstGLRenderbufferAllocationParams for allocating #GstGLRenderbuffer's * * Since: 1.10 */ GstGLRenderbufferAllocationParams * gst_gl_renderbuffer_allocation_params_new (GstGLContext * context, GstAllocationParams * alloc_params, GstVideoGLTextureType renderbuffer_type, guint width, guint height) { GstGLRenderbufferAllocationParams *params = g_new0 (GstGLRenderbufferAllocationParams, 1); if (!_gst_gl_renderbuffer_allocation_params_init_full (params, sizeof (GstGLRenderbufferAllocationParams), GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC | GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data, (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data, context, alloc_params, width, height, renderbuffer_type, NULL, 0, NULL, NULL)) { g_free (params); return NULL; } return params; } /** * gst_gl_renderbuffer_allocation_params_new_wrapped: * @context: a #GstGLContext * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id * @width: the width of the renderbuffer * @height: the height of the renderbuffer * @renderbuffer_type: the #GstVideoGLTextureType for @tex_id * @gl_handle: the GL handle to wrap * @user_data: (allow-none): user data to call @notify with * @notify: (allow-none): a #GDestroyNotify * * Returns: a new #GstGLRenderbufferAllocationParams for wrapping @gl_handle as a * renderbuffer * * Since: 1.10 */ GstGLRenderbufferAllocationParams * gst_gl_renderbuffer_allocation_params_new_wrapped (GstGLContext * context, GstAllocationParams * alloc_params, GstVideoGLTextureType renderbuffer_type, guint width, guint height, gpointer gl_handle, gpointer user_data, GDestroyNotify notify) { GstGLRenderbufferAllocationParams *params = g_new0 (GstGLRenderbufferAllocationParams, 1); if (!_gst_gl_renderbuffer_allocation_params_init_full (params, sizeof (GstGLRenderbufferAllocationParams), GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE | GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data, (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data, context, alloc_params, width, height, renderbuffer_type, NULL, gl_handle, user_data, notify)) { g_free (params); return NULL; } return params; }