eglglessink: refactor to allow eagl support

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.
This commit is contained in:
Thiago Santos 2013-04-10 17:27:27 -03:00
parent 2a877b072b
commit 149cb5a7cf
7 changed files with 3398 additions and 2881 deletions

View file

@ -1,20 +1,39 @@
plugin_LTLIBRARIES = libgsteglglessink.la
libgsteglglessink_la_SOURCES = gsteglglessink.c gstegladaptation.c video_platform_wrapper.c
if HAVE_IOS
DISTRO_SRC = gstegladaptation_eagl.m
else
DISTRO_SRC = gstegladaptation_egl.c video_platform_wrapper.c
endif
libgsteglglessink_la_SOURCES = gsteglglessink.c gstegladaptation.c $(DISTRO_SRC)
libgsteglglessink_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_CFLAGS) \
$(EGL_CFLAGS) \
$(EGL_CFLAGS) \
$(EGLGLES_CFLAGS)
libgsteglglessink_la_OBJCFLAGS = $(GST_PLUGINS_BAD_OBJCFLAGS) \
$(GST_PLUGINS_BASE_OBJCFLAGS) \
$(GST_BASE_OBJCFLAGS) \
$(GST_OBJCFLAGS) \
$(EGLGLES_OBJCFLAGS) \
-fobjc-abi-version=2 -fobjc-legacy-dispatch
libgsteglglessink_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) \
$(GST_PLUGINS_BASE_LIBS) $(EGL_LIBS) $(EGLGLES_LIBS) \
-lgstvideo-$(GST_API_VERSION) \
$(top_builddir)/gst-libs/gst/egl/libgstegl-$(GST_API_VERSION).la
if HAVE_IOS
libgsteglglessink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore -Wl,-framework,IOKit -Wl,-framework,UIKit -Wl,-framework,CoreGraphics -Wl,-framework,CoreFoundation -Wl,-framework,Foundation -W
else
libgsteglglessink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
endif
libgsteglglessink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = gsteglglessink.h gstegladaptation.h video_platform_wrapper.h
EXTRA_DIST = gsteglglessink.c gstegladaptation.c gstegladaptation_egl.c video_platform_wrapper.c gstegladaptation_eagl.m $(noinst_HEADERS)

View file

