mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 19:31:12 +00:00
149cb5a7cf
Put EGL specific code to a separate file and create the same functions for EAGL, the Apple's specific EGL implementaton. At this point, the EAGL version wasn't compiled or tested as there isn't any simple documented way to build 1.0 for iOS. This code for the EAGL version is still the 0.10 version, some updates should be made when 1.0 is buildable for iOS.
909 lines
31 KiB
C
909 lines
31 KiB
C
/*
|
|
* Copyright (C) 2012-2013 Collabora Ltd.
|
|
* @author: Reynaldo H. Verdejo Pinochet <reynaldo@collabora.com>
|
|
* @author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
|
* @author: Thiago Santos <thiago.sousa.santos@collabora.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
|
|
* which case the following provisions apply instead of the ones
|
|
* mentioned above:
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "gstegladaptation.h"
|
|
#include "video_platform_wrapper.h"
|
|
#include <string.h>
|
|
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#include <gst/egl/egl.h>
|
|
|
|
/* Some EGL implementations are reporting wrong
|
|
* values for the display's EGL_PIXEL_ASPECT_RATIO.
|
|
* They are required by the khronos specs to report
|
|
* this value as w/h * EGL_DISPLAY_SCALING (Which is
|
|
* a constant with value 10000) but at least the
|
|
* Galaxy SIII (Android) is reporting just 1 when
|
|
* w = h. We use these two to bound returned values to
|
|
* sanity.
|
|
*/
|
|
#define EGL_SANE_DAR_MIN ((EGL_DISPLAY_SCALING)/10)
|
|
#define EGL_SANE_DAR_MAX ((EGL_DISPLAY_SCALING)*10)
|
|
|
|
/*
|
|
* GstEglGlesRenderContext:
|
|
* @config: Current EGL config
|
|
* @eglcontext: Current EGL context
|
|
* @egl_minor: EGL version (minor)
|
|
* @egl_major: EGL version (major)
|
|
*
|
|
* This struct holds the sink's EGL/GLES rendering context.
|
|
*/
|
|
struct _GstEglGlesRenderContext
|
|
{
|
|
EGLConfig config;
|
|
EGLContext eglcontext;
|
|
EGLSurface surface;
|
|
EGLint egl_minor, egl_major;
|
|
};
|
|
|
|
gboolean
|
|
got_egl_error (const char *wtf)
|
|
{
|
|
EGLint error;
|
|
|
|
if ((error = eglGetError ()) != EGL_SUCCESS) {
|
|
GST_CAT_DEBUG (GST_CAT_DEFAULT, "EGL ERROR: %s returned 0x%04x", wtf,
|
|
error);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Prints available EGL/GLES extensions
|
|
* If another rendering path is implemented this is the place
|
|
* where you want to check for the availability of its supporting
|
|
* EGL/GLES extensions.
|
|
*/
|
|
void
|
|
gst_egl_adaptation_init_egl_exts (GstEglAdaptationContext * ctx)
|
|
{
|
|
const char *eglexts;
|
|
unsigned const char *glexts;
|
|
|
|
eglexts = eglQueryString (gst_egl_display_get (ctx->display), EGL_EXTENSIONS);
|
|
glexts = glGetString (GL_EXTENSIONS);
|
|
|
|
GST_DEBUG_OBJECT (ctx->element, "Available EGL extensions: %s\n",
|
|
GST_STR_NULL (eglexts));
|
|
GST_DEBUG_OBJECT (ctx->element, "Available GLES extensions: %s\n",
|
|
GST_STR_NULL ((const char *) glexts));
|
|
|
|
return;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_init_egl_display (GstEglAdaptationContext * ctx)
|
|
{
|
|
GstMessage *msg;
|
|
EGLDisplay display;
|
|
GST_DEBUG_OBJECT (ctx->element, "Enter EGL initial configuration");
|
|
|
|
if (!platform_wrapper_init ()) {
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't init EGL platform wrapper");
|
|
goto HANDLE_ERROR;
|
|
}
|
|
#ifdef USE_EGL_RPI
|
|
/* See https://github.com/raspberrypi/firmware/issues/99 */
|
|
if (!eglMakeCurrent ((EGLDisplay) 1, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT)) {
|
|
got_egl_error ("eglMakeCurrent");
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
msg = gst_message_new_need_context (GST_OBJECT_CAST (ctx->element));
|
|
gst_message_add_context_type (msg, GST_EGL_DISPLAY_CONTEXT_TYPE);
|
|
gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg);
|
|
|
|
GST_OBJECT_LOCK (ctx->element);
|
|
if (ctx->set_display) {
|
|
GstContext *context;
|
|
|
|
ctx->display = gst_egl_display_ref (ctx->set_display);
|
|
GST_OBJECT_UNLOCK (ctx->element);
|
|
context = gst_element_get_context (GST_ELEMENT_CAST (ctx->element));
|
|
if (!context)
|
|
context = gst_context_new ();
|
|
context = gst_context_make_writable (context);
|
|
gst_context_set_egl_display (context, ctx->display);
|
|
gst_element_set_context (GST_ELEMENT_CAST (ctx->element), context);
|
|
} else {
|
|
GstContext *context;
|
|
|
|
GST_OBJECT_UNLOCK (ctx->element);
|
|
|
|
display = eglGetDisplay (EGL_DEFAULT_DISPLAY);
|
|
if (display == EGL_NO_DISPLAY) {
|
|
GST_ERROR_OBJECT (ctx->element, "Could not get EGL display connection");
|
|
goto HANDLE_ERROR; /* No EGL error is set by eglGetDisplay() */
|
|
}
|
|
ctx->display = gst_egl_display_new (display);
|
|
|
|
context = gst_context_new ();
|
|
gst_context_set_egl_display (context, ctx->display);
|
|
|
|
msg = gst_message_new_have_context (GST_OBJECT (ctx->element), context);
|
|
gst_element_post_message (GST_ELEMENT_CAST (ctx->element), msg);
|
|
|
|
context = gst_element_get_context (GST_ELEMENT_CAST (ctx->element));
|
|
if (!context)
|
|
context = gst_context_new ();
|
|
context = gst_context_make_writable (context);
|
|
gst_context_set_egl_display (context, ctx->display);
|
|
gst_element_set_context (GST_ELEMENT_CAST (ctx->element), context);
|
|
}
|
|
|
|
if (!eglInitialize (gst_egl_display_get (ctx->display),
|
|
&ctx->eglglesctx->egl_major, &ctx->eglglesctx->egl_minor)) {
|
|
got_egl_error ("eglInitialize");
|
|
GST_ERROR_OBJECT (ctx->element, "Could not init EGL display connection");
|
|
goto HANDLE_EGL_ERROR;
|
|
}
|
|
|
|
/* Check against required EGL version
|
|
* XXX: Need to review the version requirement in terms of the needed API
|
|
*/
|
|
if (ctx->eglglesctx->egl_major < GST_EGLGLESSINK_EGL_MIN_VERSION) {
|
|
GST_ERROR_OBJECT (ctx->element, "EGL v%d needed, but you only have v%d.%d",
|
|
GST_EGLGLESSINK_EGL_MIN_VERSION, ctx->eglglesctx->egl_major,
|
|
ctx->eglglesctx->egl_minor);
|
|
goto HANDLE_ERROR;
|
|
}
|
|
|
|
GST_INFO_OBJECT (ctx->element, "System reports supported EGL version v%d.%d",
|
|
ctx->eglglesctx->egl_major, ctx->eglglesctx->egl_minor);
|
|
|
|
eglBindAPI (EGL_OPENGL_ES_API);
|
|
|
|
return TRUE;
|
|
|
|
/* Errors */
|
|
HANDLE_EGL_ERROR:
|
|
GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x", eglGetError ());
|
|
HANDLE_ERROR:
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't setup window/surface from handle");
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_context_make_current (GstEglAdaptationContext * ctx,
|
|
gboolean bind)
|
|
{
|
|
g_assert (ctx->display != NULL);
|
|
|
|
if (bind && ctx->eglglesctx->surface && ctx->eglglesctx->eglcontext) {
|
|
EGLContext *cur_ctx = eglGetCurrentContext ();
|
|
|
|
if (cur_ctx == ctx->eglglesctx->eglcontext) {
|
|
GST_DEBUG_OBJECT (ctx->element,
|
|
"Already attached the context to thread %p", g_thread_self ());
|
|
return TRUE;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (ctx->element, "Attaching context to thread %p",
|
|
g_thread_self ());
|
|
if (!eglMakeCurrent (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface, ctx->eglglesctx->surface,
|
|
ctx->eglglesctx->eglcontext)) {
|
|
got_egl_error ("eglMakeCurrent");
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't bind context");
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
GST_DEBUG_OBJECT (ctx->element, "Detaching context from thread %p",
|
|
g_thread_self ());
|
|
if (!eglMakeCurrent (gst_egl_display_get (ctx->display),
|
|
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
|
got_egl_error ("eglMakeCurrent");
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* XXX: Lock eglgles context? */
|
|
gboolean
|
|
gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext * ctx)
|
|
{
|
|
gint width, height;
|
|
|
|
/* Save surface dims */
|
|
eglQuerySurface (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface, EGL_WIDTH, &width);
|
|
eglQuerySurface (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface, EGL_HEIGHT, &height);
|
|
|
|
if (width != ctx->surface_width || height != ctx->surface_height) {
|
|
ctx->surface_width = width;
|
|
ctx->surface_height = height;
|
|
GST_INFO_OBJECT (ctx->element, "Got surface of %dx%d pixels", width,
|
|
height);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx)
|
|
{
|
|
gboolean ret = eglSwapBuffers (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface);
|
|
if (ret == EGL_FALSE) {
|
|
got_egl_error ("eglSwapBuffers");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
_gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only,
|
|
gint * num_configs)
|
|
{
|
|
EGLint cfg_number;
|
|
gboolean ret;
|
|
EGLConfig *config = NULL;
|
|
|
|
if (!try_only)
|
|
config = &ctx->eglglesctx->config;
|
|
|
|
ret = eglChooseConfig (gst_egl_display_get (ctx->display),
|
|
eglglessink_RGBA8888_attribs, config, 1, &cfg_number) != EGL_FALSE;
|
|
|
|
if (!ret)
|
|
got_egl_error ("eglChooseConfig");
|
|
else if (num_configs)
|
|
*num_configs = cfg_number;
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx)
|
|
{
|
|
ctx->eglglesctx->surface =
|
|
eglCreateWindowSurface (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->config, ctx->used_window, NULL);
|
|
|
|
if (ctx->eglglesctx->surface == EGL_NO_SURFACE) {
|
|
got_egl_error ("eglCreateWindowSurface");
|
|
GST_ERROR_OBJECT (ctx->element, "Can't create surface");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx)
|
|
{
|
|
EGLint swap_behavior;
|
|
|
|
ctx->buffer_preserved = FALSE;
|
|
if (eglQuerySurface (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface, EGL_SWAP_BEHAVIOR, &swap_behavior)) {
|
|
GST_DEBUG_OBJECT (ctx->element, "Buffer swap behavior %x", swap_behavior);
|
|
ctx->buffer_preserved = swap_behavior == EGL_BUFFER_PRESERVED;
|
|
} else {
|
|
GST_DEBUG_OBJECT (ctx->element, "Can't query buffer swap behavior");
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx)
|
|
{
|
|
EGLint display_par;
|
|
|
|
/* fixed value */
|
|
ctx->pixel_aspect_ratio_d = EGL_DISPLAY_SCALING;
|
|
|
|
/* Save display's pixel aspect ratio
|
|
*
|
|
* DAR is reported as w/h * EGL_DISPLAY_SCALING wich is
|
|
* a constant with value 10000. This attribute is only
|
|
* supported if the EGL version is >= 1.2
|
|
* XXX: Setup this as a property.
|
|
* or some other one time check. Right now it's being called once
|
|
* per frame.
|
|
*/
|
|
if (ctx->eglglesctx->egl_major == 1 && ctx->eglglesctx->egl_minor < 2) {
|
|
GST_DEBUG_OBJECT (ctx->element, "Can't query PAR. Using default: %dx%d",
|
|
EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING);
|
|
ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING;
|
|
} else {
|
|
eglQuerySurface (ctx->display,
|
|
ctx->eglglesctx->surface, EGL_PIXEL_ASPECT_RATIO, &display_par);
|
|
/* Fix for outbound DAR reporting on some implementations not
|
|
* honoring the 'should return w/h * EGL_DISPLAY_SCALING' spec
|
|
* requirement
|
|
*/
|
|
if (display_par == EGL_UNKNOWN || display_par < EGL_SANE_DAR_MIN ||
|
|
display_par > EGL_SANE_DAR_MAX) {
|
|
GST_DEBUG_OBJECT (ctx->element, "Nonsensical PAR value returned: %d. "
|
|
"Bad EGL implementation? "
|
|
"Will use default: %d/%d", ctx->pixel_aspect_ratio_n,
|
|
EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING);
|
|
ctx->pixel_aspect_ratio_n = EGL_DISPLAY_SCALING;
|
|
} else {
|
|
ctx->pixel_aspect_ratio_n = display_par;
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx)
|
|
{
|
|
EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
|
|
ctx->eglglesctx->eglcontext =
|
|
eglCreateContext (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->config, EGL_NO_CONTEXT, con_attribs);
|
|
|
|
if (ctx->eglglesctx->eglcontext == EGL_NO_CONTEXT) {
|
|
GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x",
|
|
eglGetError ());
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (ctx->element, "EGL Context: %p",
|
|
ctx->eglglesctx->eglcontext);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_egl_gles_image_data_free (GstEGLGLESImageData * data)
|
|
{
|
|
glDeleteTextures (1, &data->texture);
|
|
g_slice_free (GstEGLGLESImageData, data);
|
|
}
|
|
|
|
|
|
GstBuffer *
|
|
gst_egl_adaptation_allocate_eglimage (GstEglAdaptationContext * ctx,
|
|
GstAllocator * allocator, GstVideoFormat format, gint width, gint height)
|
|
{
|
|
GstEGLGLESImageData *data = NULL;
|
|
GstBuffer *buffer;
|
|
GstVideoInfo info;
|
|
gint i;
|
|
gint stride[3];
|
|
gsize offset[3];
|
|
GstMemory *mem[3] = { NULL, NULL, NULL };
|
|
guint n_mem;
|
|
GstMemoryFlags flags = 0;
|
|
|
|
memset (stride, 0, sizeof (stride));
|
|
memset (offset, 0, sizeof (offset));
|
|
|
|
if (!gst_egl_image_memory_is_mappable ())
|
|
flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
|
|
/* See https://bugzilla.gnome.org/show_bug.cgi?id=695203 */
|
|
flags |= GST_MEMORY_FLAG_NO_SHARE;
|
|
|
|
gst_video_info_set_format (&info, format, width, height);
|
|
|
|
switch (format) {
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:{
|
|
gsize size;
|
|
EGLImageKHR image;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), &size);
|
|
if (mem[0]) {
|
|
stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
|
|
n_mem = 1;
|
|
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
|
|
} else {
|
|
data = g_slice_new0 (GstEGLGLESImageData);
|
|
|
|
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 3);
|
|
size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
|
|
|
|
glGenTextures (1, &data->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto mem_error;
|
|
|
|
glBindTexture (GL_TEXTURE_2D, data->texture);
|
|
if (got_gl_error ("glBindTexture"))
|
|
goto mem_error;
|
|
|
|
/* Set 2D resizing params */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* If these are not set the texture image unit will return
|
|
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
|
|
* * frames. For a deeper explanation take a look at the OpenGL ES
|
|
* * documentation for glTexParameter */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (got_gl_error ("glTexParameteri"))
|
|
goto mem_error;
|
|
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
|
|
GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
if (got_gl_error ("glTexImage2D"))
|
|
goto mem_error;
|
|
|
|
image =
|
|
eglCreateImageKHR (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
|
|
(EGLClientBuffer) (guintptr) data->texture, NULL);
|
|
if (got_egl_error ("eglCreateImageKHR"))
|
|
goto mem_error;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_wrap (allocator, ctx->display,
|
|
image, GST_EGL_IMAGE_MEMORY_TYPE_RGB,
|
|
flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
|
|
n_mem = 1;
|
|
}
|
|
break;
|
|
}
|
|
case GST_VIDEO_FORMAT_RGB16:{
|
|
EGLImageKHR image;
|
|
gsize size;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_RGB, GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), &size);
|
|
if (mem[0]) {
|
|
stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
|
|
n_mem = 1;
|
|
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
|
|
} else {
|
|
data = g_slice_new0 (GstEGLGLESImageData);
|
|
|
|
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 2);
|
|
size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
|
|
|
|
glGenTextures (1, &data->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto mem_error;
|
|
|
|
glBindTexture (GL_TEXTURE_2D, data->texture);
|
|
if (got_gl_error ("glBindTexture"))
|
|
goto mem_error;
|
|
|
|
/* Set 2D resizing params */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* If these are not set the texture image unit will return
|
|
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
|
|
* * frames. For a deeper explanation take a look at the OpenGL ES
|
|
* * documentation for glTexParameter */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (got_gl_error ("glTexParameteri"))
|
|
goto mem_error;
|
|
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
|
|
GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
|
|
NULL);
|
|
if (got_gl_error ("glTexImage2D"))
|
|
goto mem_error;
|
|
|
|
image =
|
|
eglCreateImageKHR (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
|
|
(EGLClientBuffer) (guintptr) data->texture, NULL);
|
|
if (got_egl_error ("eglCreateImageKHR"))
|
|
goto mem_error;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_wrap (allocator, ctx->display,
|
|
image, GST_EGL_IMAGE_MEMORY_TYPE_RGB,
|
|
flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
|
|
n_mem = 1;
|
|
}
|
|
break;
|
|
}
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
case GST_VIDEO_FORMAT_NV21:{
|
|
EGLImageKHR image;
|
|
gsize size[2];
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
|
|
0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]);
|
|
mem[1] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE_ALPHA,
|
|
GST_VIDEO_INFO_COMP_WIDTH (&info, 1),
|
|
GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]);
|
|
|
|
if (mem[0] && mem[1]) {
|
|
stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info);
|
|
offset[1] = size[0];
|
|
stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info);
|
|
n_mem = 2;
|
|
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
|
|
GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
|
|
} else {
|
|
if (mem[0])
|
|
gst_memory_unref (mem[0]);
|
|
if (mem[1])
|
|
gst_memory_unref (mem[1]);
|
|
mem[0] = mem[1] = NULL;
|
|
|
|
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0));
|
|
stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1) * 2);
|
|
offset[1] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0);
|
|
size[0] = offset[1];
|
|
size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
data = g_slice_new0 (GstEGLGLESImageData);
|
|
|
|
glGenTextures (1, &data->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto mem_error;
|
|
|
|
glBindTexture (GL_TEXTURE_2D, data->texture);
|
|
if (got_gl_error ("glBindTexture"))
|
|
goto mem_error;
|
|
|
|
/* Set 2D resizing params */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* If these are not set the texture image unit will return
|
|
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
|
|
* * frames. For a deeper explanation take a look at the OpenGL ES
|
|
* * documentation for glTexParameter */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (got_gl_error ("glTexParameteri"))
|
|
goto mem_error;
|
|
|
|
if (i == 0)
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
|
|
GST_VIDEO_INFO_COMP_WIDTH (&info, i),
|
|
GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
else
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
|
|
GST_VIDEO_INFO_COMP_WIDTH (&info, i),
|
|
GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE_ALPHA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
|
|
if (got_gl_error ("glTexImage2D"))
|
|
goto mem_error;
|
|
|
|
image =
|
|
eglCreateImageKHR (ctx->display,
|
|
ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
|
|
(EGLClientBuffer) (guintptr) data->texture, NULL);
|
|
if (got_egl_error ("eglCreateImageKHR"))
|
|
goto mem_error;
|
|
|
|
mem[i] =
|
|
gst_egl_image_allocator_wrap (allocator, ctx->display,
|
|
image,
|
|
(i ==
|
|
0 ? GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE :
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE_ALPHA),
|
|
flags, size[i], data,
|
|
(GDestroyNotify) gst_egl_gles_image_data_free);
|
|
}
|
|
|
|
n_mem = 2;
|
|
}
|
|
break;
|
|
}
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_Y444:
|
|
case GST_VIDEO_FORMAT_Y42B:
|
|
case GST_VIDEO_FORMAT_Y41B:{
|
|
EGLImageKHR image;
|
|
gsize size[3];
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
|
|
0), GST_VIDEO_INFO_COMP_HEIGHT (&info, 0), &size[0]);
|
|
mem[1] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
|
|
1), GST_VIDEO_INFO_COMP_HEIGHT (&info, 1), &size[1]);
|
|
mem[2] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE, GST_VIDEO_INFO_COMP_WIDTH (&info,
|
|
2), GST_VIDEO_INFO_COMP_HEIGHT (&info, 2), &size[2]);
|
|
|
|
if (mem[0] && mem[1] && mem[2]) {
|
|
stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (&info);
|
|
offset[1] = size[0];
|
|
stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (&info);
|
|
offset[2] = size[1];
|
|
stride[2] = size[2] / GST_VIDEO_INFO_HEIGHT (&info);
|
|
n_mem = 3;
|
|
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
|
|
GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
|
|
GST_MINI_OBJECT_FLAG_SET (mem[2], GST_MEMORY_FLAG_NO_SHARE);
|
|
} else {
|
|
if (mem[0])
|
|
gst_memory_unref (mem[0]);
|
|
if (mem[1])
|
|
gst_memory_unref (mem[1]);
|
|
if (mem[2])
|
|
gst_memory_unref (mem[2]);
|
|
mem[0] = mem[1] = mem[2] = NULL;
|
|
|
|
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 0));
|
|
stride[1] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 1));
|
|
stride[2] = GST_ROUND_UP_4 (GST_VIDEO_INFO_COMP_WIDTH (&info, 2));
|
|
size[0] = stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 0);
|
|
size[1] = stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 1);
|
|
size[2] = stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&info, 2);
|
|
offset[0] = 0;
|
|
offset[1] = size[0];
|
|
offset[2] = offset[1] + size[1];
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
data = g_slice_new0 (GstEGLGLESImageData);
|
|
|
|
glGenTextures (1, &data->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto mem_error;
|
|
|
|
glBindTexture (GL_TEXTURE_2D, data->texture);
|
|
if (got_gl_error ("glBindTexture"))
|
|
goto mem_error;
|
|
|
|
/* Set 2D resizing params */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* If these are not set the texture image unit will return
|
|
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
|
|
* * frames. For a deeper explanation take a look at the OpenGL ES
|
|
* * documentation for glTexParameter */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (got_gl_error ("glTexParameteri"))
|
|
goto mem_error;
|
|
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE,
|
|
GST_VIDEO_INFO_COMP_WIDTH (&info, i),
|
|
GST_VIDEO_INFO_COMP_HEIGHT (&info, i), 0, GL_LUMINANCE,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
|
|
if (got_gl_error ("glTexImage2D"))
|
|
goto mem_error;
|
|
|
|
image =
|
|
eglCreateImageKHR (ctx->display,
|
|
ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
|
|
(EGLClientBuffer) (guintptr) data->texture, NULL);
|
|
if (got_egl_error ("eglCreateImageKHR"))
|
|
goto mem_error;
|
|
|
|
mem[i] =
|
|
gst_egl_image_allocator_wrap (allocator, ctx->display,
|
|
image, GST_EGL_IMAGE_MEMORY_TYPE_LUMINANCE,
|
|
flags, size[i], data,
|
|
(GDestroyNotify) gst_egl_gles_image_data_free);
|
|
}
|
|
|
|
n_mem = 3;
|
|
}
|
|
break;
|
|
}
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_AYUV:{
|
|
gsize size;
|
|
EGLImageKHR image;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_alloc (allocator, ctx->display,
|
|
GST_EGL_IMAGE_MEMORY_TYPE_RGBA, GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), &size);
|
|
if (mem[0]) {
|
|
stride[0] = size / GST_VIDEO_INFO_HEIGHT (&info);
|
|
n_mem = 1;
|
|
GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
|
|
} else {
|
|
data = g_slice_new0 (GstEGLGLESImageData);
|
|
|
|
stride[0] = GST_ROUND_UP_4 (GST_VIDEO_INFO_WIDTH (&info) * 4);
|
|
size = stride[0] * GST_VIDEO_INFO_HEIGHT (&info);
|
|
|
|
glGenTextures (1, &data->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto mem_error;
|
|
|
|
glBindTexture (GL_TEXTURE_2D, data->texture);
|
|
if (got_gl_error ("glBindTexture"))
|
|
goto mem_error;
|
|
|
|
/* Set 2D resizing params */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* If these are not set the texture image unit will return
|
|
* * (R, G, B, A) = black on glTexImage2D for non-POT width/height
|
|
* * frames. For a deeper explanation take a look at the OpenGL ES
|
|
* * documentation for glTexParameter */
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
if (got_gl_error ("glTexParameteri"))
|
|
goto mem_error;
|
|
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
|
|
GST_VIDEO_INFO_WIDTH (&info),
|
|
GST_VIDEO_INFO_HEIGHT (&info), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
if (got_gl_error ("glTexImage2D"))
|
|
goto mem_error;
|
|
|
|
image =
|
|
eglCreateImageKHR (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->eglcontext, EGL_GL_TEXTURE_2D_KHR,
|
|
(EGLClientBuffer) (guintptr) data->texture, NULL);
|
|
if (got_egl_error ("eglCreateImageKHR"))
|
|
goto mem_error;
|
|
|
|
mem[0] =
|
|
gst_egl_image_allocator_wrap (allocator, ctx->display,
|
|
image, GST_EGL_IMAGE_MEMORY_TYPE_RGBA,
|
|
flags, size, data, (GDestroyNotify) gst_egl_gles_image_data_free);
|
|
|
|
n_mem = 1;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
buffer = gst_buffer_new ();
|
|
gst_buffer_add_video_meta_full (buffer, 0, format, width, height,
|
|
GST_VIDEO_INFO_N_PLANES (&info), offset, stride);
|
|
|
|
for (i = 0; i < n_mem; i++)
|
|
gst_buffer_append_memory (buffer, mem[i]);
|
|
|
|
return buffer;
|
|
|
|
mem_error:
|
|
{
|
|
GST_ERROR_OBJECT (ctx->element, "Failed to create EGLImage");
|
|
|
|
if (data)
|
|
gst_egl_gles_image_data_free (data);
|
|
|
|
if (mem[0])
|
|
gst_memory_unref (mem[0]);
|
|
if (mem[1])
|
|
gst_memory_unref (mem[1]);
|
|
if (mem[2])
|
|
gst_memory_unref (mem[2]);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx,
|
|
gpointer * own_window_data)
|
|
{
|
|
platform_destroy_native_window (gst_egl_display_get
|
|
(ctx->display), ctx->used_window, own_window_data);
|
|
ctx->used_window = 0;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_create_native_window (GstEglAdaptationContext * ctx,
|
|
gint width, gint height, gpointer * own_window_data)
|
|
{
|
|
EGLNativeWindowType window =
|
|
platform_create_native_window (width, height, own_window_data);
|
|
if (window)
|
|
gst_egl_adaptation_set_window (ctx, window);
|
|
GST_DEBUG_OBJECT (ctx->element, "Using window handle %p", window);
|
|
return window != 0;
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window)
|
|
{
|
|
ctx->window = (EGLNativeWindowType) window;
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_init (GstEglAdaptationContext * ctx)
|
|
{
|
|
ctx->eglglesctx = g_new0 (GstEglGlesRenderContext, 1);
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx)
|
|
{
|
|
g_free (ctx->eglglesctx);
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx)
|
|
{
|
|
if (ctx->eglglesctx->surface) {
|
|
eglDestroySurface (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->surface);
|
|
ctx->eglglesctx->surface = NULL;
|
|
ctx->have_surface = FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx)
|
|
{
|
|
if (ctx->eglglesctx->eglcontext) {
|
|
eglDestroyContext (gst_egl_display_get (ctx->display),
|
|
ctx->eglglesctx->eglcontext);
|
|
ctx->eglglesctx->eglcontext = NULL;
|
|
}
|
|
}
|