mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
5ba0ab3da9
Removes extremely similar code from glmemorypbo.
1199 lines
38 KiB
C
1199 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)
|
|
{
|
|
gst_gl_memory_texsubimage (gl_mem, gl_mem->mem.data);
|
|
}
|
|
|
|
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;
|
|
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) read_pointer);
|
|
|
|
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)
|
|
{
|
|
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 = 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 (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
|
|
* @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);
|
|
|
|
/* 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;
|
|
}
|