@ -44,20 +44,10 @@
* Boston, MA 02111-1307, USA.
*/
#include <gst/video/video.h>
#include "gstegladaptation.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)
#include <gst/video/video.h>
#include <string.h>
/* GLESv2 GLSL Shaders
*
@ -66,16 +56,6 @@
* conversion.
*/
static const EGLint eglglessink_RGBA8888_attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
/* *INDENT-OFF* */
/* Direct vertex copy */
static const char *vert_COPY_prog = {
@ -242,7 +222,6 @@ GstCaps *
gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext *
ctx)
{
EGLint cfg_number;
GstCaps *caps = NULL, *copy1, *copy2;
guint i, n;
@ -250,8 +229,7 @@ gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext *
"Building initial list of wanted eglattribs per format");
/* Init supported format/caps list */
if (eglChooseConfig (gst_egl_display_get (ctx->eglglesctx.display),
eglglessink_RGBA8888_attribs, NULL, 1, &cfg_number) != EGL_FALSE) {
if (_gst_egl_choose_config (ctx, TRUE, NULL)) {
caps = gst_caps_new_empty ();
@ -323,53 +301,41 @@ gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext *
}
void
gst_egl_adaptation_wipe_eglglesctx (GstEglAdaptationContext * ctx)
gst_egl_adaptation_cleanup (GstEglAdaptationContext * ctx)
{
gint i;
glUseProgram (0);
if (ctx->have_vbo) {
glDeleteBuffers (1, &ctx->eglglesctx.position_buffer);
glDeleteBuffers (1, &ctx->eglglesctx.index_buffer);
glDeleteBuffers (1, &ctx->position_buffer);
glDeleteBuffers (1, &ctx->index_buffer);
ctx->have_vbo = FALSE;
}
if (ctx->have_texture) {
glDeleteTextures (ctx->eglglesctx.n_textures + 1, ctx->eglglesctx.texture);
glDeleteTextures (ctx->n_textures + 1, ctx->texture);
ctx->have_texture = FALSE;
ctx->eglglesctx.n_textures = 0;
ctx->n_textures = 0;
}
for (i = 0; i < 3; i++) {
if (ctx->eglglesctx.glslprogram[i]) {
glDetachShader (ctx->eglglesctx.glslprogram[i],
ctx->eglglesctx.fragshader[i]);
glDetachShader (ctx->eglglesctx.glslprogram[i],
ctx->eglglesctx.vertshader[i]);
glDeleteProgram (ctx->eglglesctx.glslprogram[i]);
glDeleteShader (ctx->eglglesctx.fragshader[i]);
glDeleteShader (ctx->eglglesctx.vertshader[i]);
ctx->eglglesctx.glslprogram[i] = 0;
ctx->eglglesctx.fragshader[i] = 0;
ctx->eglglesctx.vertshader[i] = 0;
if (ctx->glslprogram[i]) {
glDetachShader (ctx->glslprogram[i], ctx->fragshader[i]);
glDetachShader (ctx->glslprogram[i], ctx->vertshader[i]);
glDeleteProgram (ctx->glslprogram[i]);
glDeleteShader (ctx->fragshader[i]);
glDeleteShader (ctx->vertshader[i]);
ctx->glslprogram[i] = 0;
ctx->fragshader[i] = 0;
ctx->vertshader[i] = 0;
}
}
gst_egl_adaptation_context_make_current (ctx, FALSE);
if (ctx->eglglesctx.surface) {
eglDestroySurface (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.surface);
ctx->eglglesctx.surface = NULL;
ctx->have_surface = FALSE;
}
if (ctx->eglglesctx.eglcontext) {
eglDestroyContext (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.eglcontext);
ctx->eglglesctx.eglcontext = NULL;
}
gst_egl_adaptation_destroy_surface (ctx);
gst_egl_adaptation_destroy_context (ctx);
}
gboolean
@ -384,106 +350,6 @@ got_gl_error (const char *wtf)
return FALSE;
}
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->eglglesctx.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;
}
/* 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->eglglesctx.display),
ctx->eglglesctx.surface, EGL_WIDTH, &width);
eglQuerySurface (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.surface, EGL_HEIGHT, &height);
if (width != ctx->eglglesctx.surface_width ||
height != ctx->eglglesctx.surface_height) {
ctx->eglglesctx.surface_width = width;
ctx->eglglesctx.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_make_current (GstEglAdaptationContext * ctx,
gboolean bind)
{
g_assert (ctx->eglglesctx.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->eglglesctx.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->eglglesctx.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;
}
static gboolean
create_shader_program (GstEglAdaptationContext * ctx, GLuint * prog,
GLuint * vert, GLuint * frag, const gchar * vert_text,
@ -584,36 +450,22 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
GstVideoFormat format)
{
GLboolean ret;
EGLint display_par;
const gchar *texnames[3] = { NULL, };
gchar *frag_prog = NULL;
gboolean free_frag_prog = FALSE;
EGLint swap_behavior;
gint i;
GST_DEBUG_OBJECT (ctx->element, "Enter EGL surface setup");
ctx->eglglesctx.surface =
eglCreateWindowSurface (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.config, ctx->eglglesctx.used_window, NULL);
if (ctx->eglglesctx.surface == EGL_NO_SURFACE) {
got_egl_error ("eglCreateWindowSurface");
if (!gst_egl_adaptation_create_surface (ctx)) {
GST_ERROR_OBJECT (ctx->element, "Can't create surface");
goto HANDLE_EGL_ERROR_LOCKED;
goto HANDLE_ERROR_LOCKED;
}
ctx->eglglesctx.buffer_preserved = FALSE;
if (eglQuerySurface (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.surface, EGL_SWAP_BEHAVIOR, &swap_behavior)) {
GST_DEBUG_OBJECT (ctx->element, "Buffer swap behavior %x", swap_behavior);
ctx->eglglesctx.buffer_preserved = swap_behavior == EGL_BUFFER_PRESERVED;
} else {
GST_DEBUG_OBJECT (ctx->element, "Can't query buffer swap behavior");
}
gst_egl_adaptation_query_buffer_preserved (ctx);
if (!gst_egl_adaptation_context_make_current (ctx, TRUE))
goto HANDLE_EGL_ERROR_LOCKED;
goto HANDLE_ERROR_LOCKED;
gst_egl_adaptation_init_egl_exts (ctx);
@ -626,28 +478,7 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
* 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->eglglesctx.pixel_aspect_ratio = EGL_DISPLAY_SCALING;
} else {
eglQuerySurface (gst_egl_display_get (ctx->eglglesctx.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->eglglesctx.pixel_aspect_ratio,
EGL_DISPLAY_SCALING, EGL_DISPLAY_SCALING);
ctx->eglglesctx.pixel_aspect_ratio = EGL_DISPLAY_SCALING;
} else {
ctx->eglglesctx.pixel_aspect_ratio = display_par;
}
}
gst_egl_adaptation_query_par (ctx);
/* Save surface dims */
gst_egl_adaptation_update_surface_dimensions (ctx);
@ -671,7 +502,7 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
case GST_VIDEO_FORMAT_AYUV:
frag_prog = (gchar *) frag_AYUV_prog;
free_frag_prog = FALSE;
ctx->eglglesctx.n_textures = 1;
ctx->n_textures = 1;
texnames[0] = "tex";
break;
case GST_VIDEO_FORMAT_Y444:
@ -681,7 +512,7 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
case GST_VIDEO_FORMAT_Y41B:
frag_prog = (gchar *) frag_PLANAR_YUV_prog;
free_frag_prog = FALSE;
ctx->eglglesctx.n_textures = 3;
ctx->n_textures = 3;
texnames[0] = "Ytex";
texnames[1] = "Utex";
texnames[2] = "Vtex";
@ -689,14 +520,14 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
case GST_VIDEO_FORMAT_NV12:
frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'r', 'a');
free_frag_prog = TRUE;
ctx->eglglesctx.n_textures = 2;
ctx->n_textures = 2;
texnames[0] = "Ytex";
texnames[1] = "UVtex";
break;
case GST_VIDEO_FORMAT_NV21:
frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'a', 'r');
free_frag_prog = TRUE;
ctx->eglglesctx.n_textures = 2;
ctx->n_textures = 2;
texnames[0] = "Ytex";
texnames[1] = "UVtex";
break;
@ -705,21 +536,21 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
case GST_VIDEO_FORMAT_BGRA:
frag_prog = g_strdup_printf (frag_REORDER_prog, 'b', 'g', 'r');
free_frag_prog = TRUE;
ctx->eglglesctx.n_textures = 1;
ctx->n_textures = 1;
texnames[0] = "tex";
break;
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_ARGB:
frag_prog = g_strdup_printf (frag_REORDER_prog, 'g', 'b', 'a');
free_frag_prog = TRUE;
ctx->eglglesctx.n_textures = 1;
ctx->n_textures = 1;
texnames[0] = "tex";
break;
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_ABGR:
frag_prog = g_strdup_printf (frag_REORDER_prog, 'a', 'b', 'g');
free_frag_prog = TRUE;
ctx->eglglesctx.n_textures = 1;
ctx->n_textures = 1;
texnames[0] = "tex";
break;
case GST_VIDEO_FORMAT_RGB:
@ -728,7 +559,7 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
case GST_VIDEO_FORMAT_RGB16:
frag_prog = (gchar *) frag_COPY_prog;
free_frag_prog = FALSE;
ctx->eglglesctx.n_textures = 1;
ctx->n_textures = 1;
texnames[0] = "tex";
break;
default:
@ -737,9 +568,9 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
}
if (!create_shader_program (ctx,
&ctx->eglglesctx.glslprogram[0],
&ctx->eglglesctx.vertshader[0],
&ctx->eglglesctx.fragshader[0], vert_COPY_prog, frag_prog)) {
&ctx->glslprogram[0],
&ctx->vertshader[0],
&ctx->fragshader[0], vert_COPY_prog, frag_prog)) {
if (free_frag_prog)
g_free (frag_prog);
frag_prog = NULL;
@ -749,37 +580,34 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
g_free (frag_prog);
frag_prog = NULL;
ctx->eglglesctx.position_loc[0] =
glGetAttribLocation (ctx->eglglesctx.glslprogram[0], "position");
ctx->eglglesctx.texpos_loc[0] =
glGetAttribLocation (ctx->eglglesctx.glslprogram[0], "texpos");
ctx->eglglesctx.tex_scale_loc[0][0] =
glGetUniformLocation (ctx->eglglesctx.glslprogram[0], "tex_scale0");
ctx->eglglesctx.tex_scale_loc[0][1] =
glGetUniformLocation (ctx->eglglesctx.glslprogram[0], "tex_scale1");
ctx->eglglesctx.tex_scale_loc[0][2] =
glGetUniformLocation (ctx->eglglesctx.glslprogram[0], "tex_scale2");
ctx->position_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "position");
ctx->texpos_loc[0] = glGetAttribLocation (ctx->glslprogram[0], "texpos");
ctx->tex_scale_loc[0][0] =
glGetUniformLocation (ctx->glslprogram[0], "tex_scale0");
ctx->tex_scale_loc[0][1] =
glGetUniformLocation (ctx->glslprogram[0], "tex_scale1");
ctx->tex_scale_loc[0][2] =
glGetUniformLocation (ctx->glslprogram[0], "tex_scale2");
glEnableVertexAttribArray (ctx->eglglesctx.position_loc[0]);
glEnableVertexAttribArray (ctx->position_loc[0]);
if (got_gl_error ("glEnableVertexAttribArray"))
goto HANDLE_ERROR;
glEnableVertexAttribArray (ctx->eglglesctx.texpos_loc[0]);
glEnableVertexAttribArray (ctx->texpos_loc[0]);
if (got_gl_error ("glEnableVertexAttribArray"))
goto HANDLE_ERROR;
for (i = 0; i < ctx->eglglesctx.n_textures; i++) {
ctx->eglglesctx.tex_loc[0][i] =
glGetUniformLocation (ctx->eglglesctx.glslprogram[0], texnames[i]);
for (i = 0; i < ctx->n_textures; i++) {
ctx->tex_loc[0][i] =
glGetUniformLocation (ctx->glslprogram[0], texnames[i]);
}
/* custom rendering shader */
if (!create_shader_program (ctx,
&ctx->eglglesctx.glslprogram[2],
&ctx->eglglesctx.vertshader[2],
&ctx->eglglesctx.fragshader[2], vert_COPY_prog,
frag_COPY_DIRECT_prog)) {
&ctx->glslprogram[2],
&ctx->vertshader[2],
&ctx->fragshader[2], vert_COPY_prog, frag_COPY_DIRECT_prog)) {
if (free_frag_prog)
g_free (frag_prog);
frag_prog = NULL;
@ -789,31 +617,27 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
g_free (frag_prog);
frag_prog = NULL;
ctx->eglglesctx.position_loc[2] =
glGetAttribLocation (ctx->eglglesctx.glslprogram[2], "position");
ctx->eglglesctx.texpos_loc[1] =
glGetAttribLocation (ctx->eglglesctx.glslprogram[2], "texpos");
ctx->position_loc[2] = glGetAttribLocation (ctx->glslprogram[2], "position");
ctx->texpos_loc[1] = glGetAttribLocation (ctx->glslprogram[2], "texpos");
glEnableVertexAttribArray (ctx->eglglesctx.position_loc[2]);
glEnableVertexAttribArray (ctx->position_loc[2]);
if (got_gl_error ("glEnableVertexAttribArray"))
goto HANDLE_ERROR;
ctx->eglglesctx.tex_loc[1][0] =
glGetUniformLocation (ctx->eglglesctx.glslprogram[2], "tex");
ctx->tex_loc[1][0] = glGetUniformLocation (ctx->glslprogram[2], "tex");
if (!ctx->eglglesctx.buffer_preserved) {
if (!ctx->buffer_preserved) {
/* Build shader program for black borders */
if (!create_shader_program (ctx,
&ctx->eglglesctx.glslprogram[1],
&ctx->eglglesctx.vertshader[1],
&ctx->eglglesctx.fragshader[1], vert_COPY_prog_no_tex,
frag_BLACK_prog))
&ctx->glslprogram[1],
&ctx->vertshader[1],
&ctx->fragshader[1], vert_COPY_prog_no_tex, frag_BLACK_prog))
goto HANDLE_ERROR;
ctx->eglglesctx.position_loc[1] =
glGetAttribLocation (ctx->eglglesctx.glslprogram[1], "position");
ctx->position_loc[1] =
glGetAttribLocation (ctx->glslprogram[1], "position");
glEnableVertexAttribArray (ctx->eglglesctx.position_loc[1]);
glEnableVertexAttribArray (ctx->position_loc[1]);
if (got_gl_error ("glEnableVertexAttribArray"))
goto HANDLE_ERROR;
}
@ -822,12 +646,12 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
if (!ctx->have_texture) {
GST_INFO_OBJECT (ctx->element, "Performing initial texture setup");
glGenTextures (ctx->eglglesctx.n_textures + 1, ctx->eglglesctx.texture);
glGenTextures (ctx->n_textures + 1, ctx->texture);
if (got_gl_error ("glGenTextures"))
goto HANDLE_ERROR_LOCKED;
for (i = 0; i < ctx->eglglesctx.n_textures + 1; i++) {
glBindTexture (GL_TEXTURE_2D, ctx->eglglesctx.texture[i]);
for (i = 0; i < ctx->n_textures + 1; i++) {
glBindTexture (GL_TEXTURE_2D, ctx->texture[i]);
if (got_gl_error ("glBindTexture"))
goto HANDLE_ERROR;
@ -852,117 +676,20 @@ gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx,
return TRUE;
/* Errors */
HANDLE_EGL_ERROR_LOCKED:
GST_ERROR_OBJECT (ctx->element, "EGL call returned error %x", eglGetError ());
HANDLE_ERROR_LOCKED:
HANDLE_ERROR:
GST_ERROR_OBJECT (ctx->element, "Couldn't setup EGL surface");
return FALSE;
}
gboolean
gst_egl_adaptation_init_egl_display (GstEglAdaptationContext * ctx)
{
GstMessage *msg;
EGLDisplay display;
GST_DEBUG_OBJECT (ctx->element, "Enter EGL initial configuration");
#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->eglglesctx.set_display) {
GstContext *context;
ctx->eglglesctx.display = gst_egl_display_ref (ctx->eglglesctx.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->eglglesctx.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->eglglesctx.display = gst_egl_display_new (display);
context = gst_context_new ();
gst_context_set_egl_display (context, ctx->eglglesctx.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->eglglesctx.display);
gst_element_set_context (GST_ELEMENT_CAST (ctx->element), context);
}
if (!eglInitialize (gst_egl_display_get (ctx->eglglesctx.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_choose_config (GstEglAdaptationContext * ctx)
{
EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
GLint egl_configs;
gint egl_configs;
if ((eglChooseConfig (gst_egl_display_get (ctx->eglglesctx.display),
eglglessink_RGBA8888_attribs,
&ctx->eglglesctx.config, 1, &egl_configs)) == EGL_FALSE) {
got_egl_error ("eglChooseConfig");
if (!_gst_egl_choose_config (ctx, FALSE, &egl_configs)) {
GST_ERROR_OBJECT (ctx->element, "eglChooseConfig failed");
goto HANDLE_EGL_ERROR;
goto HANDLE_ERROR;
}
if (egl_configs < 1) {
@ -971,23 +698,14 @@ gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx)
goto HANDLE_ERROR;
}
ctx->eglglesctx.eglcontext =
eglCreateContext (gst_egl_display_get (ctx->eglglesctx.display),
ctx->eglglesctx.config, EGL_NO_CONTEXT, con_attribs);
if (ctx->eglglesctx.eglcontext == EGL_NO_CONTEXT) {
if (!gst_egl_adaptation_create_egl_context (ctx)) {
GST_ERROR_OBJECT (ctx->element, "Error getting context, eglCreateContext");
goto HANDLE_EGL_ERROR;
goto HANDLE_ERROR;
}
GST_DEBUG_OBJECT (ctx->element, "EGL Context: %p",
ctx->eglglesctx.eglcontext);
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 choose an usable config");
return FALSE;
@ -1000,12 +718,14 @@ gst_egl_adaptation_context_new (GstElement * element)
ctx->element = gst_object_ref (element);
gst_egl_adaptation_init (ctx);
return ctx;
}
void
gst_egl_adaptation_context_free (GstEglAdaptationContext * ctx)
{
gst_egl_adaptation_deinit (ctx);
gst_object_unref (ctx->element);
g_free (ctx);
}

View file

@ -47,21 +47,50 @@
#ifndef __GST_EGL_ADAPTATION_H__
#define __GST_EGL_ADAPTATION_H__
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <gst/gst.h>
#include <gst/egl/egl.h>
#include <gst/video/gstvideopool.h>
#ifdef HAVE_IOS
#include <OpenGLES/ES2/gl.h>
#else
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
#define GST_EGLGLESSINK_EGL_MIN_VERSION 1
static const EGLint eglglessink_RGBA8888_attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
G_BEGIN_DECLS
typedef struct _GstEglAdaptationContext GstEglAdaptationContext;
typedef struct _GstEglGlesRenderContext GstEglGlesRenderContext;
typedef struct _GstEglGlesImageFmt GstEglGlesImageFmt;
#ifdef HAVE_IOS
typedef struct _GstEaglContext GstEaglContext;
#else
typedef struct _GstEglGlesRenderContext GstEglGlesRenderContext;
#endif
typedef struct _coord5
{
float x;
@ -71,62 +100,10 @@ typedef struct _coord5
float b; /* texpos y */
} coord5;
/*
* GstEglGlesRenderContext:
* @config: Current EGL config
* @eglcontext: Current EGL context
* @display: Current EGL display connection
* @window: Current EGL window asociated with the display connection
* @used_window: Last seen EGL window asociated with the display connection
* @surface: EGL surface the sink is rendering into
* @fragshader: Fragment shader
* @vertshader: Vertex shader
* @glslprogram: Compiled and linked GLSL program in use for rendering
* @texture Texture units in use
* @surface_width: Pixel width of the surface the sink is rendering into
* @surface_height: Pixel height of the surface the sink is rendering into
* @pixel_aspect_ratio: EGL display aspect ratio
* @egl_minor: EGL version (minor)
* @egl_major: EGL version (major)
* @n_textures: Texture units count
* @position_loc: Index of the position vertex attribute array
* @texpos_loc: Index of the textpos vertex attribute array
* @position_array: VBO position array
* @texpos_array: VBO texpos array
* @index_array: VBO index array
* @position_buffer: Position buffer object name
* @texpos_buffer: Texpos buffer object name
* @index_buffer: Index buffer object name
*
* This struct holds the sink's EGL/GLES rendering context.
*/
struct _GstEglGlesRenderContext
typedef struct
{
EGLConfig config;
EGLContext eglcontext;
GstEGLDisplay *display, *set_display;
EGLNativeWindowType window, used_window;
EGLSurface surface;
gboolean buffer_preserved;
GLuint fragshader[3]; /* frame, border, frame-custom */
GLuint vertshader[3]; /* frame, border, frame-custom */
GLuint glslprogram[3]; /* frame, border, frame-custom */
GLuint texture[4]; /* RGB/Y, U/UV, V, custom */
EGLint surface_width;
EGLint surface_height;
EGLint pixel_aspect_ratio;
EGLint egl_minor, egl_major;
gint n_textures;
/* shader vars */
GLuint position_loc[3]; /* frame, border, frame-custom */
GLuint texpos_loc[2]; /* frame, frame-custom */
GLuint tex_scale_loc[1][3]; /* [frame] RGB/Y, U/UV, V */
GLuint tex_loc[2][3]; /* [frame,frame-custom] RGB/Y, U/UV, V */
coord5 position_array[16]; /* 4 x Frame x-normal,y-normal, 4x Frame x-normal,y-flip, 4 x Border1, 4 x Border2 */
unsigned short index_array[4];
unsigned int position_buffer, index_buffer;
};
GLuint texture;
} GstEGLGLESImageData;
/*
* GstEglAdaptationContext:
@ -139,15 +116,59 @@ struct _GstEglGlesRenderContext
struct _GstEglAdaptationContext
{
GstElement *element;
GstEglGlesRenderContext eglglesctx;
#ifdef HAVE_IOS
GstEaglContext *eaglctx;
#else
GstEglGlesRenderContext *eglglesctx;
#endif
GstEGLDisplay *display, *set_display;
EGLNativeWindowType window, used_window;
GLuint fragshader[3]; /* frame, border, frame-custom */
GLuint vertshader[3]; /* frame, border, frame-custom */
GLuint glslprogram[3]; /* frame, border, frame-custom */
GLuint texture[4]; /* RGB/Y, U/UV, V, custom */
/* shader vars */
GLuint position_loc[3]; /* frame, border, frame-custom */
GLuint texpos_loc[2]; /* frame, frame-custom */
GLuint tex_scale_loc[1][3]; /* [frame] RGB/Y, U/UV, V */
GLuint tex_loc[2][3]; /* [frame,frame-custom] RGB/Y, U/UV, V */
coord5 position_array[16]; /* 4 x Frame x-normal,y-normal, 4x Frame x-normal,y-flip, 4 x Border1, 4 x Border2 */
unsigned short index_array[4];
unsigned int position_buffer, index_buffer;
gint n_textures;
EGLint surface_width;
EGLint surface_height;
EGLint pixel_aspect_ratio_n;
EGLint pixel_aspect_ratio_d;
gboolean have_vbo;
gboolean have_texture;
gboolean have_surface;
gboolean buffer_preserved;
};
GstEglAdaptationContext * gst_egl_adaptation_context_new (GstElement * element);
void gst_egl_adaptation_context_free (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_init (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx);
gboolean gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx);
gboolean
gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx);
/* platform window */
gboolean gst_egl_adaptation_create_native_window (GstEglAdaptationContext
* ctx, gint width, gint height, gpointer * own_window_data);
void gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx, gpointer * own_window_data);
GstCaps *gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext * ctx);
gboolean gst_egl_adaptation_init_egl_display (GstEglAdaptationContext * ctx);
@ -155,13 +176,21 @@ gboolean gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx);
gboolean gst_egl_adaptation_init_egl_surface (GstEglAdaptationContext * ctx, GstVideoFormat format);
void gst_egl_adaptation_init_egl_exts (GstEglAdaptationContext * ctx);
gboolean gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext * ctx);
gboolean _gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only, gint * num_configs);
gboolean got_gl_error (const char *wtf);
gboolean got_egl_error (const char *wtf);
gboolean gst_egl_adaptation_context_make_current (GstEglAdaptationContext *
ctx, gboolean bind);
void gst_egl_adaptation_wipe_eglglesctx (GstEglAdaptationContext * ctx);
void gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window);
gboolean gst_egl_adaptation_context_make_current (GstEglAdaptationContext * ctx, gboolean bind);
void gst_egl_adaptation_cleanup (GstEglAdaptationContext * ctx);
GstBuffer *
gst_egl_adaptation_allocate_eglimage (GstEglAdaptationContext * ctx, GstAllocator * allocator,
GstVideoFormat format, gint width, gint height);
gboolean
gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx);
G_END_DECLS

