mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 03:31:05 +00:00
28dbe4fffc
Add gst_gl_memory_allocator_get_default to get the default allocator based on the opengl version. Allows us to stop hardcoding the PBO allocator which isn't supported on gles2. Fixes GL upload on iOS9 among other things.
1194 lines
38 KiB
C
1194 lines
38 KiB
C
/*
|
|
* 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 <gst/video/video.h>
|
|
|
|
#include <gst/gl/gstglbasememory.h>
|
|
#include <gst/gl/gstglmemory.h>
|
|
|
|
/**
|
|
* SECTION:gstglmemory
|
|
* @short_description: memory subclass for GL textures
|
|
* @see_also: #GstMemory, #GstAllocator, #GstGLBufferPool
|
|
*
|
|
* GstGLMemory is a #GstGLBaseBuffer subclass providing support for the mapping of
|
|
* GL textures.
|
|
*
|
|
* #GstGLMemory is created through gst_gl_memory_alloc() or system memory can
|
|
* be wrapped through gst_gl_memory_wrapped().
|
|
*
|
|
* Data is uploaded or downloaded from the GPU as is necessary.
|
|
*/
|
|
|
|
/* Implementation notes
|
|
*
|
|
* PBO transfer's are implemented using GstGLBaseBuffer. We just need to
|
|
* ensure that the texture data is written/read to/from before/after calling
|
|
* the parent class which performs the pbo buffer transfer.
|
|
*/
|
|
|
|
#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
|
|
|
|
G_DEFINE_TYPE (GstGLMemoryAllocator, gst_gl_memory_allocator,
|
|
GST_TYPE_GL_BASE_MEMORY_ALLOCATOR);
|
|
|
|
typedef struct
|
|
{
|
|
/* in */
|
|
GstGLMemory *src;
|
|
GstVideoGLTextureType out_format;
|
|
guint out_width, out_height;
|
|
GstGLTextureTarget tex_target;
|
|
GstVideoGLTextureType tex_type;
|
|
/* 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;
|
|
|
|
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);
|
|
|
|
n_gl_bytes = gst_gl_texture_type_n_bytes (gl_mem->tex_type);
|
|
if (n_gl_bytes == 0) {
|
|
GST_ERROR ("Unsupported texture type %d", gl_mem->tex_type);
|
|
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 = gst_gl_format_from_gl_texture_type (gl_mem->tex_type);
|
|
tex_type = GL_UNSIGNED_BYTE;
|
|
if (gl_mem->tex_type == GST_VIDEO_GL_TEXTURE_TYPE_RGB16)
|
|
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
|
|
* @params: (allow-none): the @GstAllocationParams to initialize with
|
|
* @size: the number of bytes to be allocated
|
|
* @notify: (allow-none): a #GDestroyNotify
|
|
* @user_data: (allow-none): user data to call @notify with
|
|
*
|
|
* Initializes @mem with the required parameters
|
|
*/
|
|
void
|
|
gst_gl_memory_init (GstGLMemory * mem, GstAllocator * allocator,
|
|
GstMemory * parent, GstGLContext * context, GstGLTextureTarget target,
|
|
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_type =
|
|
gst_gl_texture_type_from_format (context, GST_VIDEO_INFO_FORMAT (info),
|
|
plane);
|
|
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_type, mem->tex_width, GL_MEM_HEIGHT (mem), GL_MEM_STRIDE (mem),
|
|
mem->mem.mem.size);
|
|
}
|
|
|
|
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 = gst_gl_format_from_gl_texture_type (gl_mem->tex_type);
|
|
type = GL_UNSIGNED_BYTE;
|
|
if (gl_mem->tex_type == GST_VIDEO_GL_TEXTURE_TYPE_RGB16)
|
|
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)) {
|
|
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;
|
|
}
|
|
|
|
gl->ReadPixels (0, 0, gl_mem->tex_width, GL_MEM_HEIGHT (gl_mem), format,
|
|
type, read_pointer);
|
|
|
|
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_type != GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE
|
|
&& gl_mem->tex_type != GST_VIDEO_GL_TEXTURE_TYPE_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 = gst_gl_format_from_gl_texture_type (gl_mem->tex_type);
|
|
type = GL_UNSIGNED_BYTE;
|
|
if (gl_mem->tex_type == GST_VIDEO_GL_TEXTURE_TYPE_RGB16)
|
|
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);
|
|
gl->GetTexImage (target, 0, format, type, gl_mem->mem.data);
|
|
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)
|
|
{
|
|
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;
|
|
if (gl_mem->tex_type == GST_VIDEO_GL_TEXTURE_TYPE_RGB16)
|
|
gl_type = GL_UNSIGNED_SHORT_5_6_5;
|
|
|
|
gl_format = gst_gl_format_from_gl_texture_type (gl_mem->tex_type);
|
|
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) gl_mem->mem.data);
|
|
|
|
gl->BindTexture (gl_target, gl_mem->tex_id);
|
|
gl->TexSubImage2D (gl_target, 0, 0, 0, gl_mem->tex_width,
|
|
GL_MEM_HEIGHT (gl_mem), gl_format, gl_type, data);
|
|
|
|
/* Reset to default values */
|
|
if (USING_OPENGL (context) || USING_GLES3 (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);
|
|
}
|
|
|
|
gboolean
|
|
gst_gl_memory_copy_teximage (GstGLMemory * src, guint tex_id,
|
|
GstGLTextureTarget out_target, GstVideoGLTextureType out_tex_type,
|
|
gint out_width, gint out_height)
|
|
{
|
|
const GstGLFuncs *gl;
|
|
guint out_gl_format, out_tex_target;
|
|
GstMapInfo sinfo;
|
|
guint src_tex_id;
|
|
guint fbo;
|
|
|
|
gl = src->mem.context->gl_vtable;
|
|
out_tex_target = gst_gl_texture_target_to_gl (out_target);
|
|
out_gl_format = gst_gl_format_from_gl_texture_type (out_tex_type);
|
|
|
|
if (!gl->GenFramebuffers) {
|
|
GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Framebuffer objects not supported");
|
|
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... */
|
|
/* create a framebuffer object */
|
|
gl->GenFramebuffers (1, &fbo);
|
|
gl->BindFramebuffer (GL_FRAMEBUFFER, fbo);
|
|
|
|
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->context))
|
|
// goto fbo_error;
|
|
|
|
gl->BindTexture (out_tex_target, tex_id);
|
|
gl->CopyTexImage2D (out_tex_target, 0, out_gl_format, 0, 0, out_width,
|
|
out_height, 0);
|
|
|
|
gl->BindTexture (out_tex_target, 0);
|
|
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
|
|
gl->DeleteFramebuffers (1, &fbo);
|
|
|
|
gst_memory_unmap (GST_MEMORY_CAST (src), &sinfo);
|
|
|
|
return TRUE;
|
|
|
|
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 =
|
|
gst_gl_format_from_gl_texture_type (copy_params->src->tex_type);
|
|
out_gl_type = GL_UNSIGNED_BYTE;
|
|
if (copy_params->out_format == GST_VIDEO_GL_TEXTURE_TYPE_RGB16)
|
|
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_type,
|
|
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,
|
|
¶ms, &src->info, src->plane, &src->valign, NULL, NULL);
|
|
|
|
if (GST_MEMORY_FLAG_IS_SET (src, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) {
|
|
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;
|
|
}
|
|
} else {
|
|
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_type, 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);
|
|
gst_memory_unref (GST_MEMORY_CAST (dest));
|
|
return NULL;
|
|
}
|
|
|
|
gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo);
|
|
}
|
|
|
|
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)
|
|
{
|
|
GstGLMemory *mem;
|
|
|
|
g_return_val_if_fail (params->parent.
|
|
alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, NULL);
|
|
|
|
mem = g_new0 (GstGLMemory, 1);
|
|
|
|
if (params->parent.
|
|
alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
|
|
mem->tex_id = params->parent.gl_handle;
|
|
mem->texture_wrapped = TRUE;
|
|
}
|
|
|
|
gst_gl_memory_init (mem, GST_ALLOCATOR_CAST (allocator), NULL,
|
|
params->parent.context, params->target, params->parent.alloc_params,
|
|
params->v_info, params->plane, params->valign, params->parent.user_data,
|
|
params->parent.notify);
|
|
|
|
if (params->parent.
|
|
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 (params->parent.
|
|
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
|
|
* @taget: the #GstGLTextureTarget
|
|
* @tex_type: the #GstVideoGLTextureType
|
|
* @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_type, @width and @height.
|
|
*
|
|
* If @respecify is %TRUE, then the copy is performed in terms of the texture
|
|
* data. This is useful for splitting RGBA textures into RG or R textures or
|
|
* vice versa. The requirement for this to succeed is that the backing texture
|
|
* data must be the same size, i.e. say a RGBA8 texture is converted into a RG8
|
|
* texture, then the RG texture must have twice as many pixels available for
|
|
* output as the RGBA texture.
|
|
*
|
|
* Otherwise, if @respecify is %FALSE, then the copy is performed per texel
|
|
* using glCopyTexImage. See the OpenGL specification for details on the
|
|
* mappings between texture formats.
|
|
*
|
|
* Returns: Whether the copy suceeded
|
|
*/
|
|
gboolean
|
|
gst_gl_memory_copy_into (GstGLMemory * gl_mem, guint tex_id,
|
|
GstGLTextureTarget target, GstVideoGLTextureType tex_type, 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_type = tex_type;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
GstVideoGLTextureType
|
|
gst_gl_memory_get_texture_type (GstGLMemory * gl_mem)
|
|
{
|
|
g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
|
|
|
|
return gl_mem->tex_type;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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.
|
|
*/
|
|
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_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
|
|
*/
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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,
|
|
gpointer wrapped_data, guint 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->plane = plane;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_video_allocation_params_new:
|
|
* @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
|
|
*
|
|
* Returns: a new #GstGLVideoAllocationParams for allocating #GstGLMemory's
|
|
*/
|
|
GstGLVideoAllocationParams *
|
|
gst_gl_video_allocation_params_new (GstGLContext * context,
|
|
GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
|
|
GstVideoAlignment * valign, GstGLTextureTarget target)
|
|
{
|
|
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, 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
|
|
* @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
|
|
*/
|
|
GstGLVideoAllocationParams *
|
|
gst_gl_video_allocation_params_new_wrapped_data (GstGLContext * context,
|
|
GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
|
|
GstVideoAlignment * valign, GstGLTextureTarget target,
|
|
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, wrapped_data, 0, 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_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
|
|
*/
|
|
GstGLVideoAllocationParams *
|
|
gst_gl_video_allocation_params_new_wrapped_texture (GstGLContext * context,
|
|
GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
|
|
GstVideoAlignment * valign, GstGLTextureTarget target,
|
|
guint tex_id, 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, NULL, tex_id, user_data, notify)) {
|
|
g_free (params);
|
|
return NULL;
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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->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
|
|
*
|
|
* Returns: whether the buffer was correctly setup
|
|
*/
|
|
gboolean
|
|
gst_gl_memory_setup_buffer (GstGLMemoryAllocator * allocator,
|
|
GstBuffer * buffer, GstGLVideoAllocationParams * params)
|
|
{
|
|
GstGLBaseMemoryAllocator *base_allocator;
|
|
guint n_mem, i, v, views;
|
|
|
|
g_return_val_if_fail (params != NULL, FALSE);
|
|
g_return_val_if_fail ((params->parent.
|
|
alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM)
|
|
== 0, FALSE);
|
|
g_return_val_if_fail ((params->parent.alloc_flags &
|
|
GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) == 0, FALSE);
|
|
g_return_val_if_fail (params->parent.
|
|
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;
|
|
|
|
for (v = 0; v < views; v++) {
|
|
for (i = 0; i < n_mem; i++) {
|
|
GstGLMemory *gl_mem;
|
|
|
|
params->plane = 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;
|
|
}
|
|
|
|
GstGLMemoryAllocator *
|
|
gst_gl_memory_allocator_get_default (GstGLContext * context)
|
|
{
|
|
GstGLMemoryAllocator *allocator = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
|
|
|
|
if (USING_OPENGL (context) || USING_OPENGL3 (context)
|
|
|| USING_GLES3 (context)) {
|
|
allocator = (GstGLMemoryAllocator *)
|
|
gst_allocator_find (GST_GL_MEMORY_PBO_ALLOCATOR_NAME);
|
|
} else {
|
|
allocator = (GstGLMemoryAllocator *)
|
|
gst_allocator_find (GST_GL_MEMORY_ALLOCATOR_NAME);
|
|
}
|
|
|
|
return allocator;
|
|
}
|