| /* |
| * GStreamer |
| * Copyright (C) 2015 Matthew Waters <matthew@centricular.com> |
| * |
| * 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 <string.h> |
| |
| #include "gstglmemory.h" |
| |
| #include "gl.h" |
| #include "gstglfuncs.h" |
| |
| /** |
| * SECTION:gstglmemory |
| * @title: GstGLMemory |
| * @short_description: memory subclass for GL textures |
| * @see_also: #GstMemory, #GstAllocator, #GstGLBufferPool |
| * |
| * GstGLMemory is a #GstGLBaseMemory subclass providing support for the mapping of |
| * OpenGL textures. |
| * |
| * #GstGLMemory is created or wrapped through gst_gl_base_memory_alloc() |
| * with #GstGLVideoAllocationParams. |
| * |
| * Data is uploaded or downloaded from the GPU as is necessary. |
| */ |
| |
| #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)) |
| |
| #define GL_MEM_WIDTH(gl_mem) _get_plane_width (&gl_mem->info, gl_mem->plane) |
| #define GL_MEM_HEIGHT(gl_mem) _get_plane_height (&gl_mem->info, gl_mem->plane) |
| #define GL_MEM_STRIDE(gl_mem) GST_VIDEO_INFO_PLANE_STRIDE (&gl_mem->info, gl_mem->plane) |
| |
| static GstAllocator *_gl_memory_allocator; |
| |
| GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_MEMORY); |
| #define GST_CAT_DEFAULT GST_CAT_GL_MEMORY |
| |
| /* compatability definitions... */ |
| #ifndef GL_UNPACK_ROW_LENGTH |
| #define GL_UNPACK_ROW_LENGTH 0x0CF2 |
| #endif |
| |
| #ifndef GL_TEXTURE_RECTANGLE |
| #define GL_TEXTURE_RECTANGLE 0x84F5 |
| #endif |
| #ifndef GL_TEXTURE_EXTERNAL_OES |
| #define GL_TEXTURE_EXTERNAL_OES 0x8D65 |
| #endif |
| |
| #ifndef GL_READ_FRAMEBUFFER |
| #define GL_READ_FRAMEBUFFER 0x8CA8 |
| #endif |
| #ifndef GL_DRAW_FRAMEBUFFER |
| #define GL_DRAW_FRAMEBUFFER 0x8CA9 |
| #endif |
| |
| G_DEFINE_TYPE (GstGLMemoryAllocator, gst_gl_memory_allocator, |
| GST_TYPE_GL_BASE_MEMORY_ALLOCATOR); |
| |
| typedef struct |
| { |
| /* in */ |
| GstGLMemory *src; |
| GstGLFormat out_format; |
| guint out_width, out_height; |
| GstGLTextureTarget tex_target; |
| GstGLFormat tex_format; |
| /* inout */ |
| guint tex_id; |
| /* out */ |
| gboolean result; |
| } GstGLMemoryCopyParams; |
| |
| static inline guint |
| _get_plane_width (GstVideoInfo * info, guint plane) |
| { |
| if (GST_VIDEO_INFO_IS_YUV (info)) |
| /* For now component width and plane width are the same and the |
| * plane-component mapping matches |
| */ |
| return GST_VIDEO_INFO_COMP_WIDTH (info, plane); |
| else /* RGB, GRAY */ |
| return GST_VIDEO_INFO_WIDTH (info); |
| } |
| |
| static inline guint |
| _get_plane_height (GstVideoInfo * info, guint plane) |
| { |
| if (GST_VIDEO_INFO_IS_YUV (info)) |
| /* For now component width and plane width are the same and the |
| * plane-component mapping matches |
| */ |
| return GST_VIDEO_INFO_COMP_HEIGHT (info, plane); |
| else /* RGB, GRAY */ |
| return GST_VIDEO_INFO_HEIGHT (info); |
| } |
| |
| static inline void |
| _calculate_unpack_length (GstGLMemory * gl_mem, GstGLContext * context) |
| { |
| guint n_gl_bytes; |
| guint tex_type; |
| |
| gl_mem->tex_scaling[0] = 1.0f; |
| gl_mem->tex_scaling[1] = 1.0f; |
| gl_mem->unpack_length = 1; |
| gl_mem->tex_width = GL_MEM_WIDTH (gl_mem); |
| |
| tex_type = GL_UNSIGNED_BYTE; |
| if (gl_mem->tex_format == GST_GL_RGB565) |
| tex_type = GL_UNSIGNED_SHORT_5_6_5; |
| |
| n_gl_bytes = gst_gl_format_type_n_bytes (gl_mem->tex_format, tex_type); |
| if (n_gl_bytes == 0) { |
| GST_ERROR ("Unsupported texture type %d", gl_mem->tex_format); |
| return; |
| } |
| |
| if (USING_OPENGL (context) || USING_GLES3 (context) |
| || USING_OPENGL3 (context)) { |
| gl_mem->unpack_length = GL_MEM_STRIDE (gl_mem) / n_gl_bytes; |
| } else if (USING_GLES2 (context)) { |
| guint j = 8; |
| |
| while (j >= n_gl_bytes) { |
| /* GST_ROUND_UP_j(GL_MEM_WIDTH (gl_mem) * n_gl_bytes) */ |
| guint round_up_j = |
| ((GL_MEM_WIDTH (gl_mem) * n_gl_bytes) + j - 1) & ~(j - 1); |
| |
| if (round_up_j == GL_MEM_STRIDE (gl_mem)) { |
| GST_CAT_LOG (GST_CAT_GL_MEMORY, "Found alignment of %u based on " |
| "width (with plane width:%u, plane stride:%u and pixel stride:%u. " |
| "RU%u(%u*%u) = %u)", j, GL_MEM_WIDTH (gl_mem), |
| GL_MEM_STRIDE (gl_mem), n_gl_bytes, j, GL_MEM_WIDTH (gl_mem), |
| n_gl_bytes, round_up_j); |
| |
| gl_mem->unpack_length = j; |
| break; |
| } |
| j >>= 1; |
| } |
| |
| if (j < n_gl_bytes) { |
| /* Failed to find a suitable alignment, try based on plane_stride and |
| * scale in the shader. Useful for alignments that are greater than 8. |
| */ |
| j = 8; |
| |
| while (j >= n_gl_bytes) { |
| /* GST_ROUND_UP_j((GL_MEM_STRIDE (gl_mem)) */ |
| guint round_up_j = ((GL_MEM_STRIDE (gl_mem)) + j - 1) & ~(j - 1); |
| |
| if (round_up_j == (GL_MEM_STRIDE (gl_mem))) { |
| GST_CAT_LOG (GST_CAT_GL_MEMORY, "Found alignment of %u based " |
| "on stride (with plane stride:%u and pixel stride:%u. " |
| "RU%u(%u) = %u)", j, GL_MEM_STRIDE (gl_mem), n_gl_bytes, j, |
| GL_MEM_STRIDE (gl_mem), round_up_j); |
| |
| gl_mem->unpack_length = j; |
| gl_mem->tex_scaling[0] = |
| (gfloat) (GL_MEM_WIDTH (gl_mem) * n_gl_bytes) / |
| (gfloat) GL_MEM_STRIDE (gl_mem); |
| gl_mem->tex_width = GL_MEM_STRIDE (gl_mem) / n_gl_bytes; |
| break; |
| } |
| j >>= 1; |
| } |
| |
| if (j < n_gl_bytes) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to find matching " |
| "alignment. Image may look corrupted. plane width:%u, " |
| "plane stride:%u and pixel stride:%u", GL_MEM_WIDTH (gl_mem), |
| GL_MEM_STRIDE (gl_mem), n_gl_bytes); |
| } |
| } |
| } |
| |
| if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_RECTANGLE) { |
| guint w_sub = |
| GST_VIDEO_FORMAT_INFO_W_SUB (gl_mem->info.finfo, gl_mem->plane); |
| guint h_sub = |
| GST_VIDEO_FORMAT_INFO_H_SUB (gl_mem->info.finfo, gl_mem->plane); |
| |
| if (w_sub) |
| gl_mem->tex_scaling[0] /= (1 << w_sub); |
| if (h_sub) |
| gl_mem->tex_scaling[1] /= (1 << h_sub); |
| } |
| } |
| |
| static guint |
| _new_texture (GstGLContext * context, guint target, guint internal_format, |
| guint format, guint type, guint width, guint height) |
| { |
| const GstGLFuncs *gl = context->gl_vtable; |
| guint tex_id; |
| |
| gl->GenTextures (1, &tex_id); |
| gl->BindTexture (target, tex_id); |
| if (target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE) |
| gl->TexImage2D (target, 0, internal_format, width, height, 0, format, type, |
| NULL); |
| |
| gl->TexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| gl->TexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| gl->TexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| gl->TexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| gl->BindTexture (target, 0); |
| |
| return tex_id; |
| } |
| |
| static gboolean |
| _gl_tex_create (GstGLMemory * gl_mem, GError ** error) |
| { |
| GstGLContext *context = gl_mem->mem.context; |
| GLenum internal_format; |
| GLenum tex_format; |
| GLenum tex_type; |
| |
| tex_format = gl_mem->tex_format; |
| tex_type = GL_UNSIGNED_BYTE; |
| if (gl_mem->tex_format == GST_GL_RGB565) { |
| tex_format = GST_GL_RGB; |
| tex_type = GL_UNSIGNED_SHORT_5_6_5; |
| } |
| |
| internal_format = |
| gst_gl_sized_gl_format_from_gl_format_type (context, tex_format, |
| tex_type); |
| |
| if (!gl_mem->texture_wrapped) { |
| gl_mem->tex_id = |
| _new_texture (context, gst_gl_texture_target_to_gl (gl_mem->tex_target), |
| internal_format, tex_format, tex_type, gl_mem->tex_width, |
| GL_MEM_HEIGHT (gl_mem)); |
| |
| GST_TRACE ("Generating texture id:%u format:%u type:%u dimensions:%ux%u", |
| gl_mem->tex_id, tex_format, tex_type, gl_mem->tex_width, |
| GL_MEM_HEIGHT (gl_mem)); |
| } |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_gl_memory_init: |
| * @mem: the #GstGLBaseMemory to initialize |
| * @allocator: the #GstAllocator to initialize with |
| * @parent: (allow-none): the parent #GstMemory to initialize with |
| * @context: the #GstGLContext to initialize with |
| * @target: the #GstGLTextureTarget for this #GstGLMemory |
| * @tex_format: the #GstGLFormat for this #GstGLMemory |
| * @params: (allow-none): the @GstAllocationParams to initialize with |
| * @info: the #GstVideoInfo for this #GstGLMemory |
| * @plane: the plane number (starting from 0) for this #GstGLMemory |
| * @valign: (allow-none): optional #GstVideoAlignment parameters |
| * @notify: (allow-none): a #GDestroyNotify |
| * @user_data: (allow-none): user data to call @notify with |
| * |
| * Initializes @mem with the required parameters. @info is assumed to have |
| * already have been modified with gst_video_info_align(). |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_gl_memory_init (GstGLMemory * mem, GstAllocator * allocator, |
| GstMemory * parent, GstGLContext * context, GstGLTextureTarget target, |
| GstGLFormat tex_format, GstAllocationParams * params, |
| GstVideoInfo * info, guint plane, GstVideoAlignment * valign, |
| gpointer user_data, GDestroyNotify notify) |
| { |
| const gchar *target_str; |
| gsize size; |
| |
| g_return_if_fail (plane < GST_VIDEO_INFO_N_PLANES (info)); |
| |
| mem->info = *info; |
| if (valign) |
| mem->valign = *valign; |
| else |
| gst_video_alignment_reset (&mem->valign); |
| |
| /* double-check alignment requirements (caller should've taken care of this) */ |
| if (params) { |
| guint max_align, n; |
| |
| max_align = gst_memory_alignment; |
| max_align |= params->align; |
| for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n) |
| max_align |= mem->valign.stride_align[n]; |
| |
| if (params->align < max_align && max_align > gst_memory_alignment) { |
| GST_WARNING ("allocation params alignment %" G_GSIZE_FORMAT " is smaller " |
| "than the max required video alignment %u", params->align, max_align); |
| } |
| } |
| |
| size = gst_gl_get_plane_data_size (info, valign, plane); |
| |
| mem->tex_target = target; |
| mem->tex_format = tex_format; |
| mem->plane = plane; |
| |
| _calculate_unpack_length (mem, context); |
| |
| gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context, |
| params, size, user_data, notify); |
| |
| target_str = gst_gl_texture_target_to_string (target); |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY, "new GL texture context:%" |
| GST_PTR_FORMAT " memory:%p target:%s format:%u dimensions:%ux%u " |
| "stride:%u size:%" G_GSIZE_FORMAT, context, mem, target_str, |
| mem->tex_format, mem->tex_width, GL_MEM_HEIGHT (mem), GL_MEM_STRIDE (mem), |
| mem->mem.mem.size); |
| } |
| |
| /** |
| * gst_gl_memory_read_pixels: |
| * @gl_mem: a #GstGLMemory |
| * @read_pointer: the data pointer to pass to glReadPixels |
| * |
| * Reads the texture in #GstGLMemory into @read_pointer if no buffer is bound |
| * to %GL_PIXEL_PACK_BUFFER. Otherwise @read_pointer is the byte offset into |
| * the currently bound %GL_PIXEL_PACK_BUFFER buffer to store the result of |
| * glReadPixels. See the OpenGL specification for glReadPixels for more |
| * details. |
| * |
| * Returns: whether theread operation succeeded |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_memory_read_pixels (GstGLMemory * gl_mem, gpointer read_pointer) |
| { |
| GstGLContext *context = gl_mem->mem.context; |
| const GstGLFuncs *gl = context->gl_vtable; |
| guint format, type; |
| guint fbo; |
| |
| format = gl_mem->tex_format; |
| type = GL_UNSIGNED_BYTE; |
| if (gl_mem->tex_format == GST_GL_RGB565) |
| type = GL_UNSIGNED_SHORT_5_6_5; |
| |
| /* FIXME: avoid creating a framebuffer every download/copy */ |
| gl->GenFramebuffers (1, &fbo); |
| gl->BindFramebuffer (GL_FRAMEBUFFER, fbo); |
| |
| gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| gst_gl_texture_target_to_gl (gl_mem->tex_target), gl_mem->tex_id, 0); |
| |
| if (!gst_gl_context_check_framebuffer_status (context, GL_FRAMEBUFFER)) { |
| GST_CAT_WARNING (GST_CAT_GL_MEMORY, |
| "Could not create framebuffer to read pixels for memory %p", gl_mem); |
| gl->DeleteFramebuffers (1, &fbo); |
| return FALSE; |
| } |
| |
| if (USING_GLES2 (context) || USING_GLES3 (context)) { |
| if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) { |
| /* explicitly supported */ |
| } else { |
| gint supported_format, supported_type; |
| |
| gl->GetIntegerv (GL_IMPLEMENTATION_COLOR_READ_FORMAT, &supported_format); |
| gl->GetIntegerv (GL_IMPLEMENTATION_COLOR_READ_TYPE, &supported_type); |
| |
| if (supported_format != format || supported_type != type) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "cannot read pixels with " |
| "unsupported format and type. Supported format 0x%x type 0x%x", |
| supported_format, supported_type); |
| gl->BindFramebuffer (GL_FRAMEBUFFER, 0); |
| gl->DeleteFramebuffers (1, &fbo); |
| return FALSE; |
| } |
| } |
| } |
| |
| gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (gl_mem)->query, |
| GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s", "glReadPixels took"); |
| gl->ReadPixels (0, 0, gl_mem->tex_width, GL_MEM_HEIGHT (gl_mem), format, |
| type, read_pointer); |
| gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (gl_mem)->query); |
| |
| gl->BindFramebuffer (GL_FRAMEBUFFER, 0); |
| gl->DeleteFramebuffers (1, &fbo); |
| |
| return TRUE; |
| } |
| |
| static gpointer |
| _gl_tex_download_get_tex_image (GstGLMemory * gl_mem, GstMapInfo * info, |
| gsize size) |
| { |
| GstGLContext *context = gl_mem->mem.context; |
| const GstGLFuncs *gl = context->gl_vtable; |
| |
| if (size != -1 && size != ((GstMemory *) gl_mem)->maxsize) |
| return NULL; |
| |
| if (USING_GLES2 (context) || USING_GLES3 (context)) |
| return NULL; |
| |
| /* taken care of by read pixels */ |
| if (gl_mem->tex_format != GST_GL_LUMINANCE |
| && gl_mem->tex_format != GST_GL_LUMINANCE_ALPHA) |
| return NULL; |
| |
| if (info->flags & GST_MAP_READ |
| && GST_MEMORY_FLAG_IS_SET (gl_mem, |
| GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) { |
| guint format, type; |
| guint target; |
| |
| GST_CAT_TRACE (GST_CAT_GL_MEMORY, "attempting download of texture %u " |
| "using glGetTexImage", gl_mem->tex_id); |
| |
| format = gl_mem->tex_format; |
| type = GL_UNSIGNED_BYTE; |
| if (gl_mem->tex_format == GST_GL_RGB565) |
| type = GL_UNSIGNED_SHORT_5_6_5; |
| |
| target = gst_gl_texture_target_to_gl (gl_mem->tex_target); |
| gl->BindTexture (target, gl_mem->tex_id); |
| gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (gl_mem)->query, |
| GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s", "glGetTexImage took"); |
| gl->GetTexImage (target, 0, format, type, gl_mem->mem.data); |
| gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (gl_mem)->query); |
| gl->BindTexture (target, 0); |
| } |
| |
| return gl_mem->mem.data; |
| } |
| |
| static gpointer |
| _gl_tex_download_read_pixels (GstGLMemory * gl_mem, GstMapInfo * info, |
| gsize size) |
| { |
| if (size != -1 && size != ((GstMemory *) gl_mem)->maxsize) |
| return NULL; |
| |
| if (info->flags & GST_MAP_READ |
| && GST_MEMORY_FLAG_IS_SET (gl_mem, |
| GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) { |
| GST_CAT_TRACE (GST_CAT_GL_MEMORY, |
| "attempting download of texture %u " "using glReadPixels", |
| gl_mem->tex_id); |
| if (!gst_gl_memory_read_pixels (gl_mem, gl_mem->mem.data)) |
| return NULL; |
| } |
| |
| return gl_mem->mem.data; |
| } |
| |
| static gpointer |
| _gl_tex_map_cpu_access (GstGLMemory * gl_mem, GstMapInfo * info, gsize size) |
| { |
| gpointer data = NULL; |
| |
| if (!gst_gl_base_memory_alloc_data (GST_GL_BASE_MEMORY_CAST (gl_mem))) |
| return NULL; |
| |
| if (!data) |
| data = _gl_tex_download_get_tex_image (gl_mem, info, size); |
| |
| if (!data) |
| data = _gl_tex_download_read_pixels (gl_mem, info, size); |
| |
| return data; |
| } |
| |
| static void |
| _upload_cpu_write (GstGLMemory * gl_mem, GstMapInfo * info, gsize maxsize) |
| { |
| gst_gl_memory_texsubimage (gl_mem, gl_mem->mem.data); |
| } |
| |
| /** |
| * gst_gl_memory_texsubimage: |
| * @gl_mem: a #GstGLMemory |
| * @read_pointer: the data pointer to pass to glTexSubImage |
| * |
| * See gst_gl_memory_read_pixels() for what @read_pointer signifies. |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_gl_memory_texsubimage (GstGLMemory * gl_mem, gpointer read_pointer) |
| { |
| GstGLContext *context = gl_mem->mem.context; |
| const GstGLFuncs *gl; |
| GLenum gl_format, gl_type, gl_target; |
| gpointer data; |
| gsize plane_start; |
| |
| if (!GST_MEMORY_FLAG_IS_SET (gl_mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) |
| return; |
| |
| gl = context->gl_vtable; |
| |
| gl_type = GL_UNSIGNED_BYTE; |
| gl_format = gl_mem->tex_format; |
| if (gl_mem->tex_format == GST_GL_RGB565) { |
| gl_format = GST_GL_RGB; |
| gl_type = GL_UNSIGNED_SHORT_5_6_5; |
| } |
| |
| gl_target = gst_gl_texture_target_to_gl (gl_mem->tex_target); |
| |
| if (USING_OPENGL (context) || USING_GLES3 (context) |
| || USING_OPENGL3 (context)) { |
| gl->PixelStorei (GL_UNPACK_ROW_LENGTH, gl_mem->unpack_length); |
| } else if (USING_GLES2 (context)) { |
| gl->PixelStorei (GL_UNPACK_ALIGNMENT, gl_mem->unpack_length); |
| } |
| |
| GST_CAT_LOG (GST_CAT_GL_MEMORY, "upload for texture id:%u, %ux%u", |
| gl_mem->tex_id, gl_mem->tex_width, GL_MEM_HEIGHT (gl_mem)); |
| |
| /* find the start of the plane data including padding */ |
| plane_start = |
| gst_gl_get_plane_start (&gl_mem->info, &gl_mem->valign, |
| gl_mem->plane) + gl_mem->mem.mem.offset; |
| |
| data = (gpointer) ((gintptr) plane_start + (gintptr) read_pointer); |
| |
| gl->BindTexture (gl_target, gl_mem->tex_id); |
| gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (gl_mem)->query, |
| GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s", "glTexSubImage took"); |
| gl->TexSubImage2D (gl_target, 0, 0, 0, gl_mem->tex_width, |
| GL_MEM_HEIGHT (gl_mem), gl_format, gl_type, data); |
| gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (gl_mem)->query); |
| |
| /* Reset to default values */ |
| if (USING_OPENGL (context) || USING_GLES3 (context) |
| || USING_OPENGL3 (context)) { |
| gl->PixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
| } else if (USING_GLES2 (context)) { |
| gl->PixelStorei (GL_UNPACK_ALIGNMENT, 4); |
| } |
| |
| gl->BindTexture (gl_target, 0); |
| } |
| |
| static gpointer |
| _default_gl_tex_map (GstGLMemory * gl_mem, GstMapInfo * info, gsize size) |
| { |
| if ((info->flags & GST_MAP_GL) == GST_MAP_GL) { |
| _upload_cpu_write (gl_mem, info, size); |
| return &gl_mem->tex_id; |
| } else { |
| return _gl_tex_map_cpu_access (gl_mem, info, size); |
| } |
| } |
| |
| static gpointer |
| _gl_tex_map (GstGLMemory * gl_mem, GstMapInfo * info, gsize maxsize) |
| { |
| GstGLMemoryAllocatorClass *alloc_class; |
| gpointer data; |
| |
| alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (gl_mem->mem.mem.allocator); |
| |
| if ((info->flags & GST_MAP_GL) == GST_MAP_GL) { |
| if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) |
| return &gl_mem->tex_id; |
| } else { /* not GL */ |
| if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot map External OES textures"); |
| return NULL; |
| } |
| } |
| |
| g_return_val_if_fail (alloc_class->map != NULL, NULL); |
| data = alloc_class->map (GST_GL_BASE_MEMORY_CAST (gl_mem), info, maxsize); |
| |
| return data; |
| } |
| |
| static void |
| _default_gl_tex_unmap (GstGLMemory * gl_mem, GstMapInfo * info) |
| { |
| } |
| |
| static void |
| _gl_tex_unmap (GstGLMemory * gl_mem, GstMapInfo * info) |
| { |
| GstGLMemoryAllocatorClass *alloc_class; |
| |
| alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (gl_mem->mem.mem.allocator); |
| g_return_if_fail (alloc_class->unmap != NULL); |
| |
| alloc_class->unmap (GST_GL_BASE_MEMORY_CAST (gl_mem), info); |
| } |
| |
| /** |
| * gst_gl_memory_copy_teximage: |
| * @src: the source #GstGLMemory |
| * @tex_id: the destination texture id |
| * @out_target: the destination #GstGLTextureTarget |
| * @out_tex_format: the destination #GstGLFormat |
| * @out_width: the destination width |
| * @out_height: the destination height |
| * |
| * Copies the texture in #GstGLMemory into the texture specified by @tex_id, |
| * @out_target, @out_tex_format, @out_width and @out_height. |
| * |
| * Returns: whether the copy succeeded. |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_memory_copy_teximage (GstGLMemory * src, guint tex_id, |
| GstGLTextureTarget out_target, GstGLFormat out_tex_format, |
| gint out_width, gint out_height) |
| { |
| const GstGLFuncs *gl; |
| guint out_tex_target; |
| GstMapInfo sinfo; |
| guint src_tex_id; |
| guint fbo[2]; |
| guint n_fbos; |
| |
| gl = src->mem.context->gl_vtable; |
| out_tex_target = gst_gl_texture_target_to_gl (out_target); |
| |
| if (!gl->GenFramebuffers) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Framebuffer objects not supported"); |
| goto error; |
| } |
| |
| if (USING_GLES2 (src->mem.context) |
| && (src->tex_format == GST_GL_LUMINANCE |
| || src->tex_format == GST_GL_LUMINANCE_ALPHA)) { |
| GST_CAT_FIXME (GST_CAT_GL_MEMORY, |
| "Cannot copy Luminance/Luminance Alpha textures in GLES"); |
| goto error; |
| } |
| |
| if (!gst_memory_map (GST_MEMORY_CAST (src), &sinfo, |
| GST_MAP_READ | GST_MAP_GL)) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, |
| "Failed to map source memory for copying"); |
| goto error; |
| } |
| |
| src_tex_id = *(guint *) sinfo.data; |
| |
| GST_CAT_LOG (GST_CAT_GL_MEMORY, "copying memory %p, tex %u into " |
| "texture %i", src, src_tex_id, tex_id); |
| |
| /* FIXME: try and avoid creating and destroying fbo's every copy... */ |
| if (!gl->BlitFramebuffer || (!gl->DrawBuffer && !gl->DrawBuffers) |
| || !gl->ReadBuffer) { |
| /* create a framebuffer object */ |
| n_fbos = 1; |
| gl->GenFramebuffers (n_fbos, &fbo[0]); |
| gl->BindFramebuffer (GL_FRAMEBUFFER, fbo[0]); |
| |
| gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| gst_gl_texture_target_to_gl (src->tex_target), src_tex_id, 0); |
| |
| if (!gst_gl_context_check_framebuffer_status (src->mem.context, |
| GL_FRAMEBUFFER)) |
| goto fbo_error; |
| |
| gl->BindTexture (out_tex_target, tex_id); |
| gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (src)->query, |
| GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s", "CopyTexImage2D took"); |
| gl->CopyTexImage2D (out_tex_target, 0, out_tex_format, 0, 0, out_width, |
| out_height, 0); |
| gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (src)->query); |
| |
| gl->BindTexture (out_tex_target, 0); |
| gl->BindFramebuffer (GL_FRAMEBUFFER, 0); |
| |
| gl->DeleteFramebuffers (n_fbos, &fbo[0]); |
| } else { |
| GLenum multipleRT[] = { |
| GL_COLOR_ATTACHMENT0, |
| GL_COLOR_ATTACHMENT1, |
| GL_COLOR_ATTACHMENT2 |
| }; |
| |
| /* create a framebuffer object */ |
| n_fbos = 2; |
| gl->GenFramebuffers (n_fbos, &fbo[0]); |
| |
| gl->BindFramebuffer (GL_READ_FRAMEBUFFER, fbo[0]); |
| gl->FramebufferTexture2D (GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| gst_gl_texture_target_to_gl (src->tex_target), src_tex_id, 0); |
| |
| if (!gst_gl_context_check_framebuffer_status (src->mem.context, |
| GL_READ_FRAMEBUFFER)) |
| goto fbo_error; |
| |
| gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, fbo[1]); |
| |
| gl->FramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| gst_gl_texture_target_to_gl (src->tex_target), tex_id, 0); |
| |
| if (!gst_gl_context_check_framebuffer_status (src->mem.context, |
| GL_DRAW_FRAMEBUFFER)) |
| goto fbo_error; |
| |
| gl->BindTexture (out_tex_target, tex_id); |
| gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (src)->query, |
| GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s", "BlitFramebuffer took"); |
| gl->ReadBuffer (GL_COLOR_ATTACHMENT0); |
| if (gl->DrawBuffers) |
| gl->DrawBuffers (1, multipleRT); |
| else |
| gl->DrawBuffer (GL_COLOR_ATTACHMENT0); |
| gl->BlitFramebuffer (0, 0, out_width, out_height, |
| 0, 0, out_width, out_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (src)->query); |
| |
| gl->BindTexture (out_tex_target, 0); |
| gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, 0); |
| gl->BindFramebuffer (GL_READ_FRAMEBUFFER, 0); |
| |
| gl->DeleteFramebuffers (n_fbos, &fbo[0]); |
| |
| if (gl->DrawBuffer) |
| gl->DrawBuffer (GL_BACK); |
| } |
| |
| gst_memory_unmap (GST_MEMORY_CAST (src), &sinfo); |
| |
| return TRUE; |
| |
| fbo_error: |
| { |
| gl->BindTexture (out_tex_target, 0); |
| if (!gl->BlitFramebuffer) { |
| gl->BindFramebuffer (GL_FRAMEBUFFER, 0); |
| } else { |
| gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, 0); |
| gl->BindFramebuffer (GL_READ_FRAMEBUFFER, 0); |
| } |
| |
| gl->DeleteFramebuffers (n_fbos, &fbo[0]); |
| |
| gst_memory_unmap (GST_MEMORY_CAST (src), &sinfo); |
| } |
| error: |
| return FALSE; |
| } |
| |
| static void |
| _gl_tex_copy_thread (GstGLContext * context, gpointer data) |
| { |
| GstGLMemoryCopyParams *copy_params; |
| |
| copy_params = (GstGLMemoryCopyParams *) data; |
| |
| if (!copy_params->tex_id) { |
| guint internal_format, out_gl_format, out_gl_type, out_tex_target; |
| |
| out_tex_target = gst_gl_texture_target_to_gl (copy_params->tex_target); |
| out_gl_format = copy_params->src->tex_format; |
| out_gl_type = GL_UNSIGNED_BYTE; |
| if (copy_params->out_format == GST_GL_RGB565) { |
| out_gl_format = GST_GL_RGB; |
| out_gl_type = GL_UNSIGNED_SHORT_5_6_5; |
| } |
| |
| internal_format = |
| gst_gl_sized_gl_format_from_gl_format_type (context, out_gl_format, |
| out_gl_type); |
| |
| copy_params->tex_id = |
| _new_texture (context, out_tex_target, |
| internal_format, out_gl_format, out_gl_type, copy_params->out_width, |
| copy_params->out_height); |
| } |
| |
| copy_params->result = gst_gl_memory_copy_teximage (copy_params->src, |
| copy_params->tex_id, copy_params->tex_target, copy_params->tex_format, |
| copy_params->out_width, copy_params->out_height); |
| } |
| |
| static GstMemory * |
| _default_gl_tex_copy (GstGLMemory * src, gssize offset, gssize size) |
| { |
| GstAllocationParams params = { 0, GST_MEMORY_CAST (src)->align, 0, 0 }; |
| GstGLBaseMemoryAllocator *base_mem_allocator; |
| GstAllocator *allocator; |
| GstGLMemory *dest = NULL; |
| |
| allocator = GST_MEMORY_CAST (src)->allocator; |
| base_mem_allocator = (GstGLBaseMemoryAllocator *) allocator; |
| |
| if (src->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy External OES textures"); |
| return NULL; |
| } |
| |
| /* If not doing a full copy, then copy to sysmem, the 2D represention of the |
| * texture would become wrong */ |
| if (offset > 0 || size < GST_MEMORY_CAST (src)->size) { |
| return base_mem_allocator->fallback_mem_copy (GST_MEMORY_CAST (src), offset, |
| size); |
| } |
| |
| dest = g_new0 (GstGLMemory, 1); |
| |
| gst_gl_memory_init (dest, allocator, NULL, src->mem.context, src->tex_target, |
| src->tex_format, ¶ms, &src->info, src->plane, &src->valign, NULL, |
| NULL); |
| |
| if (!GST_MEMORY_FLAG_IS_SET (src, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) { |
| GstMapInfo dinfo; |
| |
| if (!gst_memory_map (GST_MEMORY_CAST (dest), &dinfo, |
| GST_MAP_WRITE | GST_MAP_GL)) { |
| GST_CAT_WARNING (GST_CAT_GL_MEMORY, |
| "Failed not map destination for writing"); |
| gst_memory_unref (GST_MEMORY_CAST (dest)); |
| return NULL; |
| } |
| |
| if (!gst_gl_memory_copy_into ((GstGLMemory *) src, |
| ((GstGLMemory *) dest)->tex_id, src->tex_target, |
| src->tex_format, src->tex_width, GL_MEM_HEIGHT (src))) { |
| GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory"); |
| gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo); |
| goto memcpy; |
| } |
| |
| gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo); |
| } else { |
| memcpy: |
| if (!gst_gl_base_memory_memcpy ((GstGLBaseMemory *) src, |
| (GstGLBaseMemory *) dest, offset, size)) { |
| GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory"); |
| gst_memory_unref (GST_MEMORY_CAST (dest)); |
| return NULL; |
| } |
| } |
| |
| return (GstMemory *) dest; |
| } |
| |
| static GstMemory * |
| _gl_tex_copy (GstGLMemory * src, gssize offset, gssize size) |
| { |
| GstGLMemoryAllocatorClass *alloc_class; |
| |
| alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (src->mem.mem.allocator); |
| |
| if (src->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy External OES textures"); |
| return NULL; |
| } |
| |
| g_return_val_if_fail (alloc_class->copy, NULL); |
| return (GstMemory *) alloc_class->copy (GST_GL_BASE_MEMORY_CAST (src), offset, |
| size); |
| } |
| |
| static GstMemory * |
| _gl_tex_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_tex_destroy (GstGLMemory * gl_mem) |
| { |
| const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable; |
| |
| if (gl_mem->tex_id && !gl_mem->texture_wrapped) |
| gl->DeleteTextures (1, &gl_mem->tex_id); |
| } |
| |
| static GstGLMemory * |
| _default_gl_tex_alloc (GstGLMemoryAllocator * allocator, |
| GstGLVideoAllocationParams * params) |
| { |
| guint alloc_flags = params->parent.alloc_flags; |
| GstGLMemory *mem; |
| |
| g_return_val_if_fail (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| NULL); |
| |
| mem = g_new0 (GstGLMemory, 1); |
| |
| if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) { |
| mem->tex_id = GPOINTER_TO_UINT (params->parent.gl_handle); |
| mem->texture_wrapped = TRUE; |
| } |
| |
| gst_gl_memory_init (mem, GST_ALLOCATOR_CAST (allocator), NULL, |
| params->parent.context, params->target, params->tex_format, |
| params->parent.alloc_params, params->v_info, params->plane, |
| params->valign, params->parent.user_data, params->parent.notify); |
| |
| if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) { |
| GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD); |
| } |
| if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) { |
| mem->mem.data = params->parent.wrapped_data; |
| GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD); |
| } |
| |
| return mem; |
| } |
| |
| static void |
| gst_gl_memory_allocator_class_init (GstGLMemoryAllocatorClass * klass) |
| { |
| GstGLBaseMemoryAllocatorClass *gl_base; |
| GstAllocatorClass *allocator_class; |
| |
| gl_base = (GstGLBaseMemoryAllocatorClass *) klass; |
| allocator_class = (GstAllocatorClass *) klass; |
| |
| klass->map = (GstGLBaseMemoryAllocatorMapFunction) _default_gl_tex_map; |
| klass->unmap = (GstGLBaseMemoryAllocatorUnmapFunction) _default_gl_tex_unmap; |
| klass->copy = (GstGLBaseMemoryAllocatorCopyFunction) _default_gl_tex_copy; |
| |
| gl_base->alloc = |
| (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_tex_alloc; |
| gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_tex_create; |
| gl_base->map = (GstGLBaseMemoryAllocatorMapFunction) _gl_tex_map; |
| gl_base->unmap = (GstGLBaseMemoryAllocatorUnmapFunction) _gl_tex_unmap; |
| gl_base->copy = (GstGLBaseMemoryAllocatorCopyFunction) _gl_tex_copy; |
| gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_tex_destroy; |
| |
| allocator_class->alloc = _gl_tex_alloc; |
| } |
| |
| static void |
| gst_gl_memory_allocator_init (GstGLMemoryAllocator * allocator) |
| { |
| GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); |
| |
| alloc->mem_type = GST_GL_MEMORY_ALLOCATOR_NAME; |
| |
| GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); |
| } |
| |
| /** |
| * gst_gl_memory_copy_into: |
| * @gl_mem:a #GstGLMemory |
| * @tex_id:OpenGL texture id |
| * @target: the #GstGLTextureTarget |
| * @tex_format: the #GstGLFormat |
| * @width: width of @tex_id |
| * @height: height of @tex_id |
| * |
| * Copies @gl_mem into the texture specfified by @tex_id. The format of @tex_id |
| * is specified by @tex_format, @width and @height. |
| * |
| * Returns: Whether the copy suceeded |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_memory_copy_into (GstGLMemory * gl_mem, guint tex_id, |
| GstGLTextureTarget target, GstGLFormat tex_format, gint width, gint height) |
| { |
| GstGLMemoryCopyParams copy_params; |
| |
| copy_params.src = gl_mem; |
| copy_params.tex_id = tex_id; |
| copy_params.tex_target = target; |
| copy_params.tex_format = tex_format; |
| copy_params.out_width = width; |
| copy_params.out_height = height; |
| |
| gst_gl_context_thread_add (gl_mem->mem.context, _gl_tex_copy_thread, |
| ©_params); |
| |
| return copy_params.result; |
| } |
| |
| /** |
| * gst_gl_memory_get_texture_width: |
| * @gl_mem: a #GstGLMemory |
| * |
| * Returns: the texture width of @gl_mem |
| * |
| * Since: 1.8 |
| */ |
| gint |
| gst_gl_memory_get_texture_width (GstGLMemory * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->tex_width; |
| } |
| |
| /** |
| * gst_gl_memory_get_texture_height: |
| * @gl_mem: a #GstGLMemory |
| * |
| * Returns: the texture height of @gl_mem |
| * |
| * Since: 1.8 |
| */ |
| gint |
| gst_gl_memory_get_texture_height (GstGLMemory * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0); |
| |
| return _get_plane_height (&gl_mem->info, gl_mem->plane); |
| } |
| |
| /** |
| * gst_gl_memory_get_texture_format: |
| * @gl_mem: a #GstGLMemory |
| * |
| * Returns: the #GstGLFormat of @gl_mem |
| * |
| * Since: 1.12 |
| */ |
| GstGLFormat |
| gst_gl_memory_get_texture_format (GstGLMemory * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->tex_format; |
| } |
| |
| /** |
| * gst_gl_memory_get_texture_target: |
| * @gl_mem: a #GstGLMemory |
| * |
| * Returns: the #GstGLTextureTarget of @gl_mem |
| * |
| * Since: 1.8 |
| */ |
| GstGLTextureTarget |
| gst_gl_memory_get_texture_target (GstGLMemory * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->tex_target; |
| } |
| |
| /** |
| * gst_gl_memory_get_texture_id: |
| * @gl_mem: a #GstGLMemory |
| * |
| * Returns: the OpenGL texture handle of @gl_mem |
| * |
| * Since: 1.8 |
| */ |
| guint |
| gst_gl_memory_get_texture_id (GstGLMemory * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->tex_id; |
| } |
| |
| /** |
| * gst_gl_memory_init_once: |
| * |
| * Initializes the GL Base Texture allocator. It is safe to call this function |
| * multiple times. This must be called before any other GstGLMemory operation. |
| * |
| * Since: 1.4 |
| */ |
| void |
| gst_gl_memory_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_MEMORY, "glbasetexture", 0, |
| "OpenGL Base Texture Memory"); |
| |
| _gl_memory_allocator = g_object_new (GST_TYPE_GL_MEMORY_ALLOCATOR, NULL); |
| gst_object_ref_sink (_gl_memory_allocator); |
| |
| gst_allocator_register (GST_GL_MEMORY_ALLOCATOR_NAME, _gl_memory_allocator); |
| |
| g_once_init_leave (&_init, 1); |
| } |
| } |
| |
| /** |
| * gst_is_gl_memory: |
| * @mem:a #GstMemory |
| * |
| * Returns: whether the memory at @mem is a #GstGLMemory |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_is_gl_memory (GstMemory * mem) |
| { |
| return mem != NULL && mem->allocator != NULL |
| && g_type_is_a (G_OBJECT_TYPE (mem->allocator), |
| GST_TYPE_GL_MEMORY_ALLOCATOR); |
| } |
| |
| G_DEFINE_BOXED_TYPE (GstGLVideoAllocationParams, gst_gl_video_allocation_params, |
| (GBoxedCopyFunc) gst_gl_allocation_params_copy, |
| (GBoxedFreeFunc) gst_gl_allocation_params_free); |
| |
| static void |
| _gst_gl_video_allocation_params_set_video_alignment (GstGLVideoAllocationParams |
| * params, GstVideoAlignment * valign) |
| { |
| g_return_if_fail (params != NULL); |
| |
| if (!params->valign) |
| params->valign = g_new0 (GstVideoAlignment, 1); |
| |
| if (valign) { |
| *params->valign = *valign; |
| } else { |
| gst_video_alignment_reset (params->valign); |
| } |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_init_full: (skip) |
| * @params: a #GstGLVideoAllocationParams to initialize |
| * @struct_size: the size of the struct in @params |
| * @alloc_flags: some allocation flags |
| * @copy: a copy function |
| * @free: a free function |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for @wrapped_data |
| * @v_info: the #GstVideoInfo for @wrapped_data |
| * @plane: the video plane @wrapped_data represents |
| * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @wrapped_data |
| * @target: the #GstGLTextureTarget |
| * @tex_format: the #GstGLFormat |
| * @wrapped_data: (allow-none): the optional data pointer to wrap |
| * @gl_handle: the optional OpenGL handle to wrap or 0 |
| * @user_data: (allow-none): user data to call @notify with |
| * @notify: (allow-none): a #GDestroyNotify |
| * |
| * Intended for subclass usage |
| * |
| * Returns: initializes @params with the parameters specified |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_video_allocation_params_init_full (GstGLVideoAllocationParams * params, |
| gsize struct_size, guint alloc_flags, GstGLAllocationParamsCopyFunc copy, |
| GstGLAllocationParamsFreeFunc free, GstGLContext * context, |
| GstAllocationParams * alloc_params, GstVideoInfo * v_info, |
| guint plane, GstVideoAlignment * valign, GstGLTextureTarget target, |
| GstGLFormat tex_format, gpointer wrapped_data, gpointer gl_handle, |
| gpointer user_data, GDestroyNotify notify) |
| { |
| guint i; |
| |
| 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); |
| g_return_val_if_fail (v_info != NULL, 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->v_info = g_new0 (GstVideoInfo, 1); |
| *params->v_info = *v_info; |
| for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { |
| params->v_info->offset[i] = v_info->offset[i]; |
| params->v_info->stride[i] = v_info->stride[i]; |
| } |
| _gst_gl_video_allocation_params_set_video_alignment (params, valign); |
| params->target = target; |
| params->tex_format = tex_format; |
| params->plane = plane; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_new: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture |
| * @v_info: the #GstVideoInfo for the texture |
| * @plane: the video plane of @v_info to allocate |
| * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of the texture |
| * @target: the #GstGLTextureTarget for the created textures |
| * @tex_format: the #GstGLFormat for the created textures |
| * |
| * Returns: a new #GstGLVideoAllocationParams for allocating #GstGLMemory's |
| * |
| * Since: 1.8 |
| */ |
| GstGLVideoAllocationParams * |
| gst_gl_video_allocation_params_new (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane, |
| GstVideoAlignment * valign, GstGLTextureTarget target, |
| GstGLFormat tex_format) |
| { |
| GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1); |
| |
| if (!gst_gl_video_allocation_params_init_full (params, |
| sizeof (GstGLVideoAllocationParams), |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC | |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| (GstGLAllocationParamsCopyFunc) |
| gst_gl_video_allocation_params_copy_data, |
| (GstGLAllocationParamsFreeFunc) |
| gst_gl_video_allocation_params_free_data, context, alloc_params, |
| v_info, plane, valign, target, tex_format, NULL, 0, NULL, NULL)) { |
| g_free (params); |
| return NULL; |
| } |
| |
| return params; |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_new_wrapped_data: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for @wrapped_data |
| * @v_info: the #GstVideoInfo for @wrapped_data |
| * @plane: the video plane @wrapped_data represents |
| * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @wrapped_data |
| * @target: the #GstGLTextureTarget for @wrapped_data |
| * @tex_format: the #GstGLFormat for @wrapped_data |
| * @wrapped_data: the data pointer to wrap |
| * @user_data: (allow-none): user data to call @notify with |
| * @notify: (allow-none): a #GDestroyNotify |
| * |
| * Returns: a new #GstGLVideoAllocationParams for wrapping @wrapped_data |
| * |
| * Since: 1.8 |
| */ |
| GstGLVideoAllocationParams * |
| gst_gl_video_allocation_params_new_wrapped_data (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane, |
| GstVideoAlignment * valign, GstGLTextureTarget target, |
| GstGLFormat tex_format, gpointer wrapped_data, gpointer user_data, |
| GDestroyNotify notify) |
| { |
| GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1); |
| |
| if (!gst_gl_video_allocation_params_init_full (params, |
| sizeof (GstGLVideoAllocationParams), |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM | |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| (GstGLAllocationParamsCopyFunc) |
| gst_gl_video_allocation_params_copy_data, |
| (GstGLAllocationParamsFreeFunc) |
| gst_gl_video_allocation_params_free_data, context, alloc_params, |
| v_info, plane, valign, target, tex_format, wrapped_data, 0, user_data, |
| notify)) { |
| g_free (params); |
| return NULL; |
| } |
| |
| return params; |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_new_wrapped_gl_handle: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id |
| * @v_info: the #GstVideoInfo for @tex_id |
| * @plane: the video plane @tex_id represents |
| * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @tex_id |
| * @target: the #GstGLTextureTarget for @tex_id |
| * @tex_format: the #GstGLFormat 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 |
| * |
| * @gl_handle is defined by the specific OpenGL handle being wrapped |
| * For #GstGLMemory and #GstGLMemoryPBO it is an OpenGL texture id. |
| * Other memory types may define it to require a different type of parameter. |
| * |
| * Returns: a new #GstGLVideoAllocationParams for wrapping @gl_handle |
| * |
| * Since: 1.8 |
| */ |
| GstGLVideoAllocationParams * |
| gst_gl_video_allocation_params_new_wrapped_gl_handle (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane, |
| GstVideoAlignment * valign, GstGLTextureTarget target, |
| GstGLFormat tex_format, gpointer gl_handle, gpointer user_data, |
| GDestroyNotify notify) |
| { |
| GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1); |
| |
| if (!gst_gl_video_allocation_params_init_full (params, |
| sizeof (GstGLVideoAllocationParams), |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE | |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| (GstGLAllocationParamsCopyFunc) |
| gst_gl_video_allocation_params_copy_data, |
| (GstGLAllocationParamsFreeFunc) |
| gst_gl_video_allocation_params_free_data, context, alloc_params, |
| v_info, plane, valign, target, tex_format, NULL, gl_handle, user_data, |
| notify)) { |
| g_free (params); |
| return NULL; |
| } |
| |
| return params; |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_new_wrapped_texture: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id |
| * @v_info: the #GstVideoInfo for @tex_id |
| * @plane: the video plane @tex_id represents |
| * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @tex_id |
| * @target: the #GstGLTextureTarget for @tex_id |
| * @tex_format: the #GstGLFormat for @tex_id |
| * @tex_id: the GL texture to wrap |
| * @user_data: (allow-none): user data to call @notify with |
| * @notify: (allow-none): a #GDestroyNotify |
| * |
| * Returns: a new #GstGLVideoAllocationParams for wrapping @tex_id |
| * |
| * Since: 1.8 |
| */ |
| GstGLVideoAllocationParams * |
| gst_gl_video_allocation_params_new_wrapped_texture (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane, |
| GstVideoAlignment * valign, GstGLTextureTarget target, |
| GstGLFormat tex_format, guint tex_id, gpointer user_data, |
| GDestroyNotify notify) |
| { |
| return gst_gl_video_allocation_params_new_wrapped_gl_handle (context, |
| alloc_params, v_info, plane, valign, target, tex_format, |
| GUINT_TO_POINTER (tex_id), user_data, notify); |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_free_data: |
| * @params: a #GstGLVideoAllocationParams |
| * |
| * Unset and free any dynamically allocated resources. Intended for subclass |
| * usage only to chain up at the end of a subclass free function. |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_gl_video_allocation_params_free_data (GstGLVideoAllocationParams * params) |
| { |
| g_free (params->v_info); |
| g_free (params->valign); |
| |
| gst_gl_allocation_params_free_data (¶ms->parent); |
| } |
| |
| /** |
| * gst_gl_video_allocation_params_copy_data: |
| * @src_vid: source #GstGLVideoAllocationParams to copy from |
| * @dest_vid: destination #GstGLVideoAllocationParams to copy into |
| * |
| * Copy and set any dynamically allocated resources in @dest_vid. Intended |
| * for subclass usage only to chain up at the end of a subclass copy function. |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_gl_video_allocation_params_copy_data (GstGLVideoAllocationParams * src_vid, |
| GstGLVideoAllocationParams * dest_vid) |
| { |
| GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid; |
| GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid; |
| guint i; |
| |
| gst_gl_allocation_params_copy_data (src, dest); |
| |
| dest_vid->v_info = g_new0 (GstVideoInfo, 1); |
| *dest_vid->v_info = *src_vid->v_info; |
| for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { |
| dest_vid->v_info->offset[i] = src_vid->v_info->offset[i]; |
| dest_vid->v_info->stride[i] = src_vid->v_info->stride[i]; |
| } |
| _gst_gl_video_allocation_params_set_video_alignment (dest_vid, |
| src_vid->valign); |
| dest_vid->target = src_vid->target; |
| dest_vid->tex_format = src_vid->tex_format; |
| dest_vid->plane = src_vid->plane; |
| } |
| |
| /** |
| * gst_gl_memory_setup_buffer: |
| * @allocator: the @GstGLMemoryAllocator to allocate from |
| * @buffer: a #GstBuffer to setup |
| * @params: the #GstGLVideoAllocationParams to allocate with |
| * @tex_formats: (allow-none) (array length=n_wrapped_pointers): |
| * a list of #GstGLFormat's to allocate with. |
| * @wrapped_data: (array length=n_wrapped_pointers) (element-type gpointer): |
| * a list of wrapped data pointers |
| * @n_wrapped_pointers: the number of elements in @tex_formats and @wrapped_data |
| * |
| * Returns: whether the buffer was correctly setup |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_memory_setup_buffer (GstGLMemoryAllocator * allocator, |
| GstBuffer * buffer, GstGLVideoAllocationParams * params, |
| GstGLFormat * tex_formats, gpointer * wrapped_data, |
| gsize n_wrapped_pointers) |
| { |
| GstGLBaseMemoryAllocator *base_allocator; |
| guint n_mem, i, v, views; |
| guint alloc_flags = params->parent.alloc_flags; |
| |
| g_return_val_if_fail (params != NULL, FALSE); |
| g_return_val_if_fail (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| FALSE); |
| |
| base_allocator = GST_GL_BASE_MEMORY_ALLOCATOR (allocator); |
| n_mem = GST_VIDEO_INFO_N_PLANES (params->v_info); |
| |
| if (GST_VIDEO_INFO_MULTIVIEW_MODE (params->v_info) == |
| GST_VIDEO_MULTIVIEW_MODE_SEPARATED) |
| views = params->v_info->views; |
| else |
| views = 1; |
| |
| g_return_val_if_fail (!wrapped_data |
| || views * n_mem != n_wrapped_pointers, FALSE); |
| |
| for (v = 0; v < views; v++) { |
| for (i = 0; i < n_mem; i++) { |
| GstGLMemory *gl_mem; |
| |
| if (tex_formats) { |
| params->tex_format = tex_formats[i]; |
| } else { |
| params->tex_format = |
| gst_gl_format_from_video_info (params->parent.context, |
| params->v_info, i); |
| } |
| |
| params->plane = i; |
| if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) { |
| g_return_val_if_fail (wrapped_data != NULL, FALSE); |
| params->parent.wrapped_data = wrapped_data[i]; |
| } else if (alloc_flags & |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) { |
| g_return_val_if_fail (wrapped_data != NULL, FALSE); |
| params->parent.gl_handle = wrapped_data[i]; |
| } |
| |
| if (!(gl_mem = (GstGLMemory *) gst_gl_base_memory_alloc (base_allocator, |
| (GstGLAllocationParams *) params))) |
| return FALSE; |
| |
| gst_buffer_append_memory (buffer, (GstMemory *) gl_mem); |
| } |
| |
| gst_buffer_add_video_meta_full (buffer, v, |
| GST_VIDEO_INFO_FORMAT (params->v_info), |
| GST_VIDEO_INFO_WIDTH (params->v_info), |
| GST_VIDEO_INFO_HEIGHT (params->v_info), n_mem, params->v_info->offset, |
| params->v_info->stride); |
| } |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_gl_memory_allocator_get_default: |
| * @context: a #GstGLContext |
| * |
| * Returns: (transfer full): the default #GstGLMemoryAllocator supported by |
| * @context |
| * |
| * Since: 1.8 |
| */ |
| GstGLMemoryAllocator * |
| gst_gl_memory_allocator_get_default (GstGLContext * context) |
| { |
| GstGLMemoryAllocator *allocator = NULL; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL); |
| |
| /* we can only use the pbo allocator with GL > 3.0 contexts */ |
| if (gst_gl_context_check_gl_version (context, |
| GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2, 3, 0)) { |
| allocator = (GstGLMemoryAllocator *) |
| gst_allocator_find (GST_GL_MEMORY_PBO_ALLOCATOR_NAME); |
| } else { |
| allocator = (GstGLMemoryAllocator *) |
| gst_allocator_find (GST_GL_MEMORY_ALLOCATOR_NAME); |
| } |
| |
| return allocator; |
| } |