View file

@ -0,0 +1,335 @@
/*
* GStreamer EGL/GLES Sink Adaptation for IOS
* Copyright (C) 2013 Collabora Ltd.
* @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.
*/
#import <OpenGLES/EAGL.h>
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
#include <OpenGLES/ES2/gl.h>
#include "gstegladaptation.h"
struct _GstEaglContext
{
EAGLContext *eagl_context;
GLuint framebuffer;
GLuint color_renderbuffer;
UIView *window;
UIView *used_window;
};
void
gst_egl_adaptation_init (GstEglAdaptationContext * ctx)
{
ctx->eaglctx = g_new0 (GstEaglContext, 1);
}
void
gst_egl_adaptation_deinit (GstEglAdaptationContext * ctx)
{
g_free (ctx->eaglctx);
}
gboolean
gst_egl_adaptation_init_display (GstEglAdaptationContext * ctx)
{
/* NOP - the display should be initialized by the application */
return TRUE;
}
void
gst_egl_adaptation_terminate_display (GstEglAdaptationContext * ctx)
{
/* NOP */
}
void
gst_egl_adaptation_bind_API (GstEglAdaptationContext * ctx)
{
/* NOP */
}
gboolean
gst_egl_adaptation_create_native_window (GstEglAdaptationContext * ctx, gint width, gint height, gpointer * own_window_data)
{
return FALSE;
}
void
gst_egl_adaptation_destroy_native_window (GstEglAdaptationContext * ctx, gpointer * own_window_data)
{
}
gboolean
gst_egl_adaptation_create_egl_context (GstEglAdaptationContext * ctx)
{
__block EAGLContext *context;
dispatch_sync(dispatch_get_main_queue(), ^{
EAGLContext *cur_ctx = [EAGLContext currentContext];
if (cur_ctx) {
context = cur_ctx;
} else {
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (context == nil) {
GST_ERROR_OBJECT (ctx->element, "Failed to create EAGL GLES2 context");
}
}
});
ctx->eaglctx->eagl_context = context;
if (context == nil)
return FALSE;
/* EAGL needs the context to be set here to allow surface creation */
return gst_egl_adaptation_make_current (ctx, TRUE);
}
gboolean
gst_egl_adaptation_make_current (GstEglAdaptationContext * ctx,
gboolean bind)
{
__block EAGLContext *ctx_to_set = nil;
if (bind && ctx->eaglctx->eagl_context) {
EAGLContext *cur_ctx = [EAGLContext currentContext];
if (cur_ctx == ctx->eaglctx->eagl_context) {
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 ([EAGLContext setCurrentContext: ctx->eaglctx->eagl_context] == NO) {
got_gl_error ("setCurrentContext");
GST_ERROR_OBJECT (ctx->element, "Couldn't bind context");
return FALSE;
}
ctx_to_set = ctx->eaglctx->eagl_context;
dispatch_sync(dispatch_get_main_queue(), ^{
[EAGLContext setCurrentContext: ctx_to_set];
});
} else {
GST_DEBUG_OBJECT (ctx->element, "Detaching context from thread %p",
g_thread_self ());
if ([EAGLContext setCurrentContext: nil] == NO) {
got_gl_error ("setCurrentContext");
GST_ERROR_OBJECT (ctx->element, "Couldn't unbind context");
return FALSE;
}
}
return TRUE;
}
gboolean
gst_egl_adaptation_create_surface (GstEglAdaptationContext * ctx)
{
__block GLuint framebuffer;
__block GLuint colorRenderbuffer;
__block GLint width;
__block GLint height;
__block GLuint depthRenderbuffer;
__block GLenum status;
__block CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[ctx->eaglctx->window layer];
dispatch_sync(dispatch_get_main_queue(), ^{
/* Allocate framebuffer */
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
/* Allocate color render buffer */
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[ctx->eaglctx->eagl_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, colorRenderbuffer);
/* Get renderbuffer width/height */
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
/* allocate depth render buffer */
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depthRenderbuffer);
});
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
/* check creation status */
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"failed to make complete framebuffer object %x", status);
return FALSE;
}
ctx->eaglctx->framebuffer = framebuffer;
ctx->eaglctx->color_renderbuffer = colorRenderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, ctx->eaglctx->color_renderbuffer);
return TRUE;
}
gboolean
gst_egl_choose_config (GstEglAdaptationContext * ctx, gboolean try_only, gint * num_configs)
{
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[ctx->eaglctx->window layer];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil];
[eaglLayer setOpaque:YES];
[eaglLayer setDrawableProperties:dict];
if (num_configs)
*num_configs = 1;
return TRUE;
}
void
gst_egl_adaptation_query_buffer_preserved (GstEglAdaptationContext * ctx)
{
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[ctx->eaglctx->window layer];
NSDictionary *dict = [eaglLayer drawableProperties];
ctx->buffer_preserved = FALSE;
if ([dict objectForKey: kEAGLDrawablePropertyRetainedBacking] != nil) {
NSNumber *n = [dict objectForKey: kEAGLDrawablePropertyRetainedBacking];
ctx->buffer_preserved = [n boolValue] != NO;
} else {
GST_DEBUG_OBJECT (ctx->element, "No information about buffer preserving in layer properties");
}
}
void
gst_egl_adaptation_query_par (GstEglAdaptationContext * ctx)
{
/* TODO how can we check this? */
ctx->pixel_aspect_ratio_n = 1;
ctx->pixel_aspect_ratio_d = 1;
}
gboolean
gst_egl_adaptation_update_surface_dimensions (GstEglAdaptationContext *
ctx)
{
GLint width;
GLint height;
/* Get renderbuffer width/height */
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_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;
}
void
gst_egl_adaptation_init_egl_exts (GstEglAdaptationContext * ctx)
{
const gchar *extensions = (const gchar *) glGetString(GL_EXTENSIONS);
NSString *extensionsString = NULL;
if (extensions) {
extensionsString= [NSString stringWithCString:extensions encoding: NSASCIIStringEncoding];
}
GST_DEBUG_OBJECT (ctx->element, "Available GL extensions: %s\n",
GST_STR_NULL ([extensionsString UTF8String]));
}
void
gst_egl_adaptation_destroy_surface (GstEglAdaptationContext * ctx)
{
if (ctx->eaglctx->framebuffer) {
glDeleteFramebuffers (1, &ctx->eaglctx->framebuffer);
ctx->eaglctx->framebuffer = 0;
ctx->have_surface = FALSE;
}
}
void
gst_egl_adaptation_destroy_context (GstEglAdaptationContext * ctx)
{
if (ctx->eaglctx->eagl_context) {
[ctx->eaglctx->eagl_context dealloc];
ctx->eaglctx->eagl_context = NULL;
}
}
gboolean
gst_egl_adaptation_swap_buffers (GstEglAdaptationContext * ctx)
{
[ctx->eaglctx->eagl_context presentRenderbuffer:GL_RENDERBUFFER];
return TRUE;
}
void
gst_egl_adaptation_set_window (GstEglAdaptationContext * ctx, guintptr window)
{
ctx->eaglctx->window = (UIView *) window;
}
void
gst_egl_adaptation_update_used_window (GstEglAdaptationContext * ctx)
{
ctx->eaglctx->used_window = ctx->eaglctx->window;
}
guintptr
gst_egl_adaptation_get_window (GstEglAdaptationContext * ctx)
{
return (guintptr) ctx->eaglctx->window;
}

View file

@ -0,0 +1,909 @@
/*
* 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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -52,12 +52,6 @@
#include <gst/base/gstdataqueue.h>
#include <gst/egl/egl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include "video_platform_wrapper.h"
#include "gstegladaptation.h"
G_BEGIN_DECLS
@ -116,7 +110,6 @@ struct _GstEglGlesSink
gboolean custom_format; /* If it's a single texture that is just copied */
GstBufferPool *pool;
GstEglGlesRenderContext *eglglesctx;
GstEglAdaptationContext *egl_context;
/* Runtime flags */