summaryrefslogtreecommitdiff
path: root/ext/eglgles/gstegladaptation.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/eglgles/gstegladaptation.c')
-rw-r--r--ext/eglgles/gstegladaptation.c982
1 files changed, 982 insertions, 0 deletions
diff --git a/ext/eglgles/gstegladaptation.c b/ext/eglgles/gstegladaptation.c
new file mode 100644
index 000000000..db6357d04
--- /dev/null
+++ b/ext/eglgles/gstegladaptation.c
@@ -0,0 +1,982 @@
+/*
+ * GStreamer EGL/GLES Sink Adaptation
+ * 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.
+ */
+
+#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)
+
+
+/* GLESv2 GLSL Shaders
+ *
+ * OpenGL ES Standard does not mandate YUV support. This is
+ * why most of these shaders deal with Packed/Planar YUV->RGB
+ * conversion.
+ */
+
+/* *INDENT-OFF* */
+/* Direct vertex copy */
+static const char *vert_COPY_prog = {
+ "attribute vec3 position;"
+ "attribute vec2 texpos;"
+ "varying vec2 opos;"
+ "void main(void)"
+ "{"
+ " opos = texpos;"
+ " gl_Position = vec4(position, 1.0);"
+ "}"
+};
+
+static const char *vert_COPY_prog_no_tex = {
+ "attribute vec3 position;"
+ "void main(void)"
+ "{"
+ " gl_Position = vec4(position, 1.0);"
+ "}"
+};
+
+/* Paint all black */
+static const char *frag_BLACK_prog = {
+ "precision mediump float;"
+ "void main(void)"
+ "{"
+ " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);"
+ "}"
+};
+
+/* Direct fragments copy */
+static const char *frag_COPY_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D tex;"
+ "void main(void)"
+ "{"
+ " vec4 t = texture2D(tex, opos);"
+ " gl_FragColor = vec4(t.rgb, 1.0);"
+ "}"
+};
+
+/* Channel reordering for XYZ <-> ZYX conversion */
+static const char *frag_REORDER_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D tex;"
+ "void main(void)"
+ "{"
+ " vec4 t = texture2D(tex, opos);"
+ " gl_FragColor = vec4(t.%c, t.%c, t.%c, 1.0);"
+ "}"
+};
+
+/* Packed YUV converters */
+
+/** AYUV to RGB conversion */
+static const char *frag_AYUV_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D tex;"
+ "const vec3 offset = vec3(-0.0625, -0.5, -0.5);"
+ "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);"
+ "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);"
+ "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);"
+ "void main(void) {"
+ " float r,g,b;"
+ " vec3 yuv;"
+ " yuv = texture2D(tex,opos).gba;"
+ " yuv += offset;"
+ " r = dot(yuv, rcoeff);"
+ " g = dot(yuv, gcoeff);"
+ " b = dot(yuv, bcoeff);"
+ " gl_FragColor=vec4(r,g,b,1.0);"
+ "}"
+};
+
+/** YUY2/YVYU/UYVY to RGB conversion */
+static const char *frag_YUY2_YVYU_UYVY_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D Ytex, UVtex;"
+ "const vec3 offset = vec3(-0.0625, -0.5, -0.5);"
+ "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);"
+ "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);"
+ "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);"
+ "void main(void) {"
+ " float r, g, b;"
+ " vec3 yuv;"
+ " yuv.x = texture2D(Ytex,opos).%c;"
+ " yuv.yz = texture2D(UVtex,opos).%c%c;"
+ " yuv += offset;"
+ " r = dot(yuv, rcoeff);"
+ " g = dot(yuv, gcoeff);"
+ " b = dot(yuv, bcoeff);"
+ " gl_FragColor=vec4(r,g,b,1.0);"
+ "}"
+};
+
+/* Planar YUV converters */
+
+/** YUV to RGB conversion */
+static const char *frag_PLANAR_YUV_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D Ytex,Utex,Vtex;"
+ "const vec3 offset = vec3(-0.0625, -0.5, -0.5);"
+ "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);"
+ "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);"
+ "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);"
+ "void main(void) {"
+ " float r,g,b;"
+ " vec3 yuv;"
+ " yuv.x=texture2D(Ytex,opos).r;"
+ " yuv.y=texture2D(Utex,opos).r;"
+ " yuv.z=texture2D(Vtex,opos).r;"
+ " yuv += offset;"
+ " r = dot(yuv, rcoeff);"
+ " g = dot(yuv, gcoeff);"
+ " b = dot(yuv, bcoeff);"
+ " gl_FragColor=vec4(r,g,b,1.0);"
+ "}"
+};
+
+/** NV12/NV21 to RGB conversion */
+static const char *frag_NV12_NV21_prog = {
+ "precision mediump float;"
+ "varying vec2 opos;"
+ "uniform sampler2D Ytex,UVtex;"
+ "const vec3 offset = vec3(-0.0625, -0.5, -0.5);"
+ "const vec3 rcoeff = vec3(1.164, 0.000, 1.596);"
+ "const vec3 gcoeff = vec3(1.164,-0.391,-0.813);"
+ "const vec3 bcoeff = vec3(1.164, 2.018, 0.000);"
+ "void main(void) {"
+ " float r,g,b;"
+ " vec3 yuv;"
+ " yuv.x=texture2D(Ytex,opos).r;"
+ " yuv.yz=texture2D(UVtex,opos).%c%c;"
+ " yuv += offset;"
+ " r = dot(yuv, rcoeff);"
+ " g = dot(yuv, gcoeff);"
+ " b = dot(yuv, bcoeff);"
+ " gl_FragColor=vec4(r,g,b,1.0);"
+ "}"
+};
+/* *INDENT-ON* */
+
+/* will probably move elsewhere */
+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
+};
+
+static const EGLint eglglessink_RGB888_attribs[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+static const EGLint eglglessink_RGB565_attribs[] = {
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 6,
+ EGL_BLUE_SIZE, 5,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+
+static gboolean
+create_shader_program (GstEglAdaptationContext * ctx, GLuint * prog,
+ GLuint * vert, GLuint * frag, const gchar * vert_text,
+ const gchar * frag_text)
+{
+ GLint test;
+ GLchar *info_log;
+
+ /* Build shader program for video texture rendering */
+ *vert = glCreateShader (GL_VERTEX_SHADER);
+ GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", vert_text, *vert);
+ glShaderSource (*vert, 1, &vert_text, NULL);
+ if (got_gl_error ("glShaderSource vertex"))
+ goto HANDLE_ERROR;
+
+ glCompileShader (*vert);
+ if (got_gl_error ("glCompileShader vertex"))
+ goto HANDLE_ERROR;
+
+ glGetShaderiv (*vert, GL_COMPILE_STATUS, &test);
+ if (test != GL_FALSE)
+ GST_DEBUG_OBJECT (ctx->element, "Successfully compiled vertex shader");
+ else {
+ GST_ERROR_OBJECT (ctx->element, "Couldn't compile vertex shader");
+ glGetShaderiv (*vert, GL_INFO_LOG_LENGTH, &test);
+ info_log = g_new0 (GLchar, test);
+ glGetShaderInfoLog (*vert, test, NULL, info_log);
+ GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log);
+ g_free (info_log);
+ goto HANDLE_ERROR;
+ }
+
+ *frag = glCreateShader (GL_FRAGMENT_SHADER);
+ GST_DEBUG_OBJECT (ctx->element, "Sending %s to handle %d", frag_text, *frag);
+ glShaderSource (*frag, 1, &frag_text, NULL);
+ if (got_gl_error ("glShaderSource fragment"))
+ goto HANDLE_ERROR;
+
+ glCompileShader (*frag);
+ if (got_gl_error ("glCompileShader fragment"))
+ goto HANDLE_ERROR;
+
+ glGetShaderiv (*frag, GL_COMPILE_STATUS, &test);
+ if (test != GL_FALSE)
+ GST_DEBUG_OBJECT (ctx->element, "Successfully compiled fragment shader");
+ else {
+ GST_ERROR_OBJECT (ctx->element, "Couldn't compile fragment shader");
+ glGetShaderiv (*frag, GL_INFO_LOG_LENGTH, &test);
+ info_log = g_new0 (GLchar, test);
+ glGetShaderInfoLog (*frag, test, NULL, info_log);
+ GST_INFO_OBJECT (ctx->element, "Compilation info log:\n%s", info_log);
+ g_free (info_log);
+ goto HANDLE_ERROR;
+ }
+
+ *prog = glCreateProgram ();
+ if (got_gl_error ("glCreateProgram"))
+ goto HANDLE_ERROR;
+ glAttachShader (*prog, *vert);
+ if (got_gl_error ("glAttachShader vertices"))
+ goto HANDLE_ERROR;
+ glAttachShader (*prog, *frag);
+ if (got_gl_error ("glAttachShader fragments"))
+ goto HANDLE_ERROR;
+ glLinkProgram (*prog);
+ glGetProgramiv (*prog, GL_LINK_STATUS, &test);
+ if (test != GL_FALSE) {
+ GST_DEBUG_OBJECT (ctx->element, "GLES: Successfully linked program");
+ } else {
+ GST_ERROR_OBJECT (ctx->element, "Couldn't link program");
+ goto HANDLE_ERROR;
+ }
+
+ return TRUE;
+
+HANDLE_ERROR:
+ {
+ if (*frag && *prog)
+ glDetachShader (*prog, *frag);
+ if (*vert && *prog)
+ glDetachShader (*prog, *vert);
+ if (*prog)
+ glDeleteProgram (*prog);
+ if (*frag)
+ glDeleteShader (*frag);
+ if (*vert)
+ glDeleteShader (*vert);
+ *prog = 0;
+ *frag = 0;
+ *vert = 0;
+
+ return FALSE;
+ }
+}
+
+
+gboolean
+gst_egl_adaptation_init_display (GstEglAdaptationContext * ctx)
+{
+ 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
+
+ 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 = display;
+
+ if (!eglInitialize (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;
+}
+
+GstEglAdaptationContext *
+gst_egl_adaptation_context_new (GstElement * element)
+{
+ GstEglAdaptationContext *ctx = g_new0 (GstEglAdaptationContext, 1);
+
+ ctx->element = gst_object_ref (element);
+
+ return ctx;
+}
+
+void
+gst_egl_adaptation_context_free (GstEglAdaptationContext * ctx)
+{
+ gst_object_unref (ctx->element);
+ g_free (ctx);
+}
+
+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;
+}
+
+gboolean
+gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx)
+{
+ EGLint con_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ GLint egl_configs;
+
+ if ((eglChooseConfig (ctx->eglglesctx.display,
+ eglglessink_RGBA8888_attribs,
+ &ctx->eglglesctx.config, 1, &egl_configs)) == EGL_FALSE) {
+ got_egl_error ("eglChooseConfig");
+ GST_ERROR_OBJECT (ctx->element, "eglChooseConfig failed");
+ goto HANDLE_EGL_ERROR;
+ }
+
+ if (egl_configs < 1) {
+ GST_ERROR_OBJECT (ctx->element,
+ "Could not find matching framebuffer config");
+ goto HANDLE_ERROR;
+ }
+
+ ctx->eglglesctx.eglcontext =
+ eglCreateContext (ctx->eglglesctx.display,
+ ctx->eglglesctx.config, EGL_NO_CONTEXT, con_attribs);
+
+ if (ctx->eglglesctx.eglcontext == EGL_NO_CONTEXT) {
+ GST_ERROR_OBJECT (ctx->element, "Error getting context, eglCreateContext");
+ goto HANDLE_EGL_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;
+}
+
+gint gst_egl_adaptation_context_fill_supported_fbuffer_configs
+ (GstEglAdaptationContext * ctx, GstCaps ** ret_caps)
+{
+ gboolean ret = FALSE;
+ EGLint cfg_number;
+ GstCaps *caps;
+
+ GST_DEBUG_OBJECT (ctx->element,
+ "Building initial list of wanted eglattribs per format");
+
+ /* Init supported format/caps list */
+ caps = gst_caps_new_empty ();
+
+ if (eglChooseConfig (ctx->eglglesctx.display,
+ eglglessink_RGBA8888_attribs, NULL, 1, &cfg_number) != EGL_FALSE) {
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBA));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRA));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ARGB));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_ABGR));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGBx));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGRx));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xRGB));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_xBGR));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_AYUV));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y444));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_I420));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YV12));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV12));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_NV21));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YUY2));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_YVYU));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_UYVY));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y42B));
+ gst_caps_append (caps,
+ gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y41B));
+ ret = TRUE;
+ } else {
+ GST_INFO_OBJECT (ctx->element,
+ "EGL display doesn't support RGBA8888 config");
+ }
+
+ GST_OBJECT_LOCK (ctx->element);
+ gst_caps_replace (ret_caps, caps);
+ GST_OBJECT_UNLOCK (ctx->element);
+ gst_caps_unref (caps);
+
+ return ret;
+}
+
+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 (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 (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;
+}
+
+void
+gst_egl_adaptation_context_cleanup (GstEglAdaptationContext * ctx)
+{
+ gint i;
+
+ glUseProgram (0);
+
+ if (ctx->have_vbo) {
+ glDeleteBuffers (1, &ctx->eglglesctx.position_buffer);
+ glDeleteBuffers (1, &ctx->eglglesctx.index_buffer);
+ ctx->have_vbo = FALSE;
+ }
+
+ if (ctx->have_texture) {
+ glDeleteTextures (ctx->eglglesctx.n_textures,
+ ctx->eglglesctx.texture);
+ ctx->have_texture = FALSE;
+ ctx->eglglesctx.n_textures = 0;
+ }
+
+ for (i = 0; i < 2; 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;
+ }
+ }
+
+ gst_egl_adaptation_context_make_current (ctx, FALSE);
+
+ if (ctx->eglglesctx.surface) {
+ eglDestroySurface (ctx->eglglesctx.display,
+ ctx->eglglesctx.surface);
+ ctx->eglglesctx.surface = NULL;
+ ctx->have_surface = FALSE;
+ }
+
+ if (ctx->eglglesctx.eglcontext) {
+ eglDestroyContext (ctx->eglglesctx.display,
+ ctx->eglglesctx.eglcontext);
+ ctx->eglglesctx.eglcontext = NULL;
+ }
+}
+
+/* XXX: Lock eglgles context? */
+gboolean
+gst_egl_adaptation_context_update_surface_dimensions (GstEglAdaptationContext *
+ ctx)
+{
+ gint width, height;
+
+ /* Save surface dims */
+ eglQuerySurface (ctx->eglglesctx.display,
+ ctx->eglglesctx.surface, EGL_WIDTH, &width);
+ eglQuerySurface (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_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 (ctx->eglglesctx.display,
+ ctx->eglglesctx.config, ctx->eglglesctx.used_window,
+ NULL);
+
+ if (ctx->eglglesctx.surface == EGL_NO_SURFACE) {
+ got_egl_error ("eglCreateWindowSurface");
+ GST_ERROR_OBJECT (ctx->element, "Can't create surface");
+ goto HANDLE_EGL_ERROR_LOCKED;
+ }
+
+ ctx->eglglesctx.buffer_preserved = FALSE;
+ if (eglQuerySurface (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");
+ }
+
+ if (!gst_egl_adaptation_context_make_current (ctx, TRUE))
+ goto HANDLE_EGL_ERROR_LOCKED;
+
+ /* 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->eglglesctx.pixel_aspect_ratio = EGL_DISPLAY_SCALING;
+ } else {
+ eglQuerySurface (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;
+ }
+ }
+
+ /* Save surface dims */
+ gst_egl_adaptation_context_update_surface_dimensions (ctx);
+
+ /* We have a surface! */
+ ctx->have_surface = TRUE;
+
+ /* Init vertex and fragment GLSL shaders.
+ * Note: Shader compiler support is optional but we currently rely on it.
+ */
+
+ glGetBooleanv (GL_SHADER_COMPILER, &ret);
+ if (ret == GL_FALSE) {
+ GST_ERROR_OBJECT (ctx->element, "Shader compiler support is unavailable!");
+ goto HANDLE_ERROR;
+ }
+
+ /* Build shader program for video texture rendering */
+
+ switch (format) {
+ case GST_VIDEO_FORMAT_AYUV:
+ frag_prog = (gchar *) frag_AYUV_prog;
+ free_frag_prog = FALSE;
+ ctx->eglglesctx.n_textures = 1;
+ texnames[0] = "tex";
+ break;
+ case GST_VIDEO_FORMAT_Y444:
+ case GST_VIDEO_FORMAT_I420:
+ case GST_VIDEO_FORMAT_YV12:
+ case GST_VIDEO_FORMAT_Y42B:
+ case GST_VIDEO_FORMAT_Y41B:
+ frag_prog = (gchar *) frag_PLANAR_YUV_prog;
+ free_frag_prog = FALSE;
+ ctx->eglglesctx.n_textures = 3;
+ texnames[0] = "Ytex";
+ texnames[1] = "Utex";
+ texnames[2] = "Vtex";
+ break;
+ case GST_VIDEO_FORMAT_YUY2:
+ frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'r', 'g', 'a');
+ free_frag_prog = TRUE;
+ ctx->eglglesctx.n_textures = 2;
+ texnames[0] = "Ytex";
+ texnames[1] = "UVtex";
+ break;
+ case GST_VIDEO_FORMAT_YVYU:
+ frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'r', 'a', 'g');
+ free_frag_prog = TRUE;
+ ctx->eglglesctx.n_textures = 2;
+ texnames[0] = "Ytex";
+ texnames[1] = "UVtex";
+ break;
+ case GST_VIDEO_FORMAT_UYVY:
+ frag_prog = g_strdup_printf (frag_YUY2_YVYU_UYVY_prog, 'a', 'r', 'b');
+ free_frag_prog = TRUE;
+ ctx->eglglesctx.n_textures = 2;
+ texnames[0] = "Ytex";
+ texnames[1] = "UVtex";
+ break;
+ 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;
+ 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;
+ texnames[0] = "Ytex";
+ texnames[1] = "UVtex";
+ break;
+ case GST_VIDEO_FORMAT_BGR:
+ case GST_VIDEO_FORMAT_BGRx:
+ 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;
+ 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;
+ 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;
+ texnames[0] = "tex";
+ break;
+ case GST_VIDEO_FORMAT_RGB:
+ case GST_VIDEO_FORMAT_RGBx:
+ case GST_VIDEO_FORMAT_RGBA:
+ case GST_VIDEO_FORMAT_RGB16:
+ frag_prog = (gchar *) frag_COPY_prog;
+ free_frag_prog = FALSE;
+ ctx->eglglesctx.n_textures = 1;
+ texnames[0] = "tex";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (!create_shader_program (ctx,
+ &ctx->eglglesctx.glslprogram[0],
+ &ctx->eglglesctx.vertshader[0],
+ &ctx->eglglesctx.fragshader[0], vert_COPY_prog, frag_prog)) {
+ if (free_frag_prog)
+ g_free (frag_prog);
+ frag_prog = NULL;
+ goto HANDLE_ERROR;
+ }
+ if (free_frag_prog)
+ 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");
+
+ glEnableVertexAttribArray (ctx->eglglesctx.position_loc[0]);
+ if (got_gl_error ("glEnableVertexAttribArray"))
+ goto HANDLE_ERROR;
+
+ glEnableVertexAttribArray (ctx->eglglesctx.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]);
+ }
+
+ if (!ctx->eglglesctx.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))
+ goto HANDLE_ERROR;
+
+ ctx->eglglesctx.position_loc[1] =
+ glGetAttribLocation (ctx->eglglesctx.glslprogram[1],
+ "position");
+
+ glEnableVertexAttribArray (ctx->eglglesctx.position_loc[1]);
+ if (got_gl_error ("glEnableVertexAttribArray"))
+ goto HANDLE_ERROR;
+ }
+
+ /* Generate textures */
+ if (!ctx->have_texture) {
+ GST_INFO_OBJECT (ctx->element, "Performing initial texture setup");
+
+ glGenTextures (ctx->eglglesctx.n_textures,
+ ctx->eglglesctx.texture);
+ if (got_gl_error ("glGenTextures"))
+ goto HANDLE_ERROR_LOCKED;
+
+ for (i = 0; i < ctx->eglglesctx.n_textures; i++) {
+ glBindTexture (GL_TEXTURE_2D, ctx->eglglesctx.texture[i]);
+ if (got_gl_error ("glBindTexture"))
+ goto HANDLE_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 HANDLE_ERROR_LOCKED;
+ }
+
+ ctx->have_texture = TRUE;
+ }
+
+ glUseProgram (0);
+
+ 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
+got_gl_error (const char *wtf)
+{
+ GLuint error = GL_NO_ERROR;
+
+ if ((error = glGetError ()) != GL_NO_ERROR) {
+ GST_CAT_ERROR (GST_CAT_DEFAULT, "GL ERROR: %s returned 0x%04x", wtf, error);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void
+gst_egl_adaptation_context_terminate_display (GstEglAdaptationContext * ctx)
+{
+ if (ctx->eglglesctx.display) {
+ eglTerminate (ctx->eglglesctx.display);
+ ctx->eglglesctx.display = NULL;
+ }
+}
+
+void
+gst_egl_adaptation_context_bind_API (GstEglAdaptationContext * ctx)
+{
+ eglBindAPI (EGL_OPENGL_ES_API);
+}
+
+gboolean
+gst_egl_adaptation_context_swap_buffers (GstEglAdaptationContext * ctx)
+{
+ gboolean ret =
+ eglSwapBuffers (ctx->eglglesctx.display, ctx->eglglesctx.surface);
+ if (ret == EGL_FALSE) {
+ got_egl_error ("eglSwapBuffers");
+ }
+ return ret;
+}
+
+/* Prints avilable 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_context_init_egl_exts (GstEglAdaptationContext * ctx)
+{
+ const char *eglexts;
+ unsigned const char *glexts;
+
+ eglexts = eglQueryString (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;
+}