mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
707 lines
21 KiB
C
707 lines
21 KiB
C
/*
|
|
* GStreamer EGL/GLES Sink Adaptation
|
|
* 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 <gst/video/video.h>
|
|
#include <string.h>
|
|
|
|
#define GST_CAT_DEFAULT egladaption_debug
|
|
GST_DEBUG_CATEGORY (egladaption_debug);
|
|
|
|
/* 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 with stride-scaling */
|
|
static const char *frag_COPY_prog = {
|
|
"precision mediump float;"
|
|
"varying vec2 opos;"
|
|
"uniform sampler2D tex;"
|
|
"uniform vec2 tex_scale0;"
|
|
"uniform vec2 tex_scale1;"
|
|
"uniform vec2 tex_scale2;"
|
|
"void main(void)"
|
|
"{"
|
|
" vec4 t = texture2D(tex, opos / tex_scale0);"
|
|
" 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;"
|
|
"uniform vec2 tex_scale0;"
|
|
"uniform vec2 tex_scale1;"
|
|
"uniform vec2 tex_scale2;"
|
|
"void main(void)"
|
|
"{"
|
|
" vec4 t = texture2D(tex, opos / tex_scale0);"
|
|
" 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;"
|
|
"uniform vec2 tex_scale0;"
|
|
"uniform vec2 tex_scale1;"
|
|
"uniform vec2 tex_scale2;"
|
|
"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 / tex_scale0).gba;"
|
|
" 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;"
|
|
"uniform vec2 tex_scale0;"
|
|
"uniform vec2 tex_scale1;"
|
|
"uniform vec2 tex_scale2;"
|
|
"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 / tex_scale0).r;"
|
|
" yuv.y=texture2D(Utex,opos / tex_scale1).r;"
|
|
" yuv.z=texture2D(Vtex,opos / tex_scale2).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;"
|
|
"uniform vec2 tex_scale0;"
|
|
"uniform vec2 tex_scale1;"
|
|
"uniform vec2 tex_scale2;"
|
|
"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 / tex_scale0).r;"
|
|
" yuv.yz=texture2D(UVtex,opos / tex_scale1).%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* */
|
|
|
|
void
|
|
gst_egl_adaption_init (void)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (egladaption_debug, "egladaption", 0,
|
|
"EGL adaption layer");
|
|
}
|
|
|
|
static GstCaps *
|
|
_gst_video_format_new_template_caps (GstVideoFormat format)
|
|
{
|
|
return gst_caps_new_simple ("video/x-raw",
|
|
"format", G_TYPE_STRING, gst_video_format_to_string (format),
|
|
"width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
|
|
"framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
|
|
}
|
|
|
|
GstCaps *
|
|
gst_egl_adaptation_fill_supported_fbuffer_configs (GstEglAdaptationContext *
|
|
ctx)
|
|
{
|
|
GstCaps *caps = NULL, *copy1, *copy2;
|
|
guint i, n;
|
|
|
|
GST_DEBUG_OBJECT (ctx->element,
|
|
"Building initial list of wanted eglattribs per format");
|
|
|
|
/* Init supported format/caps list */
|
|
if (_gst_egl_choose_config (ctx, TRUE, NULL)) {
|
|
|
|
caps = gst_caps_new_empty ();
|
|
|
|
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_RGB));
|
|
gst_caps_append (caps,
|
|
_gst_video_format_new_template_caps (GST_VIDEO_FORMAT_BGR));
|
|
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_Y42B));
|
|
gst_caps_append (caps,
|
|
_gst_video_format_new_template_caps (GST_VIDEO_FORMAT_Y41B));
|
|
gst_caps_append (caps,
|
|
_gst_video_format_new_template_caps (GST_VIDEO_FORMAT_RGB16));
|
|
|
|
copy1 = gst_caps_copy (caps);
|
|
copy2 = gst_caps_copy (caps);
|
|
|
|
#ifndef HAVE_IOS
|
|
n = gst_caps_get_size (caps);
|
|
for (i = 0; i < n; i++) {
|
|
GstCapsFeatures *features =
|
|
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, NULL);
|
|
gst_caps_set_features (caps, i, features);
|
|
}
|
|
#endif
|
|
|
|
n = gst_caps_get_size (copy1);
|
|
for (i = 0; i < n; i++) {
|
|
GstCapsFeatures *features =
|
|
gst_caps_features_new
|
|
(GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, NULL);
|
|
gst_caps_set_features (copy1, i, features);
|
|
}
|
|
|
|
gst_caps_append (caps, copy1);
|
|
gst_caps_append (caps, copy2);
|
|
} else {
|
|
GST_INFO_OBJECT (ctx->element,
|
|
"EGL display doesn't support RGBA8888 config");
|
|
}
|
|
|
|
return caps;
|
|
}
|
|
|
|
void
|
|
gst_egl_adaptation_cleanup (GstEglAdaptationContext * ctx)
|
|
{
|
|
gint i;
|
|
|
|
glUseProgram (0);
|
|
|
|
if (ctx->have_vbo) {
|
|
glDeleteBuffers (1, &ctx->position_buffer);
|
|
glDeleteBuffers (1, &ctx->index_buffer);
|
|
ctx->have_vbo = FALSE;
|
|
}
|
|
|
|
if (ctx->have_texture) {
|
|
glDeleteTextures (ctx->n_textures, ctx->texture);
|
|
ctx->have_texture = FALSE;
|
|
ctx->n_textures = 0;
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
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);
|
|
|
|
gst_egl_adaptation_destroy_surface (ctx);
|
|
gst_egl_adaptation_destroy_context (ctx);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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_surface (GstEglAdaptationContext * ctx,
|
|
GstVideoFormat format)
|
|
{
|
|
GLboolean ret;
|
|
const gchar *texnames[3] = { NULL, };
|
|
gchar *frag_prog = NULL;
|
|
gboolean free_frag_prog = FALSE;
|
|
gint i;
|
|
|
|
GST_DEBUG_OBJECT (ctx->element, "Enter EGL surface setup");
|
|
|
|
if (!gst_egl_adaptation_create_surface (ctx)) {
|
|
GST_ERROR_OBJECT (ctx->element, "Can't create surface");
|
|
goto HANDLE_ERROR_LOCKED;
|
|
}
|
|
|
|
if (!gst_egl_adaptation_context_make_current (ctx, TRUE))
|
|
goto HANDLE_ERROR_LOCKED;
|
|
|
|
gst_egl_adaptation_query_buffer_preserved (ctx);
|
|
|
|
gst_egl_adaptation_init_exts (ctx);
|
|
|
|
/* Save surface dims */
|
|
gst_egl_adaptation_update_surface_dimensions (ctx);
|
|
|
|
/* 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.
|
|
*/
|
|
gst_egl_adaptation_query_par (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->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->n_textures = 3;
|
|
texnames[0] = "Ytex";
|
|
texnames[1] = "Utex";
|
|
texnames[2] = "Vtex";
|
|
break;
|
|
case GST_VIDEO_FORMAT_NV12:
|
|
frag_prog = g_strdup_printf (frag_NV12_NV21_prog, 'r', 'a');
|
|
free_frag_prog = TRUE;
|
|
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->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->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->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->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->n_textures = 1;
|
|
texnames[0] = "tex";
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
if (!create_shader_program (ctx,
|
|
&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;
|
|
goto HANDLE_ERROR;
|
|
}
|
|
if (free_frag_prog)
|
|
g_free (frag_prog);
|
|
frag_prog = NULL;
|
|
|
|
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->position_loc[0]);
|
|
if (got_gl_error ("glEnableVertexAttribArray"))
|
|
goto HANDLE_ERROR;
|
|
|
|
glEnableVertexAttribArray (ctx->texpos_loc[0]);
|
|
if (got_gl_error ("glEnableVertexAttribArray"))
|
|
goto HANDLE_ERROR;
|
|
|
|
for (i = 0; i < ctx->n_textures; i++) {
|
|
ctx->tex_loc[0][i] =
|
|
glGetUniformLocation (ctx->glslprogram[0], texnames[i]);
|
|
}
|
|
|
|
if (!ctx->buffer_preserved) {
|
|
/* Build shader program for black borders */
|
|
if (!create_shader_program (ctx,
|
|
&ctx->glslprogram[1],
|
|
&ctx->vertshader[1],
|
|
&ctx->fragshader[1], vert_COPY_prog_no_tex, frag_BLACK_prog))
|
|
goto HANDLE_ERROR;
|
|
|
|
ctx->position_loc[1] =
|
|
glGetAttribLocation (ctx->glslprogram[1], "position");
|
|
|
|
glEnableVertexAttribArray (ctx->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->n_textures, ctx->texture);
|
|
if (got_gl_error ("glGenTextures"))
|
|
goto HANDLE_ERROR_LOCKED;
|
|
|
|
for (i = 0; i < ctx->n_textures; i++) {
|
|
glBindTexture (GL_TEXTURE_2D, ctx->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_ERROR_LOCKED:
|
|
HANDLE_ERROR:
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't setup EGL surface");
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_egl_adaptation_choose_config (GstEglAdaptationContext * ctx)
|
|
{
|
|
gint egl_configs;
|
|
|
|
if (!_gst_egl_choose_config (ctx, FALSE, &egl_configs)) {
|
|
GST_ERROR_OBJECT (ctx->element, "eglChooseConfig failed");
|
|
goto HANDLE_ERROR;
|
|
}
|
|
|
|
if (egl_configs < 1) {
|
|
GST_ERROR_OBJECT (ctx->element,
|
|
"Could not find matching framebuffer config");
|
|
goto HANDLE_ERROR;
|
|
}
|
|
|
|
if (!gst_egl_adaptation_create_egl_context (ctx)) {
|
|
GST_ERROR_OBJECT (ctx->element, "Error getting context, eglCreateContext");
|
|
goto HANDLE_ERROR;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
/* Errors */
|
|
HANDLE_ERROR:
|
|
GST_ERROR_OBJECT (ctx->element, "Couldn't choose an usable config");
|
|
return FALSE;
|
|
}
|
|
|
|
GstEglAdaptationContext *
|
|
gst_egl_adaptation_context_new (GstElement * element)
|
|
{
|
|
GstEglAdaptationContext *ctx = g_new0 (GstEglAdaptationContext, 1);
|
|
|
|
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);
|
|
}
|