mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 06:46:38 +00:00
b85ffef956
glmixer can be seen as a glfilter except it handles N requested sink pads. Each sink pad and the src pad are video/x-raw-gl. glmixer is responsible for managing different framerates from inputs. It uses OpenGL context sharing. It means that each input is in its own OpenGL context shared together and shared with the OpenGL context of the ouput gl chain. Also add a glmosaic which is an example of implementation of glmixer. For now glmosaic is a cube but it will be fixed in the next commits. For now the glmixer has some weird behaviours in some configurations but it will be improved in the next commits. The autotools builds is temporarly broken since those changes have been made on win32.
3458 lines
114 KiB
C
3458 lines
114 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
|
|
* Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
|
|
* Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <gst/video/gstvideosink.h>
|
|
#include "gstgldisplay.h"
|
|
|
|
#ifndef GLEW_VERSION_MAJOR
|
|
#define GLEW_VERSION_MAJOR 4
|
|
#endif
|
|
|
|
#ifndef GLEW_VERSION_MINOR
|
|
#define GLEW_VERSION_MINOR 0
|
|
#endif
|
|
|
|
/*
|
|
* gst-launch-0.10 --gst-debug=gldisplay:N pipeline
|
|
* N=1: errors
|
|
* N=2: errors warnings
|
|
* N=3: errors warnings infos
|
|
* N=4: errors warnings infos
|
|
* N=5: errors warnings infos logs
|
|
*/
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
|
|
#define GST_CAT_DEFAULT gst_gl_display_debug
|
|
|
|
#define DEBUG_INIT(bla) \
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display");
|
|
|
|
GST_BOILERPLATE_FULL (GstGLDisplay, gst_gl_display, GObject, G_TYPE_OBJECT,
|
|
DEBUG_INIT);
|
|
static void gst_gl_display_finalize (GObject * object);
|
|
|
|
/* Called in the gl thread, protected by lock and unlock */
|
|
gpointer gst_gl_display_thread_create_context (GstGLDisplay * display);
|
|
void gst_gl_display_thread_destroy_context (GstGLDisplay * display);
|
|
void gst_gl_display_thread_run_generic (GstGLDisplay * display);
|
|
void gst_gl_display_thread_gen_texture (GstGLDisplay * display);
|
|
#ifdef OPENGL_ES2
|
|
void gst_gl_display_thread_init_redisplay (GstGLDisplay * display);
|
|
#endif
|
|
void gst_gl_display_thread_init_upload (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_upload (GstGLDisplay * display);
|
|
void gst_gl_display_thread_init_download (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_download (GstGLDisplay * display);
|
|
void gst_gl_display_thread_gen_fbo (GstGLDisplay * display);
|
|
void gst_gl_display_thread_use_fbo (GstGLDisplay * display);
|
|
void gst_gl_display_thread_use_fbo_v2 (GstGLDisplay * display);
|
|
void gst_gl_display_thread_del_fbo (GstGLDisplay * display);
|
|
void gst_gl_display_thread_gen_shader (GstGLDisplay * display);
|
|
void gst_gl_display_thread_del_shader (GstGLDisplay * display);
|
|
|
|
/* private methods */
|
|
void gst_gl_display_lock (GstGLDisplay * display);
|
|
void gst_gl_display_unlock (GstGLDisplay * display);
|
|
void gst_gl_display_on_resize (GstGLDisplay * display, gint width, gint height);
|
|
void gst_gl_display_on_draw (GstGLDisplay * display);
|
|
void gst_gl_display_on_close (GstGLDisplay * display);
|
|
void gst_gl_display_glgen_texture (GstGLDisplay * display, GLuint * pTexture,
|
|
GLint width, GLint height);
|
|
void gst_gl_display_gldel_texture (GstGLDisplay * display, GLuint * pTexture,
|
|
GLint width, GLint height);
|
|
gboolean gst_gl_display_texture_pool_func_clean (gpointer key, gpointer value,
|
|
gpointer data);
|
|
void gst_gl_display_check_framebuffer_status (void);
|
|
|
|
/* To not make gst_gl_display_thread_do_upload
|
|
* and gst_gl_display_thread_do_download too big */
|
|
void gst_gl_display_thread_init_upload_fbo (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_upload_make (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_upload_fill (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_upload_draw (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_download_draw_rgb (GstGLDisplay * display);
|
|
void gst_gl_display_thread_do_download_draw_yuv (GstGLDisplay * display);
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//---------------------- For klass GstGLDisplay ---------------
|
|
//------------------------------------------------------------
|
|
|
|
static void
|
|
gst_gl_display_base_init (gpointer g_class)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gst_gl_display_class_init (GstGLDisplayClass * klass)
|
|
{
|
|
G_OBJECT_CLASS (klass)->finalize = gst_gl_display_finalize;
|
|
}
|
|
|
|
|
|
static void
|
|
gst_gl_display_init (GstGLDisplay * display, GstGLDisplayClass * klass)
|
|
{
|
|
//thread safe
|
|
display->mutex = g_mutex_new ();
|
|
|
|
//gl context
|
|
display->gl_thread = NULL;
|
|
display->gl_window = NULL;
|
|
display->isAlive = TRUE;
|
|
display->texture_pool = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
|
|
//conditions
|
|
display->cond_create_context = g_cond_new ();
|
|
display->cond_destroy_context = g_cond_new ();
|
|
|
|
//action redisplay
|
|
display->redisplay_texture = 0;
|
|
display->redisplay_texture_width = 0;
|
|
display->redisplay_texture_height = 0;
|
|
display->keep_aspect_ratio = FALSE;
|
|
#ifdef OPENGL_ES2
|
|
display->redisplay_shader = NULL;
|
|
display->redisplay_attr_position_loc = 0;
|
|
display->redisplay_attr_texture_loc = 0;
|
|
#endif
|
|
|
|
//action gen and del texture
|
|
display->gen_texture = 0;
|
|
display->gen_texture_width = 0;
|
|
display->gen_texture_height = 0;
|
|
|
|
//client callbacks
|
|
display->clientReshapeCallback = NULL;
|
|
display->clientDrawCallback = NULL;
|
|
|
|
//upload
|
|
display->upload_fbo = 0;
|
|
display->upload_depth_buffer = 0;
|
|
display->upload_outtex = 0;
|
|
display->upload_intex = 0;
|
|
display->upload_intex_u = 0;
|
|
display->upload_intex_v = 0;
|
|
display->upload_width = 0;
|
|
display->upload_height = 0;
|
|
display->upload_video_format = GST_VIDEO_FORMAT_RGBx;
|
|
display->upload_colorspace_conversion = GST_GL_DISPLAY_CONVERSION_MESA;
|
|
display->upload_data_width = 0;
|
|
display->upload_data_height = 0;
|
|
display->upload_data = NULL;
|
|
|
|
//foreign gl context
|
|
display->external_gl_context = 0;
|
|
|
|
//filter gen fbo
|
|
display->gen_fbo_width = 0;
|
|
display->gen_fbo_height = 0;
|
|
display->generated_fbo = 0;
|
|
display->generated_depth_buffer = 0;
|
|
|
|
//filter use fbo
|
|
display->use_fbo = 0;
|
|
display->use_depth_buffer = 0;
|
|
display->use_fbo_texture = 0;
|
|
display->use_fbo_width = 0;
|
|
display->use_fbo_height = 0;
|
|
display->use_fbo_scene_cb = NULL;
|
|
display->use_fbo_scene_cb_v2 = NULL;
|
|
display->use_fbo_proj_param1 = 0;
|
|
display->use_fbo_proj_param2 = 0;
|
|
display->use_fbo_proj_param3 = 0;
|
|
display->use_fbo_proj_param4 = 0;
|
|
display->use_fbo_projection = 0;
|
|
display->use_fbo_stuff = NULL;
|
|
display->input_texture_width = 0;
|
|
display->input_texture_height = 0;
|
|
display->input_texture = 0;
|
|
|
|
//filter del fbo
|
|
display->del_fbo = 0;
|
|
display->del_depth_buffer = 0;
|
|
|
|
//download
|
|
display->download_fbo = 0;
|
|
display->download_depth_buffer = 0;
|
|
display->download_texture = 0;
|
|
display->download_texture_u = 0;
|
|
display->download_texture_v = 0;
|
|
display->download_width = 0;
|
|
display->download_height = 0;
|
|
display->download_video_format = 0;
|
|
display->download_data = NULL;
|
|
display->ouput_texture = 0;
|
|
display->ouput_texture_width = 0;
|
|
display->ouput_texture_height = 0;
|
|
|
|
//action gen and del shader
|
|
display->gen_shader_fragment_source = NULL;
|
|
display->gen_shader_vertex_source = NULL;
|
|
display->gen_shader = NULL;
|
|
display->del_shader = NULL;
|
|
|
|
//fragment shader upload
|
|
display->shader_upload_YUY2 = NULL;
|
|
display->shader_upload_UYVY = NULL;
|
|
display->shader_upload_I420_YV12 = NULL;
|
|
display->shader_upload_AYUV = NULL;
|
|
|
|
#ifdef OPENGL_ES2
|
|
display->shader_upload_attr_position_loc = 0;
|
|
display->shader_upload_attr_texture_loc = 0;
|
|
#endif
|
|
|
|
//fragment shader download
|
|
display->shader_download_YUY2 = NULL;
|
|
display->shader_download_UYVY = NULL;
|
|
display->shader_download_I420_YV12 = NULL;
|
|
display->shader_download_AYUV = NULL;
|
|
|
|
#ifdef OPENGL_ES2
|
|
display->shader_download_attr_position_loc = 0;
|
|
display->shader_download_attr_texture_loc = 0;
|
|
display->shader_download_RGB = NULL;
|
|
#endif
|
|
|
|
//YUY2:r,g,a
|
|
//UYVY:a,b,r
|
|
display->text_shader_upload_YUY2_UYVY =
|
|
#ifndef OPENGL_ES2
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect Ytex, UVtex;\n"
|
|
#else
|
|
"precision mediump float;\n"
|
|
"varying vec2 v_texCoord;\n" "uniform sampler2D Ytex, UVtex;\n"
|
|
#endif
|
|
"void main(void) {\n" " float fx, fy, y, u, v, r, g, b;\n"
|
|
#ifndef OPENGL_ES2
|
|
" fx = gl_TexCoord[0].x;\n"
|
|
" fy = gl_TexCoord[0].y;\n"
|
|
" y = texture2DRect(Ytex,vec2(fx,fy)).%c;\n"
|
|
" u = texture2DRect(UVtex,vec2(fx*0.5,fy)).%c;\n"
|
|
" v = texture2DRect(UVtex,vec2(fx*0.5,fy)).%c;\n"
|
|
#else
|
|
" fx = v_texCoord.x;\n"
|
|
" fy = v_texCoord.y;\n"
|
|
" y = texture2D(Ytex,vec2(fx,fy)).%c;\n"
|
|
" u = texture2D(UVtex,vec2(fx*0.5,fy)).%c;\n"
|
|
" v = texture2D(UVtex,vec2(fx*0.5,fy)).%c;\n"
|
|
#endif
|
|
" y=1.164*(y-0.0627);\n"
|
|
" u=u-0.5;\n"
|
|
" v=v-0.5;\n"
|
|
" r = y+1.5958*v;\n"
|
|
" g = y-0.39173*u-0.81290*v;\n"
|
|
" b = y+2.017*u;\n" " gl_FragColor = vec4(r, g, b, 1.0);\n" "}\n";
|
|
|
|
//ATI: "*0.5", ""
|
|
//normal: "", "*0.5"
|
|
display->text_shader_upload_I420_YV12 =
|
|
#ifndef OPENGL_ES2
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect Ytex,Utex,Vtex;\n"
|
|
#else
|
|
"precision mediump float;\n"
|
|
"varying vec2 v_texCoord;\n" "uniform sampler2D Ytex,Utex,Vtex;\n"
|
|
#endif
|
|
"void main(void) {\n" " float r,g,b,y,u,v;\n"
|
|
#ifndef OPENGL_ES2
|
|
" vec2 nxy = gl_TexCoord[0].xy;\n"
|
|
" y=texture2DRect(Ytex,nxy%s).r;\n"
|
|
" u=texture2DRect(Utex,nxy%s).r;\n"
|
|
" v=texture2DRect(Vtex,nxy*0.5).r;\n"
|
|
#else
|
|
" vec2 nxy = v_texCoord.xy;\n"
|
|
" y=texture2D(Ytex,nxy).r;\n"
|
|
" u=texture2D(Utex,nxy).r;\n" " v=texture2D(Vtex,nxy).r;\n"
|
|
#endif
|
|
" y=1.1643*(y-0.0625);\n"
|
|
" u=u-0.5;\n"
|
|
" v=v-0.5;\n"
|
|
" r=y+1.5958*v;\n"
|
|
" g=y-0.39173*u-0.81290*v;\n"
|
|
" b=y+2.017*u;\n" " gl_FragColor=vec4(r,g,b,1.0);\n" "}\n";
|
|
|
|
display->text_shader_upload_AYUV =
|
|
#ifndef OPENGL_ES2
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect tex;\n"
|
|
#else
|
|
"precision mediump float;\n"
|
|
"varying vec2 v_texCoord;\n" "uniform sampler2D tex;\n"
|
|
#endif
|
|
"void main(void) {\n" " float r,g,b,y,u,v;\n"
|
|
#ifndef OPENGL_ES2
|
|
" vec2 nxy=gl_TexCoord[0].xy;\n"
|
|
" y=texture2DRect(tex,nxy).r;\n"
|
|
" u=texture2DRect(tex,nxy).g;\n" " v=texture2DRect(tex,nxy).b;\n"
|
|
#else
|
|
" vec2 nxy = v_texCoord.xy;\n"
|
|
" y=texture2D(tex,nxy).g;\n"
|
|
" u=texture2D(tex,nxy).b;\n" " v=texture2D(tex,nxy).a;\n"
|
|
#endif
|
|
" y=1.1643*(y-0.0625);\n"
|
|
" u=u-0.5;\n"
|
|
" v=v-0.5;\n"
|
|
" r=y+1.5958*v;\n"
|
|
" g=y-0.39173*u-0.81290*v;\n"
|
|
" b=y+2.017*u;\n" " gl_FragColor=vec4(r,g,b,1.0);\n" "}\n";
|
|
|
|
//YUY2:y2,u,y1,v
|
|
//UYVY:v,y1,u,y2
|
|
display->text_shader_download_YUY2_UYVY =
|
|
#ifndef OPENGL_ES2
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect tex;\n"
|
|
#else
|
|
"precision mediump float;\n"
|
|
"varying vec2 v_texCoord;\n" "uniform sampler2D tex;\n"
|
|
#endif
|
|
"void main(void) {\n" " float fx,fy,r,g,b,r2,g2,b2,y1,y2,u,v;\n"
|
|
#ifndef OPENGL_ES2
|
|
" fx = gl_TexCoord[0].x;\n"
|
|
" fy = gl_TexCoord[0].y;\n"
|
|
" r=texture2DRect(tex,vec2(fx*2.0,fy)).r;\n"
|
|
" g=texture2DRect(tex,vec2(fx*2.0,fy)).g;\n"
|
|
" b=texture2DRect(tex,vec2(fx*2.0,fy)).b;\n"
|
|
" r2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).r;\n"
|
|
" g2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).g;\n"
|
|
" b2=texture2DRect(tex,vec2(fx*2.0+1.0,fy)).b;\n"
|
|
#else
|
|
" fx = v_texCoord.x;\n"
|
|
" fy = v_texCoord.y;\n"
|
|
" r=texture2D(tex,vec2(fx*2.0,fy)).r;\n"
|
|
" g=texture2D(tex,vec2(fx*2.0,fy)).g;\n"
|
|
" b=texture2D(tex,vec2(fx*2.0,fy)).b;\n"
|
|
" r2=texture2D(tex,vec2(fx*2.0+1.0,fy)).r;\n"
|
|
" g2=texture2D(tex,vec2(fx*2.0+1.0,fy)).g;\n"
|
|
" b2=texture2D(tex,vec2(fx*2.0+1.0,fy)).b;\n"
|
|
#endif
|
|
" y1=0.299011*r + 0.586987*g + 0.114001*b;\n"
|
|
" y2=0.299011*r2 + 0.586987*g2 + 0.114001*b2;\n"
|
|
" u=-0.148246*r -0.29102*g + 0.439266*b;\n"
|
|
" v=0.439271*r - 0.367833*g - 0.071438*b ;\n"
|
|
" y1=0.858885*y1 + 0.0625;\n"
|
|
" y2=0.858885*y2 + 0.0625;\n"
|
|
" u=u + 0.5;\n" " v=v + 0.5;\n" " gl_FragColor=vec4(%s);\n" "}\n";
|
|
|
|
//no OpenGL ES 2.0 support because for now it's not possible
|
|
//to attach multiple textures to a frame buffer object
|
|
display->text_shader_download_I420_YV12 =
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect tex;\n"
|
|
"uniform float w, h;\n"
|
|
"void main(void) {\n"
|
|
" float r,g,b,r2,b2,g2,y,u,v;\n"
|
|
" vec2 nxy=gl_TexCoord[0].xy;\n"
|
|
" vec2 nxy2=mod(2.0*nxy, vec2(w, h));\n"
|
|
" r=texture2DRect(tex,nxy).r;\n"
|
|
" g=texture2DRect(tex,nxy).g;\n"
|
|
" b=texture2DRect(tex,nxy).b;\n"
|
|
" r2=texture2DRect(tex,nxy2).r;\n"
|
|
" g2=texture2DRect(tex,nxy2).g;\n"
|
|
" b2=texture2DRect(tex,nxy2).b;\n"
|
|
" y=0.299011*r + 0.586987*g + 0.114001*b;\n"
|
|
" u=-0.148246*r2 -0.29102*g2 + 0.439266*b2;\n"
|
|
" v=0.439271*r2 - 0.367833*g2 - 0.071438*b2 ;\n"
|
|
" y=0.858885*y + 0.0625;\n"
|
|
" u=u + 0.5;\n"
|
|
" v=v + 0.5;\n"
|
|
" gl_FragData[0] = vec4(y, 0.0, 0.0, 1.0);\n"
|
|
" gl_FragData[1] = vec4(u, 0.0, 0.0, 1.0);\n"
|
|
" gl_FragData[2] = vec4(v, 0.0, 0.0, 1.0);\n" "}\n";
|
|
|
|
display->text_shader_download_AYUV =
|
|
#ifndef OPENGL_ES2
|
|
"#extension GL_ARB_texture_rectangle : enable\n"
|
|
"uniform sampler2DRect tex;\n"
|
|
#else
|
|
"precision mediump float;\n"
|
|
"varying vec2 v_texCoord;\n" "uniform sampler2D tex;\n"
|
|
#endif
|
|
"void main(void) {\n" " float r,g,b,y,u,v;\n"
|
|
#ifndef OPENGL_ES2
|
|
" vec2 nxy=gl_TexCoord[0].xy;\n"
|
|
" r=texture2DRect(tex,nxy).r;\n"
|
|
" g=texture2DRect(tex,nxy).g;\n" " b=texture2DRect(tex,nxy).b;\n"
|
|
#else
|
|
" vec2 nxy=v_texCoord.xy;\n"
|
|
" r=texture2D(tex,nxy).r;\n"
|
|
" g=texture2D(tex,nxy).g;\n" " b=texture2D(tex,nxy).b;\n"
|
|
#endif
|
|
" y=0.299011*r + 0.586987*g + 0.114001*b;\n"
|
|
" u=-0.148246*r -0.29102*g + 0.439266*b;\n"
|
|
" v=0.439271*r - 0.367833*g - 0.071438*b ;\n"
|
|
" y=0.858885*y + 0.0625;\n" " u=u + 0.5;\n" " v=v + 0.5;\n"
|
|
#ifndef OPENGL_ES2
|
|
" gl_FragColor=vec4(y,u,v,1.0);\n"
|
|
#else
|
|
" gl_FragColor=vec4(1.0,y,u,v);\n"
|
|
#endif
|
|
"}\n";
|
|
|
|
#ifdef OPENGL_ES2
|
|
display->redisplay_vertex_shader_str =
|
|
"attribute vec4 a_position; \n"
|
|
"attribute vec2 a_texCoord; \n"
|
|
"varying vec2 v_texCoord; \n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" gl_Position = a_position; \n"
|
|
" v_texCoord = a_texCoord; \n" "} \n";
|
|
|
|
display->redisplay_fragment_shader_str =
|
|
"precision mediump float; \n"
|
|
"varying vec2 v_texCoord; \n"
|
|
"uniform sampler2D s_texture; \n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" gl_FragColor = texture2D( s_texture, v_texCoord );\n"
|
|
"} \n";
|
|
|
|
display->text_vertex_shader_upload =
|
|
"attribute vec4 a_position; \n"
|
|
"attribute vec2 a_texCoord; \n"
|
|
"varying vec2 v_texCoord; \n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" gl_Position = a_position; \n"
|
|
" v_texCoord = a_texCoord; \n" "} \n";
|
|
|
|
display->text_vertex_shader_download =
|
|
"attribute vec4 a_position; \n"
|
|
"attribute vec2 a_texCoord; \n"
|
|
"varying vec2 v_texCoord; \n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" gl_Position = a_position; \n"
|
|
" v_texCoord = a_texCoord; \n" "} \n";
|
|
|
|
display->text_fragment_shader_download_RGB =
|
|
"precision mediump float; \n"
|
|
"varying vec2 v_texCoord; \n"
|
|
"uniform sampler2D s_texture; \n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" gl_FragColor = texture2D( s_texture, v_texCoord );\n"
|
|
"} \n";
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
gst_gl_display_finalize (GObject * object)
|
|
{
|
|
GstGLDisplay *display = GST_GL_DISPLAY (object);
|
|
|
|
if (display->mutex && display->gl_window) {
|
|
|
|
gst_gl_display_lock (display);
|
|
|
|
gst_gl_window_set_resize_callback (display->gl_window, NULL, NULL);
|
|
gst_gl_window_set_draw_callback (display->gl_window, NULL, NULL);
|
|
gst_gl_window_set_close_callback (display->gl_window, NULL, NULL);
|
|
|
|
GST_INFO ("send quit gl window loop");
|
|
|
|
gst_gl_window_quit_loop (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_destroy_context), display);
|
|
|
|
GST_INFO ("quit sent to gl window loop");
|
|
|
|
g_cond_wait (display->cond_destroy_context, display->mutex);
|
|
GST_INFO ("quit received from gl window");
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
if (display->gl_thread) {
|
|
gpointer ret = g_thread_join (display->gl_thread);
|
|
GST_INFO ("gl thread joined");
|
|
if (ret != NULL)
|
|
GST_ERROR ("gl thread returned a not null pointer");
|
|
display->gl_thread = NULL;
|
|
}
|
|
|
|
if (display->texture_pool) {
|
|
//texture pool is empty after destroying the gl context
|
|
if (g_hash_table_size (display->texture_pool) != 0)
|
|
GST_ERROR ("texture pool is not empty");
|
|
g_hash_table_unref (display->texture_pool);
|
|
display->texture_pool = NULL;
|
|
}
|
|
if (display->mutex) {
|
|
g_mutex_free (display->mutex);
|
|
display->mutex = NULL;
|
|
}
|
|
if (display->cond_destroy_context) {
|
|
g_cond_free (display->cond_destroy_context);
|
|
display->cond_destroy_context = NULL;
|
|
}
|
|
if (display->cond_create_context) {
|
|
g_cond_free (display->cond_create_context);
|
|
display->cond_create_context = NULL;
|
|
}
|
|
if (display->clientReshapeCallback)
|
|
display->clientReshapeCallback = NULL;
|
|
if (display->clientDrawCallback)
|
|
display->clientDrawCallback = NULL;
|
|
if (display->use_fbo_scene_cb)
|
|
display->use_fbo_scene_cb = NULL;
|
|
if (display->use_fbo_scene_cb_v2)
|
|
display->use_fbo_scene_cb_v2 = NULL;
|
|
if (display->use_fbo_stuff)
|
|
display->use_fbo_stuff = NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//------------------ BEGIN GL THREAD PROCS -------------------
|
|
//------------------------------------------------------------
|
|
|
|
/* Called in the gl thread */
|
|
gpointer
|
|
gst_gl_display_thread_create_context (GstGLDisplay * display)
|
|
{
|
|
GLenum err = 0;
|
|
|
|
gst_gl_display_lock (display);
|
|
display->gl_window = gst_gl_window_new (display->external_gl_context);
|
|
|
|
if (!display->gl_window) {
|
|
display->isAlive = FALSE;
|
|
GST_ERROR_OBJECT (display, "Failed to create gl window");
|
|
g_cond_signal (display->cond_create_context);
|
|
gst_gl_display_unlock (display);
|
|
return NULL;
|
|
}
|
|
|
|
GST_INFO ("gl window created");
|
|
|
|
#ifndef OPENGL_ES2
|
|
err = glewInit ();
|
|
#endif
|
|
if (err != GLEW_OK) {
|
|
#ifndef OPENGL_ES2
|
|
GST_ERROR_OBJECT (display, "Failed to init GLEW: %s",
|
|
glewGetErrorString (err));
|
|
display->isAlive = FALSE;
|
|
#endif
|
|
} else {
|
|
//OpenGL > 1.2.0 and Glew > 1.4.0
|
|
GString *opengl_version =
|
|
g_string_truncate (g_string_new ((gchar *) glGetString (GL_VERSION)),
|
|
3);
|
|
gint opengl_version_major = 0;
|
|
gint opengl_version_minor = 0;
|
|
|
|
sscanf (opengl_version->str, "%d.%d", &opengl_version_major,
|
|
&opengl_version_minor);
|
|
|
|
GST_INFO ("GL_VERSION: %s", glGetString (GL_VERSION));
|
|
#ifndef OPENGL_ES2
|
|
GST_INFO ("GLEW_VERSION: %s", glewGetString (GLEW_VERSION));
|
|
#endif
|
|
if (glGetString (GL_SHADING_LANGUAGE_VERSION))
|
|
GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
|
|
glGetString (GL_SHADING_LANGUAGE_VERSION));
|
|
else
|
|
GST_INFO ("Your driver does not support GLSL (OpenGL Shading Language)");
|
|
|
|
GST_INFO ("GL_VENDOR: %s", glGetString (GL_VENDOR));
|
|
GST_INFO ("GL_RENDERER: %s", glGetString (GL_RENDERER));
|
|
|
|
g_string_free (opengl_version, TRUE);
|
|
|
|
#ifndef OPENGL_ES2
|
|
if ((opengl_version_major < 1) ||
|
|
(GLEW_VERSION_MAJOR < 1) ||
|
|
(opengl_version_major < 2 && opengl_version_major >= 1
|
|
&& opengl_version_minor < 2) || (GLEW_VERSION_MAJOR < 2
|
|
&& GLEW_VERSION_MAJOR >= 1 && GLEW_VERSION_MINOR < 4)) {
|
|
//turn off the pipeline, the old drivers are not yet supported
|
|
GST_WARNING ("Required OpenGL >= 1.2.0 and Glew >= 1.4.0");
|
|
display->isAlive = FALSE;
|
|
}
|
|
#else
|
|
if (!GL_ES_VERSION_2_0) {
|
|
GST_WARNING ("Required OpenGL ES > 2.0");
|
|
display->isAlive = FALSE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//setup callbacks
|
|
gst_gl_window_set_resize_callback (display->gl_window,
|
|
GST_GL_WINDOW_CB2 (gst_gl_display_on_resize), display);
|
|
gst_gl_window_set_draw_callback (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_on_draw), display);
|
|
gst_gl_window_set_close_callback (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_on_close), display);
|
|
|
|
g_cond_signal (display->cond_create_context);
|
|
|
|
gst_gl_display_unlock (display);
|
|
|
|
gst_gl_window_run_loop (display->gl_window);
|
|
|
|
GST_INFO ("loop exited\n");
|
|
|
|
gst_gl_display_lock (display);
|
|
|
|
display->isAlive = FALSE;
|
|
|
|
g_object_unref (G_OBJECT (display->gl_window));
|
|
|
|
display->gl_window = NULL;
|
|
|
|
g_cond_signal (display->cond_destroy_context);
|
|
|
|
gst_gl_display_unlock (display);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_destroy_context (GstGLDisplay * display)
|
|
{
|
|
//colorspace_conversion specific
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
{
|
|
glUseProgramObjectARB (0);
|
|
if (display->shader_upload_YUY2) {
|
|
g_object_unref (G_OBJECT (display->shader_upload_YUY2));
|
|
display->shader_upload_YUY2 = NULL;
|
|
}
|
|
if (display->shader_upload_UYVY) {
|
|
g_object_unref (G_OBJECT (display->shader_upload_UYVY));
|
|
display->shader_upload_UYVY = NULL;
|
|
}
|
|
if (display->shader_upload_I420_YV12) {
|
|
g_object_unref (G_OBJECT (display->shader_upload_I420_YV12));
|
|
display->shader_upload_I420_YV12 = NULL;
|
|
}
|
|
if (display->shader_upload_AYUV) {
|
|
g_object_unref (G_OBJECT (display->shader_upload_AYUV));
|
|
display->shader_upload_AYUV = NULL;
|
|
}
|
|
if (display->shader_download_YUY2) {
|
|
g_object_unref (G_OBJECT (display->shader_download_YUY2));
|
|
display->shader_download_YUY2 = NULL;
|
|
}
|
|
if (display->shader_download_UYVY) {
|
|
g_object_unref (G_OBJECT (display->shader_download_UYVY));
|
|
display->shader_download_UYVY = NULL;
|
|
}
|
|
if (display->shader_download_I420_YV12) {
|
|
g_object_unref (G_OBJECT (display->shader_download_I420_YV12));
|
|
display->shader_download_I420_YV12 = NULL;
|
|
}
|
|
if (display->shader_download_AYUV) {
|
|
g_object_unref (G_OBJECT (display->shader_download_AYUV));
|
|
display->shader_download_AYUV = NULL;
|
|
}
|
|
#ifdef OPENGL_ES2
|
|
if (display->shader_download_RGB) {
|
|
g_object_unref (G_OBJECT (display->shader_download_RGB));
|
|
display->shader_download_RGB = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (display->upload_fbo) {
|
|
glDeleteFramebuffersEXT (1, &display->upload_fbo);
|
|
display->upload_fbo = 0;
|
|
}
|
|
if (display->upload_depth_buffer) {
|
|
glDeleteRenderbuffersEXT (1, &display->upload_depth_buffer);
|
|
display->upload_depth_buffer = 0;
|
|
}
|
|
if (display->download_fbo) {
|
|
glDeleteFramebuffersEXT (1, &display->download_fbo);
|
|
display->download_fbo = 0;
|
|
}
|
|
if (display->download_depth_buffer) {
|
|
glDeleteRenderbuffersEXT (1, &display->download_depth_buffer);
|
|
display->download_depth_buffer = 0;
|
|
}
|
|
if (display->download_texture) {
|
|
glDeleteTextures (1, &display->download_texture);
|
|
display->download_texture = 0;
|
|
}
|
|
if (display->download_texture_u) {
|
|
glDeleteTextures (1, &display->download_texture_u);
|
|
display->download_texture_u = 0;
|
|
}
|
|
if (display->download_texture_v) {
|
|
glDeleteTextures (1, &display->download_texture_v);
|
|
display->download_texture_v = 0;
|
|
}
|
|
if (display->upload_intex != 0) {
|
|
glDeleteTextures (1, &display->upload_intex);
|
|
display->upload_intex = 0;
|
|
}
|
|
if (display->upload_intex_u != 0) {
|
|
glDeleteTextures (1, &display->upload_intex_u);
|
|
display->upload_intex_u = 0;
|
|
}
|
|
if (display->upload_intex_v != 0) {
|
|
glDeleteTextures (1, &display->upload_intex_v);
|
|
display->upload_intex_v = 0;
|
|
}
|
|
#ifdef OPENGL_ES2
|
|
if (display->redisplay_shader) {
|
|
g_object_unref (G_OBJECT (display->redisplay_shader));
|
|
display->redisplay_shader = NULL;
|
|
}
|
|
#endif
|
|
|
|
GST_INFO ("Cleaning texture pool");
|
|
|
|
//clean up the texture pool
|
|
g_hash_table_foreach_remove (display->texture_pool,
|
|
gst_gl_display_texture_pool_func_clean, NULL);
|
|
|
|
GST_INFO ("Context destroyed");
|
|
}
|
|
|
|
|
|
void
|
|
gst_gl_display_thread_run_generic (GstGLDisplay * display)
|
|
{
|
|
display->generic_callback (display, display->data);
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_gen_texture (GstGLDisplay * display)
|
|
{
|
|
//setup a texture to render to (this one will be in a gl buffer)
|
|
gst_gl_display_glgen_texture (display, &display->gen_texture,
|
|
display->gen_texture_width, display->gen_texture_height);
|
|
}
|
|
|
|
|
|
#ifdef OPENGL_ES2
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_init_redisplay (GstGLDisplay * display)
|
|
{
|
|
GError *error = NULL;
|
|
display->redisplay_shader = gst_gl_shader_new ();
|
|
|
|
gst_gl_shader_set_vertex_source (display->redisplay_shader,
|
|
display->redisplay_vertex_shader_str);
|
|
gst_gl_shader_set_fragment_source (display->redisplay_shader,
|
|
display->redisplay_fragment_shader_str);
|
|
|
|
gst_gl_shader_compile (display->redisplay_shader, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
} else {
|
|
display->redisplay_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location (display->redisplay_shader,
|
|
"a_position");
|
|
display->redisplay_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location (display->redisplay_shader,
|
|
"a_texCoord");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_init_upload (GstGLDisplay * display)
|
|
{
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
//color space conversion is not needed
|
|
//but if the size is different we need to redraw it
|
|
//using fbo
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
gst_gl_display_thread_init_upload_fbo (display);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//color space conversion is needed
|
|
{
|
|
//check if fragment shader is available, then load them
|
|
/* shouldn't we require ARB_shading_language_100? --Filippo */
|
|
if (GLEW_ARB_fragment_shader) {
|
|
|
|
#ifdef OPENGL_ES2
|
|
GError *error = NULL;
|
|
#endif
|
|
|
|
GST_INFO ("Context, ARB_fragment_shader supported: yes");
|
|
|
|
display->upload_colorspace_conversion = GST_GL_DISPLAY_CONVERSION_GLSL;
|
|
|
|
gst_gl_display_thread_init_upload_fbo (display);
|
|
if (!display->isAlive)
|
|
break;
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
{
|
|
gchar text_shader_upload_YUY2[2048];
|
|
sprintf (text_shader_upload_YUY2,
|
|
display->text_shader_upload_YUY2_UYVY, 'r', 'g', 'a');
|
|
|
|
display->shader_upload_YUY2 = gst_gl_shader_new ();
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_upload_YUY2,
|
|
text_shader_upload_YUY2, GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_YUY2));
|
|
display->shader_upload_YUY2 = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_upload_YUY2,
|
|
display->text_vertex_shader_upload);
|
|
gst_gl_shader_set_fragment_source (display->shader_upload_YUY2,
|
|
text_shader_upload_YUY2);
|
|
|
|
gst_gl_shader_compile (display->shader_upload_YUY2, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_YUY2));
|
|
display->shader_upload_YUY2 = NULL;
|
|
} else {
|
|
display->shader_upload_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_YUY2, "a_position");
|
|
display->shader_upload_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_YUY2, "a_texCoord");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
{
|
|
gchar text_shader_upload_UYVY[2048];
|
|
sprintf (text_shader_upload_UYVY,
|
|
#ifndef OPENGL_ES2
|
|
display->text_shader_upload_YUY2_UYVY, 'a', 'b', 'r');
|
|
#else
|
|
display->text_shader_upload_YUY2_UYVY, 'a', 'r', 'b');
|
|
#endif
|
|
|
|
display->shader_upload_UYVY = gst_gl_shader_new ();
|
|
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_upload_UYVY,
|
|
text_shader_upload_UYVY, GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_UYVY));
|
|
display->shader_upload_UYVY = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_upload_UYVY,
|
|
display->text_vertex_shader_upload);
|
|
gst_gl_shader_set_fragment_source (display->shader_upload_UYVY,
|
|
text_shader_upload_UYVY);
|
|
|
|
gst_gl_shader_compile (display->shader_upload_UYVY, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_UYVY));
|
|
display->shader_upload_UYVY = NULL;
|
|
} else {
|
|
display->shader_upload_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_UYVY, "a_position");
|
|
display->shader_upload_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_UYVY, "a_texCoord");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
{
|
|
#ifndef OPENGL_ES2
|
|
gchar text_shader_upload_I420_YV12[2048];
|
|
if (g_ascii_strncasecmp ("ATI", (gchar *) glGetString (GL_VENDOR),
|
|
3) == 0)
|
|
sprintf (text_shader_upload_I420_YV12,
|
|
display->text_shader_upload_I420_YV12, "*0.5", "");
|
|
else
|
|
sprintf (text_shader_upload_I420_YV12,
|
|
display->text_shader_upload_I420_YV12, "", "*0.5");
|
|
#endif
|
|
|
|
display->shader_upload_I420_YV12 = gst_gl_shader_new ();
|
|
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check
|
|
(display->shader_upload_I420_YV12, text_shader_upload_I420_YV12,
|
|
GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_I420_YV12));
|
|
display->shader_upload_I420_YV12 = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_upload_I420_YV12,
|
|
display->text_vertex_shader_upload);
|
|
gst_gl_shader_set_fragment_source (display->shader_upload_I420_YV12,
|
|
display->text_shader_upload_I420_YV12);
|
|
|
|
gst_gl_shader_compile (display->shader_upload_I420_YV12, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_I420_YV12));
|
|
display->shader_upload_I420_YV12 = NULL;
|
|
} else {
|
|
display->shader_upload_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_I420_YV12, "a_position");
|
|
display->shader_upload_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_I420_YV12, "a_texCoord");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
{
|
|
display->shader_upload_AYUV = gst_gl_shader_new ();
|
|
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_upload_AYUV,
|
|
display->text_shader_upload_AYUV,
|
|
GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_AYUV));
|
|
display->shader_upload_AYUV = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_upload_AYUV,
|
|
display->text_vertex_shader_upload);
|
|
gst_gl_shader_set_fragment_source (display->shader_upload_AYUV,
|
|
display->text_shader_upload_AYUV);
|
|
|
|
gst_gl_shader_compile (display->shader_upload_AYUV, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_upload_AYUV));
|
|
display->shader_upload_AYUV = NULL;
|
|
} else {
|
|
display->shader_upload_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_AYUV, "a_position");
|
|
display->shader_upload_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_upload_AYUV, "a_texCoord");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
//check if YCBCR MESA is available
|
|
else if (GLEW_MESA_ycbcr_texture) {
|
|
//GLSL and Color Matrix are not available on your drivers, switch to YCBCR MESA
|
|
GST_INFO ("Context, ARB_fragment_shader supported: no");
|
|
GST_INFO ("Context, GLEW_MESA_ycbcr_texture supported: yes");
|
|
|
|
display->upload_colorspace_conversion = GST_GL_DISPLAY_CONVERSION_MESA;
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
//color space conversion is not needed
|
|
//but if the size is different we need to redraw it
|
|
//using fbo
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
gst_gl_display_thread_init_upload_fbo (display);
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//turn off the pipeline because
|
|
//MESA only support YUY2 and UYVY
|
|
GST_WARNING
|
|
("Your MESA version only supports YUY2 and UYVY (GLSL is required for others yuv formats");
|
|
display->isAlive = FALSE;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
//check if color matrix is available
|
|
else if (GLEW_ARB_imaging) {
|
|
//GLSL is not available on your drivers, switch to Color Matrix
|
|
GST_INFO ("Context, ARB_fragment_shader supported: no");
|
|
GST_INFO ("Context, GLEW_MESA_ycbcr_texture supported: no");
|
|
GST_INFO ("Context, GLEW_ARB_imaging supported: yes");
|
|
|
|
display->upload_colorspace_conversion =
|
|
GST_GL_DISPLAY_CONVERSION_MATRIX;
|
|
|
|
//turn off the pipeline because we do not support it yet
|
|
GST_WARNING
|
|
("Colorspace conversion using Color Matrix is not yet supported");
|
|
display->isAlive = FALSE;
|
|
} else {
|
|
GST_WARNING ("Context, ARB_fragment_shader supported: no");
|
|
GST_WARNING ("Context, GLEW_ARB_imaging supported: no");
|
|
GST_WARNING ("Context, GLEW_MESA_ycbcr_texture supported: no");
|
|
|
|
//turn off the pipeline because colorspace conversion is not possible
|
|
display->isAlive = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* Called by the idle function */
|
|
void
|
|
gst_gl_display_thread_do_upload (GstGLDisplay * display)
|
|
{
|
|
gst_gl_display_thread_do_upload_fill (display);
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
//color space conversion is not needed
|
|
//but if the size is different we need to redraw it
|
|
//using fbo
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
gst_gl_display_thread_do_upload_draw (display);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
{
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
//color space conversion is needed
|
|
gst_gl_display_thread_do_upload_draw (display);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
//color space conversion is needed
|
|
//not yet supported
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
//color space conversion is not needed
|
|
//but if the size is different we need to redraw it
|
|
//using fbo
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
gst_gl_display_thread_do_upload_draw (display);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_init_download (GstGLDisplay * display)
|
|
{
|
|
switch (display->download_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
//color space conversion is not needed
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//color space conversion is needed
|
|
{
|
|
|
|
if (GLEW_EXT_framebuffer_object) {
|
|
GST_INFO ("Context, EXT_framebuffer_object supported: yes");
|
|
|
|
//-- init output frame buffer object (GL -> video)
|
|
|
|
//setup FBO
|
|
glGenFramebuffersEXT (1, &display->download_fbo);
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->download_fbo);
|
|
|
|
//setup the render buffer for depth
|
|
glGenRenderbuffersEXT (1, &display->download_depth_buffer);
|
|
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT,
|
|
display->download_depth_buffer);
|
|
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
|
|
display->download_width, display->download_height);
|
|
|
|
//setup a first texture to render to
|
|
glGenTextures (1, &display->download_texture);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->download_texture);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
display->download_width, display->download_height, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
//attach the first texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, display->download_texture, 0);
|
|
|
|
switch (display->download_video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//only one attached texture is needed
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
//setup a second texture to render to
|
|
glGenTextures (1, &display->download_texture_u);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB,
|
|
display->download_texture_u);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
display->download_width, display->download_height, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
//attach the second texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
|
|
GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB,
|
|
display->download_texture_u, 0);
|
|
|
|
//setup a third texture to render to
|
|
glGenTextures (1, &display->download_texture_v);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB,
|
|
display->download_texture_v);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
display->download_width, display->download_height, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
//attach the third texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
|
|
GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_RECTANGLE_ARB,
|
|
display->download_texture_v, 0);
|
|
|
|
display->multipleRT[0] = GL_COLOR_ATTACHMENT0_EXT;
|
|
display->multipleRT[1] = GL_COLOR_ATTACHMENT1_EXT;
|
|
display->multipleRT[2] = GL_COLOR_ATTACHMENT2_EXT;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
//attach the depth render buffer to the FBO
|
|
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT,
|
|
GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT,
|
|
display->download_depth_buffer);
|
|
|
|
gst_gl_display_check_framebuffer_status ();
|
|
|
|
g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) ==
|
|
GL_FRAMEBUFFER_COMPLETE_EXT);
|
|
|
|
//unbind the FBO
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
} else {
|
|
//turn off the pipeline because Frame buffer object is a requirement when using filters
|
|
//or when using GLSL colorspace conversion
|
|
GST_WARNING ("Context, EXT_framebuffer_object supported: no");
|
|
display->isAlive = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
switch (display->download_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
//color space conversion is not needed
|
|
#ifdef OPENGL_ES2
|
|
{
|
|
//glGetTexImage2D no available in OpenGL ES 2.0
|
|
GError *error = NULL;
|
|
display->shader_download_RGB = gst_gl_shader_new ();
|
|
|
|
gst_gl_shader_set_vertex_source (display->shader_download_RGB,
|
|
display->text_vertex_shader_download);
|
|
gst_gl_shader_set_fragment_source (display->shader_download_RGB,
|
|
display->text_fragment_shader_download_RGB);
|
|
|
|
gst_gl_shader_compile (display->shader_download_RGB, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_RGB));
|
|
display->shader_download_RGB = NULL;
|
|
} else {
|
|
display->shader_download_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location (display->shader_download_RGB,
|
|
"a_position");
|
|
display->shader_download_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location (display->shader_download_RGB,
|
|
"a_texCoord");
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//color space conversion is needed
|
|
{
|
|
//check if fragment shader is available, then load them
|
|
//GLSL is a requirement for donwload
|
|
if (GLEW_ARB_fragment_shader) {
|
|
|
|
#ifdef OPENGL_ES2
|
|
GError *error = NULL;
|
|
#endif
|
|
|
|
switch (display->download_video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
{
|
|
gchar text_shader_download_YUY2[2048];
|
|
sprintf (text_shader_download_YUY2,
|
|
display->text_shader_download_YUY2_UYVY, "y2,u,y1,v");
|
|
|
|
display->shader_download_YUY2 = gst_gl_shader_new ();
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_download_YUY2,
|
|
text_shader_download_YUY2, GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_YUY2));
|
|
display->shader_download_YUY2 = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_download_YUY2,
|
|
display->text_vertex_shader_download);
|
|
gst_gl_shader_set_fragment_source (display->shader_download_YUY2,
|
|
text_shader_download_YUY2);
|
|
|
|
gst_gl_shader_compile (display->shader_download_YUY2, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_YUY2));
|
|
display->shader_download_YUY2 = NULL;
|
|
} else {
|
|
display->shader_download_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_YUY2, "a_position");
|
|
display->shader_download_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_YUY2, "a_texCoord");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
{
|
|
gchar text_shader_download_UYVY[2048];
|
|
sprintf (text_shader_download_UYVY,
|
|
display->text_shader_download_YUY2_UYVY, "v,y1,u,y2");
|
|
|
|
display->shader_download_UYVY = gst_gl_shader_new ();
|
|
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_download_UYVY,
|
|
text_shader_download_UYVY, GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_UYVY));
|
|
display->shader_download_UYVY = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_download_UYVY,
|
|
display->text_vertex_shader_download);
|
|
gst_gl_shader_set_fragment_source (display->shader_download_UYVY,
|
|
text_shader_download_UYVY);
|
|
|
|
gst_gl_shader_compile (display->shader_download_UYVY, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_UYVY));
|
|
display->shader_download_UYVY = NULL;
|
|
} else {
|
|
display->shader_download_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_UYVY, "a_position");
|
|
display->shader_download_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_UYVY, "a_texCoord");
|
|
}
|
|
#endif
|
|
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
display->shader_download_I420_YV12 = gst_gl_shader_new ();
|
|
if (!gst_gl_shader_compile_and_check
|
|
(display->shader_download_I420_YV12,
|
|
display->text_shader_download_I420_YV12,
|
|
GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_I420_YV12));
|
|
display->shader_download_I420_YV12 = NULL;
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
display->shader_download_AYUV = gst_gl_shader_new ();
|
|
|
|
#ifndef OPENGL_ES2
|
|
if (!gst_gl_shader_compile_and_check (display->shader_download_AYUV,
|
|
display->text_shader_download_AYUV,
|
|
GST_GL_SHADER_FRAGMENT_SOURCE)) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_AYUV));
|
|
display->shader_download_AYUV = NULL;
|
|
}
|
|
#else
|
|
gst_gl_shader_set_vertex_source (display->shader_download_AYUV,
|
|
display->text_vertex_shader_download);
|
|
gst_gl_shader_set_fragment_source (display->shader_download_AYUV,
|
|
display->text_shader_download_AYUV);
|
|
|
|
gst_gl_shader_compile (display->shader_download_AYUV, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->shader_download_AYUV));
|
|
display->shader_download_AYUV = NULL;
|
|
} else {
|
|
display->shader_download_attr_position_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_AYUV, "a_position");
|
|
display->shader_download_attr_texture_loc =
|
|
gst_gl_shader_get_attribute_location
|
|
(display->shader_download_AYUV, "a_texCoord");
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
} else {
|
|
//turn off the pipeline because colorspace conversion is not possible
|
|
GST_WARNING ("Context, ARB_fragment_shader supported: no");
|
|
display->isAlive = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_do_download (GstGLDisplay * display)
|
|
{
|
|
switch (display->download_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
//color space conversion is not needed
|
|
gst_gl_display_thread_do_download_draw_rgb (display);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
//color space conversion is needed
|
|
gst_gl_display_thread_do_download_draw_yuv (display);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_gen_fbo (GstGLDisplay * display)
|
|
{
|
|
//a texture must be attached to the FBO
|
|
GLuint fake_texture = 0;
|
|
|
|
//-- generate frame buffer object
|
|
|
|
if (!GLEW_EXT_framebuffer_object) {
|
|
//turn off the pipeline because Frame buffer object is a not present
|
|
GST_WARNING ("Context, EXT_framebuffer_object supported: no");
|
|
display->isAlive = FALSE;
|
|
return;
|
|
}
|
|
//setup FBO
|
|
glGenFramebuffersEXT (1, &display->generated_fbo);
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->generated_fbo);
|
|
|
|
//setup the render buffer for depth
|
|
glGenRenderbuffersEXT (1, &display->generated_depth_buffer);
|
|
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, display->generated_depth_buffer);
|
|
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
|
|
display->gen_fbo_width, display->gen_fbo_height);
|
|
|
|
//setup a texture to render to
|
|
glGenTextures (1, &fake_texture);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, fake_texture);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
display->gen_fbo_width, display->gen_fbo_height, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
//attach the texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, fake_texture, 0);
|
|
|
|
//attach the depth render buffer to the FBO
|
|
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
|
GL_RENDERBUFFER_EXT, display->generated_depth_buffer);
|
|
|
|
g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) ==
|
|
GL_FRAMEBUFFER_COMPLETE_EXT);
|
|
|
|
//unbind the FBO
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
glDeleteTextures (1, &fake_texture);
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_use_fbo (GstGLDisplay * display)
|
|
{
|
|
#ifdef OPENGL_ES2
|
|
GLint viewport_dim[4];
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->use_fbo);
|
|
|
|
//setup a texture to render to
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->use_fbo_texture);
|
|
|
|
//attach the texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, display->use_fbo_texture, 0);
|
|
|
|
if (GLEW_ARB_fragment_shader)
|
|
gst_gl_shader_use (NULL);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glPushAttrib (GL_VIEWPORT_BIT);
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
|
|
switch (display->use_fbo_projection) {
|
|
case GST_GL_DISPLAY_PROJECTION_ORTHO2D:
|
|
gluOrtho2D (display->use_fbo_proj_param1, display->use_fbo_proj_param2,
|
|
display->use_fbo_proj_param3, display->use_fbo_proj_param4);
|
|
break;
|
|
case GST_GL_DISPLAY_PROJECTION_PERSPECTIVE:
|
|
gluPerspective (display->use_fbo_proj_param1,
|
|
display->use_fbo_proj_param2, display->use_fbo_proj_param3,
|
|
display->use_fbo_proj_param4);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
#else // OPENGL_ES2
|
|
glGetIntegerv (GL_VIEWPORT, viewport_dim);
|
|
#endif
|
|
|
|
glViewport (0, 0, display->use_fbo_width, display->use_fbo_height);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
//the opengl scene
|
|
display->use_fbo_scene_cb (display->input_texture_width,
|
|
display->input_texture_height, display->input_texture,
|
|
display->use_fbo_stuff);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_NONE);
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPopMatrix ();
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPopMatrix ();
|
|
glPopAttrib ();
|
|
#else
|
|
glViewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
|
|
viewport_dim[3]);
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
}
|
|
|
|
|
|
/* Called in a gl thread
|
|
* Need full shader support */
|
|
void
|
|
gst_gl_display_thread_use_fbo_v2 (GstGLDisplay * display)
|
|
{
|
|
GLint viewport_dim[4];
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->use_fbo);
|
|
|
|
//setup a texture to render to
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->use_fbo_texture);
|
|
|
|
//attach the texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, display->use_fbo_texture, 0);
|
|
|
|
glGetIntegerv (GL_VIEWPORT, viewport_dim);
|
|
|
|
glViewport (0, 0, display->use_fbo_width, display->use_fbo_height);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
//the opengl scene
|
|
display->use_fbo_scene_cb_v2 (display->use_fbo_stuff);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_NONE);
|
|
#endif
|
|
|
|
glViewport (viewport_dim[0], viewport_dim[1],
|
|
viewport_dim[2], viewport_dim[3]);
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_del_fbo (GstGLDisplay * display)
|
|
{
|
|
if (display->del_fbo) {
|
|
glDeleteFramebuffersEXT (1, &display->del_fbo);
|
|
display->del_fbo = 0;
|
|
}
|
|
if (display->del_depth_buffer) {
|
|
glDeleteRenderbuffersEXT (1, &display->del_depth_buffer);
|
|
display->del_depth_buffer = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_gen_shader (GstGLDisplay * display)
|
|
{
|
|
if (GLEW_ARB_fragment_shader) {
|
|
if (display->gen_shader_vertex_source ||
|
|
display->gen_shader_fragment_source) {
|
|
gboolean isAlive = TRUE;
|
|
GError *error = NULL;
|
|
|
|
display->gen_shader = gst_gl_shader_new ();
|
|
|
|
if (display->gen_shader_vertex_source)
|
|
gst_gl_shader_set_vertex_source (display->gen_shader,
|
|
display->gen_shader_vertex_source);
|
|
|
|
if (display->gen_shader_fragment_source)
|
|
gst_gl_shader_set_fragment_source (display->gen_shader,
|
|
display->gen_shader_fragment_source);
|
|
|
|
gst_gl_shader_compile (display->gen_shader, &error);
|
|
if (error) {
|
|
GST_ERROR ("%s", error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
gst_gl_shader_use (NULL);
|
|
isAlive = FALSE;
|
|
}
|
|
|
|
if (!isAlive) {
|
|
display->isAlive = FALSE;
|
|
g_object_unref (G_OBJECT (display->gen_shader));
|
|
display->gen_shader = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
GST_WARNING ("One of the filter required ARB_fragment_shader");
|
|
display->isAlive = FALSE;
|
|
display->gen_shader = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Called in the gl thread */
|
|
void
|
|
gst_gl_display_thread_del_shader (GstGLDisplay * display)
|
|
{
|
|
if (display->del_shader) {
|
|
g_object_unref (G_OBJECT (display->del_shader));
|
|
display->del_shader = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//------------------ BEGIN GL THREAD ACTIONS -----------------
|
|
//------------------------------------------------------------
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//---------------------- BEGIN PRIVATE -----------------------
|
|
//------------------------------------------------------------
|
|
|
|
|
|
void
|
|
gst_gl_display_lock (GstGLDisplay * display)
|
|
{
|
|
g_mutex_lock (display->mutex);
|
|
}
|
|
|
|
|
|
void
|
|
gst_gl_display_unlock (GstGLDisplay * display)
|
|
{
|
|
g_mutex_unlock (display->mutex);
|
|
}
|
|
|
|
|
|
void
|
|
gst_gl_display_on_resize (GstGLDisplay * display, gint width, gint height)
|
|
{
|
|
//check if a client reshape callback is registered
|
|
if (display->clientReshapeCallback)
|
|
display->clientReshapeCallback (width, height);
|
|
|
|
//default reshape
|
|
else {
|
|
if (display->keep_aspect_ratio) {
|
|
GstVideoRectangle src, dst, result;
|
|
|
|
src.x = 0;
|
|
src.y = 0;
|
|
src.w = display->redisplay_texture_width;
|
|
src.h = display->redisplay_texture_height;
|
|
|
|
dst.x = 0;
|
|
dst.y = 0;
|
|
dst.w = width;
|
|
dst.h = height;
|
|
|
|
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
|
glViewport (result.x, result.y, result.w, result.h);
|
|
} else {
|
|
glViewport (0, 0, width, height);
|
|
}
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
gluOrtho2D (0, width, 0, height);
|
|
glMatrixMode (GL_MODELVIEW);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
gst_gl_display_on_draw (GstGLDisplay * display)
|
|
{
|
|
//check if tecture is ready for being drawn
|
|
if (!display->redisplay_texture)
|
|
return;
|
|
|
|
//opengl scene
|
|
GST_DEBUG ("on draw");
|
|
|
|
//make sure that the environnement is clean
|
|
if (display->upload_colorspace_conversion == GST_GL_DISPLAY_CONVERSION_GLSL)
|
|
glUseProgramObjectARB (0);
|
|
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0);
|
|
|
|
//check if a client draw callback is registered
|
|
if (display->clientDrawCallback) {
|
|
gboolean doRedisplay =
|
|
display->clientDrawCallback (display->redisplay_texture,
|
|
display->redisplay_texture_width, display->redisplay_texture_height);
|
|
|
|
if (doRedisplay && display->gl_window)
|
|
gst_gl_window_draw_unlocked (display->gl_window,
|
|
display->redisplay_texture_width, display->redisplay_texture_height);
|
|
}
|
|
//default opengl scene
|
|
else {
|
|
|
|
#ifndef OPENGL_ES2
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->redisplay_texture);
|
|
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
|
|
|
glBegin (GL_QUADS);
|
|
glTexCoord2i (display->redisplay_texture_width, 0);
|
|
glVertex2f (1.0f, 1.0f);
|
|
glTexCoord2i (0, 0);
|
|
glVertex2f (-1.0f, 1.0f);
|
|
glTexCoord2i (0, display->redisplay_texture_height);
|
|
glVertex2f (-1.0f, -1.0f);
|
|
glTexCoord2i (display->redisplay_texture_width,
|
|
display->redisplay_texture_height);
|
|
glVertex2f (1.0f, -1.0f);
|
|
glEnd ();
|
|
|
|
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
|
|
|
#else //OPENGL_ES2
|
|
|
|
const GLfloat vVertices[] = { 1.0f, 1.0f, 0.0f,
|
|
1.0f, 0.0f,
|
|
-1.0f, 1.0f, 0.0f,
|
|
0.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f,
|
|
0.0f, 1.0f,
|
|
1.0f, -1.0f, 0.0f,
|
|
1.0f, 1.0f
|
|
};
|
|
|
|
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
|
|
glClear (GL_COLOR_BUFFER_BIT);
|
|
|
|
gst_gl_shader_use (display->redisplay_shader);
|
|
|
|
//Load the vertex position
|
|
glVertexAttribPointer (display->redisplay_attr_position_loc, 3, GL_FLOAT,
|
|
GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
|
|
//Load the texture coordinate
|
|
glVertexAttribPointer (display->redisplay_attr_texture_loc, 2, GL_FLOAT,
|
|
GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->redisplay_attr_position_loc);
|
|
glEnableVertexAttribArray (display->redisplay_attr_texture_loc);
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindTexture (GL_TEXTURE_2D, display->redisplay_texture);
|
|
gst_gl_shader_set_uniform_1i (display->redisplay_shader, "s_texture", 0);
|
|
|
|
glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
#endif
|
|
|
|
} //end default opengl scene
|
|
}
|
|
|
|
|
|
void
|
|
gst_gl_display_on_close (GstGLDisplay * display)
|
|
{
|
|
GST_INFO ("on close");
|
|
|
|
display->isAlive = FALSE;
|
|
}
|
|
|
|
|
|
/* Generate a texture if no one is available in the pool
|
|
* Called in the gl thread */
|
|
void
|
|
gst_gl_display_glgen_texture (GstGLDisplay * display, GLuint * pTexture,
|
|
GLint width, GLint height)
|
|
{
|
|
glGenTextures (1, pTexture);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, *pTexture);
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
else
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_YCBCR_MESA, width,
|
|
height, 0, GL_YCBCR_MESA, GL_UNSIGNED_SHORT_8_8_MESA, NULL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
GST_LOG ("generated texture id:%d", *pTexture);
|
|
}
|
|
|
|
|
|
/* Delete a texture, actually the texture is just added to the pool
|
|
* Called in the gl thread */
|
|
void
|
|
gst_gl_display_gldel_texture (GstGLDisplay * display, GLuint * pTexture,
|
|
GLint width, GLint height)
|
|
{
|
|
//Each existing texture is destroyed only when the pool is destroyed
|
|
//The pool of textures is deleted in the GstGLDisplay destructor
|
|
|
|
GQueue *sub_texture_pool = NULL;
|
|
GstGLDisplayTex *tex = NULL;
|
|
|
|
//make a unique key from w and h
|
|
//the key cannot be w*h because (4*6 = 6*4 = 2*12 = 12*2)
|
|
guint key = (gint) width;
|
|
key <<= 16;
|
|
key |= (gint) height;
|
|
sub_texture_pool =
|
|
g_hash_table_lookup (display->texture_pool, GUINT_TO_POINTER (key));
|
|
|
|
//if the size is known
|
|
if (!sub_texture_pool) {
|
|
sub_texture_pool = g_queue_new ();
|
|
g_hash_table_insert (display->texture_pool, GUINT_TO_POINTER (key),
|
|
sub_texture_pool);
|
|
|
|
GST_INFO ("one more sub texture pool inserted: %d ", key);
|
|
GST_INFO ("nb sub texture pools: %d",
|
|
g_hash_table_size (display->texture_pool));
|
|
}
|
|
//contruct a sub texture pool element
|
|
tex = g_new0 (GstGLDisplayTex, 1);
|
|
tex->texture = *pTexture;
|
|
*pTexture = 0;
|
|
|
|
//add tex to the pool, it makes texture allocation reusable
|
|
g_queue_push_tail (sub_texture_pool, tex);
|
|
GST_LOG ("texture id:%d added to the sub texture pool: %d",
|
|
tex->texture, key);
|
|
GST_LOG ("%d texture(s) in the sub texture pool: %d",
|
|
g_queue_get_length (sub_texture_pool), key);
|
|
}
|
|
|
|
|
|
/* call when a sub texture pool is removed from the texture pool (ghash table) */
|
|
gboolean
|
|
gst_gl_display_texture_pool_func_clean (gpointer key, gpointer value,
|
|
gpointer data)
|
|
{
|
|
GQueue *sub_texture_pool = (GQueue *) value;
|
|
|
|
while (g_queue_get_length (sub_texture_pool) > 0) {
|
|
GstGLDisplayTex *tex = g_queue_pop_head (sub_texture_pool);
|
|
GST_INFO ("trying to delete texture id: %d deleted", tex->texture);
|
|
glDeleteTextures (1, &tex->texture);
|
|
GST_INFO ("texture id: %d deleted", tex->texture);
|
|
g_free (tex);
|
|
}
|
|
|
|
g_queue_free (sub_texture_pool);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* called in the gl thread */
|
|
void
|
|
gst_gl_display_check_framebuffer_status (void)
|
|
{
|
|
GLenum status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
|
|
|
|
switch (status) {
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
GST_ERROR ("GL_FRAMEBUFFER_UNSUPPORTED");
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
|
|
GST_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
|
|
GST_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
|
break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
|
GST_ERROR ("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
|
|
break;
|
|
|
|
default:
|
|
GST_ERROR ("General FBO error");
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//--------------------- END PRIVATE -------------------------
|
|
//------------------------------------------------------------
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//---------------------- BEGIN PUBLIC ------------------------
|
|
//------------------------------------------------------------
|
|
|
|
|
|
/* Called by the first gl element of a video/x-raw-gl flow */
|
|
GstGLDisplay *
|
|
gst_gl_display_new (void)
|
|
{
|
|
return g_object_new (GST_TYPE_GL_DISPLAY, NULL);
|
|
}
|
|
|
|
|
|
/* Create an opengl context (one context for one GstGLDisplay) */
|
|
void
|
|
gst_gl_display_create_context (GstGLDisplay * display, gulong external_gl_context)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
|
|
if (!display->gl_window) {
|
|
display->external_gl_context = external_gl_context;
|
|
|
|
display->gl_thread = g_thread_create (
|
|
(GThreadFunc) gst_gl_display_thread_create_context, display, TRUE, NULL);
|
|
|
|
g_cond_wait (display->cond_create_context, display->mutex);
|
|
|
|
GST_INFO ("gl thread created");
|
|
}
|
|
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the glimagesink element */
|
|
gboolean
|
|
gst_gl_display_redisplay (GstGLDisplay * display, GLuint texture, gint width,
|
|
gint height, gboolean keep_aspect_ratio)
|
|
{
|
|
gboolean isAlive = TRUE;
|
|
|
|
gst_gl_display_lock (display);
|
|
isAlive = display->isAlive;
|
|
if (isAlive) {
|
|
|
|
#ifdef OPENGL_ES2
|
|
if (!display->redisplay_shader) {
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_init_redisplay), display);
|
|
}
|
|
#endif
|
|
|
|
if (texture) {
|
|
display->redisplay_texture = texture;
|
|
display->redisplay_texture_width = width;
|
|
display->redisplay_texture_height = height;
|
|
}
|
|
display->keep_aspect_ratio = keep_aspect_ratio;
|
|
if (display->gl_window)
|
|
gst_gl_window_draw (display->gl_window, width, height);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
|
|
return isAlive;
|
|
}
|
|
|
|
void
|
|
gst_gl_display_thread_add (GstGLDisplay * display,
|
|
GstGLDisplayThreadFunc func, gpointer data)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->data = data;
|
|
display->generic_callback = func;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_run_generic), display);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
/* Called by gst_gl_buffer_new */
|
|
void
|
|
gst_gl_display_gen_texture (GstGLDisplay * display, GLuint * pTexture,
|
|
GLint width, GLint height)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
|
|
if (display->isAlive) {
|
|
GQueue *sub_texture_pool = NULL;
|
|
|
|
//make a unique key from w and h
|
|
//the key cannot be w*h because (4*6 = 6*4 = 2*12 = 12*2)
|
|
guint key = (gint) width;
|
|
key <<= 16;
|
|
key |= (gint) height;
|
|
sub_texture_pool =
|
|
g_hash_table_lookup (display->texture_pool, GUINT_TO_POINTER (key));
|
|
|
|
//if there is a sub texture pool associated to the given key
|
|
if (sub_texture_pool && g_queue_get_length (sub_texture_pool) > 0) {
|
|
//a texture is available in the pool
|
|
GstGLDisplayTex *tex = g_queue_pop_head (sub_texture_pool);
|
|
*pTexture = tex->texture;
|
|
g_free (tex);
|
|
GST_LOG ("get texture id:%d from the sub texture pool: %d",
|
|
*pTexture, key);
|
|
} else {
|
|
//only in this case we want to ask a texture from the gl thread
|
|
display->gen_texture_width = width;
|
|
display->gen_texture_height = height;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_gen_texture), display);
|
|
*pTexture = display->gen_texture;
|
|
}
|
|
|
|
} else
|
|
*pTexture = 0;
|
|
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by gst_gl_buffer_finalize */
|
|
void
|
|
gst_gl_display_del_texture (GstGLDisplay * display, GLuint texture, GLint width,
|
|
GLint height)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
if (texture) {
|
|
gst_gl_display_gldel_texture (display, &texture, width, height);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the first gl element of a video/x-raw-gl flow */
|
|
void
|
|
gst_gl_display_init_upload (GstGLDisplay * display, GstVideoFormat video_format,
|
|
guint gl_width, guint gl_height, gint video_width, gint video_height)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->upload_video_format = video_format;
|
|
display->upload_width = gl_width;
|
|
display->upload_height = gl_height;
|
|
display->upload_data_width = video_width;
|
|
display->upload_data_height = video_height;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_init_upload), display);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the first gl element of a video/x-raw-gl flow */
|
|
gboolean
|
|
gst_gl_display_do_upload (GstGLDisplay * display, GLuint texture,
|
|
gint data_width, gint data_height, gpointer data)
|
|
{
|
|
gboolean isAlive = TRUE;
|
|
|
|
gst_gl_display_lock (display);
|
|
isAlive = display->isAlive;
|
|
if (isAlive) {
|
|
display->upload_outtex = texture;
|
|
display->upload_data_width = data_width;
|
|
display->upload_data_height = data_height;
|
|
display->upload_data = data;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_do_upload), display);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
|
|
return isAlive;
|
|
}
|
|
|
|
|
|
/* Called by the gldownload and glcolorscale element */
|
|
void
|
|
gst_gl_display_init_download (GstGLDisplay * display,
|
|
GstVideoFormat video_format, gint width, gint height)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->download_video_format = video_format;
|
|
display->download_width = width;
|
|
display->download_height = height;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_init_download), display);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the gldownload and glcolorscale element */
|
|
gboolean
|
|
gst_gl_display_do_download (GstGLDisplay * display, GLuint texture,
|
|
gint width, gint height, gpointer data)
|
|
{
|
|
gboolean isAlive = TRUE;
|
|
|
|
gst_gl_display_lock (display);
|
|
isAlive = display->isAlive;
|
|
if (isAlive) {
|
|
//data size is aocciated to the glcontext size
|
|
display->download_data = data;
|
|
display->ouput_texture = texture;
|
|
display->ouput_texture_width = width;
|
|
display->ouput_texture_height = height;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_do_download), display);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
|
|
return isAlive;
|
|
}
|
|
|
|
|
|
/* Called by gltestsrc and glfilter */
|
|
void
|
|
gst_gl_display_gen_fbo (GstGLDisplay * display, gint width, gint height,
|
|
GLuint * fbo, GLuint * depthbuffer)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
if (display->isAlive) {
|
|
display->gen_fbo_width = width;
|
|
display->gen_fbo_height = height;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_gen_fbo), display);
|
|
*fbo = display->generated_fbo;
|
|
*depthbuffer = display->generated_depth_buffer;
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by glfilter */
|
|
/* this function really has to be simplified... do we really need to
|
|
set projection this way? Wouldn't be better a set_projection
|
|
separate call? or just make glut functions available out of
|
|
gst-libs and call it if needed on drawcallback? -- Filippo */
|
|
/* GLCB too.. I think that only needed parameters should be
|
|
* GstGLDisplay *display and gpointer data, or just gpointer data */
|
|
/* ..everything here has to be simplified! */
|
|
gboolean
|
|
gst_gl_display_use_fbo (GstGLDisplay * display, gint texture_fbo_width,
|
|
gint texture_fbo_height, GLuint fbo, GLuint depth_buffer,
|
|
GLuint texture_fbo, GLCB cb, gint input_texture_width,
|
|
gint input_texture_height, GLuint input_texture, gdouble proj_param1,
|
|
gdouble proj_param2, gdouble proj_param3, gdouble proj_param4,
|
|
GstGLDisplayProjection projection, gpointer * stuff)
|
|
{
|
|
gboolean isAlive = TRUE;
|
|
|
|
gst_gl_display_lock (display);
|
|
isAlive = display->isAlive;
|
|
if (isAlive) {
|
|
display->use_fbo = fbo;
|
|
display->use_depth_buffer = depth_buffer;
|
|
display->use_fbo_texture = texture_fbo;
|
|
display->use_fbo_width = texture_fbo_width;
|
|
display->use_fbo_height = texture_fbo_height;
|
|
display->use_fbo_scene_cb = cb;
|
|
display->use_fbo_proj_param1 = proj_param1;
|
|
display->use_fbo_proj_param2 = proj_param2;
|
|
display->use_fbo_proj_param3 = proj_param3;
|
|
display->use_fbo_proj_param4 = proj_param4;
|
|
display->use_fbo_projection = projection;
|
|
display->use_fbo_stuff = stuff;
|
|
display->input_texture_width = input_texture_width;
|
|
display->input_texture_height = input_texture_height;
|
|
display->input_texture = input_texture;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_use_fbo), display);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
|
|
return isAlive;
|
|
}
|
|
|
|
gboolean
|
|
gst_gl_display_use_fbo_v2 (GstGLDisplay * display, gint texture_fbo_width,
|
|
gint texture_fbo_height, GLuint fbo, GLuint depth_buffer,
|
|
GLuint texture_fbo, GLCB_V2 cb, gpointer * stuff)
|
|
{
|
|
gboolean isAlive = TRUE;
|
|
|
|
gst_gl_display_lock (display);
|
|
isAlive = display->isAlive;
|
|
if (isAlive) {
|
|
display->use_fbo = fbo;
|
|
display->use_depth_buffer = depth_buffer;
|
|
display->use_fbo_texture = texture_fbo;
|
|
display->use_fbo_width = texture_fbo_width;
|
|
display->use_fbo_height = texture_fbo_height;
|
|
display->use_fbo_scene_cb_v2 = cb;
|
|
display->use_fbo_stuff = stuff;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_use_fbo_v2), display);
|
|
}
|
|
gst_gl_display_unlock (display);
|
|
|
|
return isAlive;
|
|
}
|
|
|
|
/* Called by gltestsrc and glfilter */
|
|
void
|
|
gst_gl_display_del_fbo (GstGLDisplay * display, GLuint fbo, GLuint depth_buffer)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->del_fbo = fbo;
|
|
display->del_depth_buffer = depth_buffer;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_del_fbo), display);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by glfilter */
|
|
void
|
|
gst_gl_display_gen_shader (GstGLDisplay * display,
|
|
const gchar * shader_vertex_source,
|
|
const gchar * shader_fragment_source, GstGLShader ** shader)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->gen_shader_vertex_source = shader_vertex_source;
|
|
display->gen_shader_fragment_source = shader_fragment_source;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_gen_shader), display);
|
|
if (shader)
|
|
*shader = display->gen_shader;
|
|
display->gen_shader = NULL;
|
|
display->gen_shader_vertex_source = NULL;
|
|
display->gen_shader_fragment_source = NULL;
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by glfilter */
|
|
void
|
|
gst_gl_display_del_shader (GstGLDisplay * display, GstGLShader * shader)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->del_shader = shader;
|
|
gst_gl_window_send_message (display->gl_window,
|
|
GST_GL_WINDOW_CB (gst_gl_display_thread_del_shader), display);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the glimagesink */
|
|
void
|
|
gst_gl_display_set_window_id (GstGLDisplay * display, gulong window_id)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
gst_gl_window_set_external_window_id (display->gl_window, window_id);
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the glimagesink */
|
|
void
|
|
gst_gl_display_set_client_reshape_callback (GstGLDisplay * display, CRCB cb)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->clientReshapeCallback = cb;
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
/* Called by the glimagesink */
|
|
void
|
|
gst_gl_display_set_client_draw_callback (GstGLDisplay * display, CDCB cb)
|
|
{
|
|
gst_gl_display_lock (display);
|
|
display->clientDrawCallback = cb;
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
gulong
|
|
gst_gl_display_get_internal_gl_context (GstGLDisplay * display)
|
|
{
|
|
gulong external_gl_context = 0;
|
|
gst_gl_display_lock (display);
|
|
external_gl_context =
|
|
gst_gl_window_get_internal_gl_context (display->gl_window);
|
|
gst_gl_display_unlock (display);
|
|
return external_gl_context;
|
|
}
|
|
|
|
void
|
|
gst_gl_display_activate_gl_context (GstGLDisplay * display, gboolean activate)
|
|
{
|
|
if (!activate)
|
|
gst_gl_display_lock (display);
|
|
gst_gl_window_activate_gl_context (display->gl_window, activate);
|
|
if (activate)
|
|
gst_gl_display_unlock (display);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
//------------------------ END PUBLIC ------------------------
|
|
//------------------------------------------------------------
|
|
|
|
/* called by gst_gl_display_thread_init_upload (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_init_upload_fbo (GstGLDisplay * display)
|
|
{
|
|
//Frame buffer object is a requirement for every cases
|
|
if (GLEW_EXT_framebuffer_object) {
|
|
//a texture must be attached to the FBO
|
|
GLuint fake_texture = 0;
|
|
|
|
GST_INFO ("Context, EXT_framebuffer_object supported: yes");
|
|
|
|
//-- init intput frame buffer object (video -> GL)
|
|
|
|
//setup FBO
|
|
glGenFramebuffersEXT (1, &display->upload_fbo);
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->upload_fbo);
|
|
|
|
//setup the render buffer for depth
|
|
glGenRenderbuffersEXT (1, &display->upload_depth_buffer);
|
|
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, display->upload_depth_buffer);
|
|
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
|
|
display->upload_width, display->upload_height);
|
|
|
|
//a fake texture is attached to the upload FBO (cannot init without it)
|
|
glGenTextures (1, &fake_texture);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, fake_texture);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
display->upload_width, display->upload_height, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
//attach the texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, fake_texture, 0);
|
|
|
|
//attach the depth render buffer to the FBO
|
|
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
|
GL_RENDERBUFFER_EXT, display->upload_depth_buffer);
|
|
|
|
gst_gl_display_check_framebuffer_status ();
|
|
|
|
g_assert (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) ==
|
|
GL_FRAMEBUFFER_COMPLETE_EXT);
|
|
|
|
//unbind the FBO
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
glDeleteTextures (1, &fake_texture);
|
|
|
|
//alloc texture (related to upload) memory only on time
|
|
gst_gl_display_thread_do_upload_make (display);
|
|
} else {
|
|
//turn off the pipeline because Frame buffer object is a not present
|
|
GST_WARNING ("Context, EXT_framebuffer_object supported: no");
|
|
display->isAlive = FALSE;
|
|
}
|
|
}
|
|
|
|
/* called by gst_gl_display_thread_do_upload (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_do_upload_make (GstGLDisplay * display)
|
|
{
|
|
gint width = display->upload_data_width;
|
|
gint height = display->upload_data_height;
|
|
|
|
glGenTextures (1, &display->upload_intex);
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA,
|
|
width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB,
|
|
width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA,
|
|
width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, NULL);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE_ALPHA,
|
|
width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
|
|
glGenTextures (1, &display->upload_intex_u);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_YCBCR_MESA, width,
|
|
height, 0, GL_YCBCR_MESA, GL_UNSIGNED_SHORT_8_8_MESA, NULL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE_ALPHA,
|
|
width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
|
|
glGenTextures (1, &display->upload_intex_u);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
|
|
width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_YCBCR_MESA, width,
|
|
height, 0, GL_YCBCR_MESA, GL_UNSIGNED_SHORT_8_8_MESA, NULL);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE,
|
|
width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glGenTextures (1, &display->upload_intex_u);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE,
|
|
GST_ROUND_UP_2 (width) / 2,
|
|
GST_ROUND_UP_2 (height) / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glGenTextures (1, &display->upload_intex_v);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_v);
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE,
|
|
GST_ROUND_UP_2 (width) / 2,
|
|
GST_ROUND_UP_2 (height) / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* called by gst_gl_display_thread_do_upload (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_do_upload_fill (GstGLDisplay * display)
|
|
{
|
|
gint width = display->upload_data_width;
|
|
gint height = display->upload_data_height;
|
|
gpointer data = display->upload_data;
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
//color space conversion is not needed
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
else
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_outtex);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
if (display->upload_width != display->upload_data_width ||
|
|
display->upload_height != display->upload_data_height)
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
else
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_outtex);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_BGR, GL_UNSIGNED_BYTE, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_BGRA, GL_UNSIGNED_BYTE, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0,
|
|
GST_ROUND_UP_2 (width) / 2, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_YCBCR_MESA, GL_UNSIGNED_SHORT_8_8_REV_MESA, data);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0,
|
|
GST_ROUND_UP_2 (width) / 2, height,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_YCBCR_MESA, GL_UNSIGNED_SHORT_8_8_MESA, data);
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
{
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height,
|
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0,
|
|
GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2,
|
|
GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
(guint8 *) data +
|
|
gst_video_format_get_component_offset (display->upload_video_format,
|
|
1, width, height));
|
|
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_v);
|
|
glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0,
|
|
GST_ROUND_UP_2 (width) / 2, GST_ROUND_UP_2 (height) / 2,
|
|
GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
(guint8 *) data +
|
|
gst_video_format_get_component_offset (display->upload_video_format,
|
|
2, width, height));
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
//make sure no texture is in use in our opengl context
|
|
//in case we want to use the upload texture in an other opengl context
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, 0);
|
|
}
|
|
|
|
|
|
/* called by gst_gl_display_thread_do_upload (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_do_upload_draw (GstGLDisplay * display)
|
|
{
|
|
|
|
#ifdef OPENGL_ES2
|
|
GLint viewport_dim[4];
|
|
|
|
const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f,
|
|
1.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f,
|
|
0.0f, .0f,
|
|
-1.0f, 1.0f, 0.0f,
|
|
0.0f, 1.0f,
|
|
1.0f, 1.0f, 0.0f,
|
|
1.0f, 1.0f
|
|
};
|
|
|
|
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->upload_fbo);
|
|
|
|
//setup a texture to render to
|
|
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_outtex);
|
|
|
|
//attach the texture to the FBO to renderer to
|
|
glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_RECTANGLE_ARB, display->upload_outtex, 0);
|
|
|
|
if (GLEW_ARB_fragment_shader)
|
|
gst_gl_shader_use (NULL);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glPushAttrib (GL_VIEWPORT_BIT);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
gluOrtho2D (0.0, display->upload_width, 0.0, display->upload_height);
|
|
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
#else // OPENGL_ES2
|
|
glGetIntegerv (GL_VIEWPORT, viewport_dim);
|
|
#endif
|
|
|
|
glViewport (0, 0, display->upload_width, display->upload_height);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
{
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_upload_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_upload_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_upload_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_upload_attr_texture_loc);
|
|
#endif
|
|
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
#ifndef OPENGL_ES2
|
|
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
{
|
|
switch (display->upload_colorspace_conversion) {
|
|
case GST_GL_DISPLAY_CONVERSION_GLSL:
|
|
case GST_GL_DISPLAY_CONVERSION_MATRIX:
|
|
{
|
|
GstGLShader *shader_upload_YUY2_UYVY = NULL;
|
|
|
|
switch (display->upload_video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
shader_upload_YUY2_UYVY = display->shader_upload_YUY2;
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
shader_upload_YUY2_UYVY = display->shader_upload_UYVY;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
gst_gl_shader_use (shader_upload_YUY2_UYVY);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_upload_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_upload_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_upload_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_upload_attr_texture_loc);
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE1_ARB);
|
|
gst_gl_shader_set_uniform_1i (shader_upload_YUY2_UYVY, "UVtex", 1);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (shader_upload_YUY2_UYVY, "Ytex", 0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
}
|
|
break;
|
|
case GST_GL_DISPLAY_CONVERSION_MESA:
|
|
{
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#endif
|
|
|
|
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
#ifndef OPENGL_ES2
|
|
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
{
|
|
gst_gl_shader_use (display->shader_upload_I420_YV12);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_upload_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_upload_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_upload_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_upload_attr_texture_loc);
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE1_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_upload_I420_YV12, "Utex",
|
|
1);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_u);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
glActiveTextureARB (GL_TEXTURE2_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_upload_I420_YV12, "Vtex",
|
|
2);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex_v);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_upload_I420_YV12, "Ytex",
|
|
0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
}
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
{
|
|
gst_gl_shader_use (display->shader_upload_AYUV);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_upload_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_upload_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_upload_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_upload_attr_texture_loc);
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_upload_AYUV, "tex", 0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->upload_intex);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
|
|
GL_LINEAR);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
|
|
GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
|
|
GL_CLAMP_TO_EDGE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
|
|
} //end switch display->currentVideo_format
|
|
|
|
#ifndef OPENGL_ES2
|
|
glBegin (GL_QUADS);
|
|
glTexCoord2i (display->upload_data_width, 0);
|
|
glVertex2f (1.0f, -1.0f);
|
|
glTexCoord2i (0, 0);
|
|
glVertex2f (-1.0f, -1.0f);
|
|
glTexCoord2i (0, display->upload_data_height);
|
|
glVertex2f (-1.0f, 1.0f);
|
|
glTexCoord2i (display->upload_data_width, display->upload_data_height);
|
|
glVertex2f (1.0f, 1.0f);
|
|
glEnd ();
|
|
|
|
glDrawBuffer (GL_NONE);
|
|
#else //OPENGL_ES2
|
|
glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
#endif
|
|
|
|
//we are done with the shader
|
|
if (display->upload_colorspace_conversion == GST_GL_DISPLAY_CONVERSION_GLSL)
|
|
glUseProgramObjectARB (0);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPopMatrix ();
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPopMatrix ();
|
|
glPopAttrib ();
|
|
#else
|
|
glViewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
|
|
viewport_dim[3]);
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
gst_gl_display_check_framebuffer_status ();
|
|
}
|
|
|
|
|
|
/* called by gst_gl_display_thread_do_download (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_do_download_draw_rgb (GstGLDisplay * display)
|
|
{
|
|
GstVideoFormat video_format = display->download_video_format;
|
|
gpointer data = display->download_data;
|
|
|
|
#ifndef OPENGL_ES2
|
|
glEnable (GL_TEXTURE_RECTANGLE_ARB);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->ouput_texture);
|
|
#else
|
|
gint width = display->ouput_texture_width;
|
|
gint height = display->ouput_texture_height;
|
|
|
|
const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f,
|
|
1.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f,
|
|
0.0f, 0.0f,
|
|
-1.0f, 1.0f, 0.0f,
|
|
0.0f, 1.0f,
|
|
1.0f, 1.0f, 0.0f,
|
|
1.0f, 1.0f
|
|
};
|
|
|
|
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
|
|
glViewport (0, 0, width, height);
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
gst_gl_shader_use (display->shader_download_RGB);
|
|
|
|
glVertexAttribPointer (display->shader_download_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_download_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_download_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_download_attr_texture_loc);
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_download_RGB, "s_texture", 0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->ouput_texture);
|
|
|
|
glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
|
|
glUseProgramObjectARB (0);
|
|
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
|
#endif
|
|
|
|
switch (video_format) {
|
|
case GST_VIDEO_FORMAT_RGBA:
|
|
case GST_VIDEO_FORMAT_RGBx:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
case GST_VIDEO_FORMAT_ARGB:
|
|
#ifndef OPENGL_ES2
|
|
glGetTexImage (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, data);
|
|
#else
|
|
glReadPixels (0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
#endif
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGRx:
|
|
case GST_VIDEO_FORMAT_BGRA:
|
|
case GST_VIDEO_FORMAT_xBGR:
|
|
case GST_VIDEO_FORMAT_ABGR:
|
|
#ifndef OPENGL_ES2
|
|
glGetTexImage (GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGRA,
|
|
GL_UNSIGNED_BYTE, data);
|
|
#endif
|
|
break;
|
|
case GST_VIDEO_FORMAT_RGB:
|
|
#ifndef OPENGL_ES2
|
|
glGetTexImage (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB,
|
|
GL_UNSIGNED_BYTE, data);
|
|
#else
|
|
glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
#endif
|
|
break;
|
|
case GST_VIDEO_FORMAT_BGR:
|
|
#ifndef OPENGL_ES2
|
|
glGetTexImage (GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGR,
|
|
GL_UNSIGNED_BYTE, data);
|
|
#endif
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
/* called by gst_gl_display_thread_do_download (in the gl thread) */
|
|
void
|
|
gst_gl_display_thread_do_download_draw_yuv (GstGLDisplay * display)
|
|
{
|
|
gint width = display->download_width;
|
|
gint height = display->download_height;
|
|
GstVideoFormat video_format = display->download_video_format;
|
|
gpointer data = display->download_data;
|
|
|
|
#ifdef OPENGL_ES2
|
|
GLint viewport_dim[4];
|
|
|
|
const GLfloat vVertices[] = { 1.0f, -1.0f, 0.0f,
|
|
1.0f, 0.0f,
|
|
-1.0f, -1.0f, 0.0f,
|
|
0.0f, .0f,
|
|
-1.0f, 1.0f, 0.0f,
|
|
0.0f, 1.0f,
|
|
1.0f, 1.0f, 0.0f,
|
|
1.0f, 1.0f
|
|
};
|
|
|
|
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->download_fbo);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glPushAttrib (GL_VIEWPORT_BIT);
|
|
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
gluOrtho2D (0.0, width, 0.0, height);
|
|
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPushMatrix ();
|
|
glLoadIdentity ();
|
|
#else // OPENGL_ES2
|
|
glGetIntegerv (GL_VIEWPORT, viewport_dim);
|
|
#endif
|
|
|
|
glViewport (0, 0, width, height);
|
|
|
|
switch (video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
{
|
|
GstGLShader *shader_download_YUY2_UYVY = NULL;
|
|
|
|
switch (video_format) {
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
shader_download_YUY2_UYVY = display->shader_download_YUY2;
|
|
break;
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
shader_download_YUY2_UYVY = display->shader_download_UYVY;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
gst_gl_shader_use (shader_download_YUY2_UYVY);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_download_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_download_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_download_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_download_attr_texture_loc);
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (shader_download_YUY2_UYVY, "tex", 0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->ouput_texture);
|
|
}
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
{
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffers (3, display->multipleRT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
gst_gl_shader_use (display->shader_download_I420_YV12);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_download_I420_YV12, "tex",
|
|
0);
|
|
gst_gl_shader_set_uniform_1f (display->shader_download_I420_YV12, "w",
|
|
(gfloat) display->ouput_texture_width);
|
|
gst_gl_shader_set_uniform_1f (display->shader_download_I420_YV12, "h",
|
|
(gfloat) display->ouput_texture_height);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->ouput_texture);
|
|
}
|
|
break;
|
|
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
{
|
|
#ifndef OPENGL_ES2
|
|
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
gst_gl_shader_use (display->shader_download_AYUV);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glLoadIdentity ();
|
|
#else
|
|
glVertexAttribPointer (display->shader_download_attr_position_loc, 3,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), vVertices);
|
|
glVertexAttribPointer (display->shader_download_attr_texture_loc, 2,
|
|
GL_FLOAT, GL_FALSE, 5 * sizeof (GLfloat), &vVertices[3]);
|
|
|
|
glEnableVertexAttribArray (display->shader_download_attr_position_loc);
|
|
glEnableVertexAttribArray (display->shader_download_attr_texture_loc);
|
|
#endif
|
|
|
|
glActiveTextureARB (GL_TEXTURE0_ARB);
|
|
gst_gl_shader_set_uniform_1i (display->shader_download_AYUV, "tex", 0);
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, display->ouput_texture);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
|
|
} //end switch display->currentVideo_format
|
|
|
|
#ifndef OPENGL_ES2
|
|
glBegin (GL_QUADS);
|
|
glTexCoord2i (0, 0);
|
|
glVertex2f (-1.0f, -1.0f);
|
|
glTexCoord2i (width, 0);
|
|
glVertex2f (1.0f, -1.0f);
|
|
glTexCoord2i (width, height);
|
|
glVertex2f (1.0f, 1.0f);
|
|
glTexCoord2i (0, height);
|
|
glVertex2f (-1.0f, 1.0f);
|
|
glEnd ();
|
|
|
|
glDrawBuffer (GL_NONE);
|
|
#else //OPENGL_ES2
|
|
glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
#endif
|
|
|
|
//dot not check if GLSL is available
|
|
//because download yuv is not available
|
|
//without GLSL (whereas rgb is)
|
|
glUseProgramObjectARB (0);
|
|
|
|
glDisable (GL_TEXTURE_RECTANGLE_ARB);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glMatrixMode (GL_PROJECTION);
|
|
glPopMatrix ();
|
|
glMatrixMode (GL_MODELVIEW);
|
|
glPopMatrix ();
|
|
glPopAttrib ();
|
|
#else
|
|
glViewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
|
|
viewport_dim[3]);
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
gst_gl_display_check_framebuffer_status ();
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, display->download_fbo);
|
|
#ifndef OPENGL_ES2
|
|
glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
|
|
#endif
|
|
|
|
switch (video_format) {
|
|
case GST_VIDEO_FORMAT_AYUV:
|
|
case GST_VIDEO_FORMAT_xRGB:
|
|
glReadPixels (0, 0, width, height, GL_BGRA,
|
|
GL_UNSIGNED_INT_8_8_8_8, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_YUY2:
|
|
case GST_VIDEO_FORMAT_UYVY:
|
|
glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2, height, GL_BGRA,
|
|
GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
|
break;
|
|
case GST_VIDEO_FORMAT_I420:
|
|
case GST_VIDEO_FORMAT_YV12:
|
|
{
|
|
glReadPixels (0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
|
|
|
#ifndef OPENGL_ES2
|
|
glReadBuffer (GL_COLOR_ATTACHMENT1_EXT);
|
|
#endif
|
|
glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2,
|
|
GST_ROUND_UP_2 (height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
(guint8 *) data + gst_video_format_get_component_offset (video_format,
|
|
1, width, height));
|
|
#ifndef OPENGL_ES2
|
|
glReadBuffer (GL_COLOR_ATTACHMENT2_EXT);
|
|
#endif
|
|
glReadPixels (0, 0, GST_ROUND_UP_2 (width) / 2,
|
|
GST_ROUND_UP_2 (height) / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
(guint8 *) data + gst_video_format_get_component_offset (video_format,
|
|
2, width, height));
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
#ifndef OPENGL_ES2
|
|
glReadBuffer (GL_NONE);
|
|
#endif
|
|
|
|
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
|
|
gst_gl_display_check_framebuffer_status ();
|
|
}
|