gstreamer/gst-libs/gst/gl/gstgldisplay.c
Matthew Waters db236c510f [633/906] display: fail properly in context creation in order to minimize a race condition
previously, on context creation, when we failed to get a valid context
we would still atempt to run the window mainloop as well as setting an error
on the display. This would cause the calling element to finalize the display
and therefore attempt to quit the window mainloop.  However the mainloop
may not have started running.  Thus when the window mainloop ran it would
never a get a quit message and never end.
2017-12-09 19:31:26 +00:00

1715 lines
50 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include "gstgldisplay.h"
#include "gstgldownload.h"
#include "gstglmemory.h"
#ifndef GLEW_VERSION_MAJOR
#define GLEW_VERSION_MAJOR 4
#endif
#ifndef GLEW_VERSION_MINOR
#define GLEW_VERSION_MINOR 0
#endif
#define USING_OPENGL(display) (display->gl_api & GST_GL_API_OPENGL)
#define USING_OPENGL3(display) (display->gl_api & GST_GL_API_OPENGL3)
#define USING_GLES(display) (display->gl_api & GST_GL_API_GLES)
#define USING_GLES2(display) (display->gl_api & GST_GL_API_GLES2)
#define USING_GLES3(display) (display->gl_api & GST_GL_API_GLES3)
GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
#define GST_CAT_DEFAULT gst_gl_display_debug
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display");
G_DEFINE_TYPE_WITH_CODE (GstGLDisplay, gst_gl_display, G_TYPE_OBJECT,
DEBUG_INIT);
#define GST_GL_DISPLAY_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_DISPLAY, GstGLDisplayPrivate))
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);
#if HAVE_GLES2
void gst_gl_display_thread_init_redisplay (GstGLDisplay * display);
#endif
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_del_texture_thread (GstGLDisplay * display,
GLuint * pTexture);
void gst_gl_display_gen_texture_window_cb (GstGLDisplay * display);
#if HAVE_OPENGL
void _gen_fbo_opengl (GstGLDisplay * display);
void _use_fbo_opengl (GstGLDisplay * display);
void _use_fbo_v2_opengl (GstGLDisplay * display);
void _del_fbo_opengl (GstGLDisplay * display);
void _gen_shader_opengl (GstGLDisplay * display);
void _del_shader_opengl (GstGLDisplay * display);
#endif
#if HAVE_GLES2
void _gen_fbo_gles2 (GstGLDisplay * display);
void _use_fbo_gles2 (GstGLDisplay * display);
void _use_fbo_v2_gles2 (GstGLDisplay * display);
void _del_fbo_gles2 (GstGLDisplay * display);
void _gen_shader_gles2 (GstGLDisplay * display);
void _del_shader_gles2 (GstGLDisplay * display);
#endif
typedef struct _GstGLVtable
{
GstGLWindowCB gen_fbo;
GstGLWindowCB use_fbo;
GstGLWindowCB use_fbo_v2;
GstGLWindowCB del_fbo;
GstGLWindowCB gen_shader;
GstGLWindowCB del_shader;
} GstGLVtable;
struct _GstGLDisplayPrivate
{
GstGLVtable vtable;
};
/*------------------------------------------------------------
--------------------- For klass GstGLDisplay ---------------
----------------------------------------------------------*/
static void
gst_gl_display_class_init (GstGLDisplayClass * klass)
{
g_type_class_add_private (klass, sizeof (GstGLDisplayPrivate));
G_OBJECT_CLASS (klass)->finalize = gst_gl_display_finalize;
}
static void
gst_gl_display_init (GstGLDisplay * display)
{
display->priv = GST_GL_DISPLAY_GET_PRIVATE (display);
/* thread safe */
display->mutex = g_mutex_new ();
/* gl context */
display->gl_thread = NULL;
display->gl_window = NULL;
display->isAlive = TRUE;
/* 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;
#if HAVE_GLES2
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;
display->gen_texture_video_format = GST_VIDEO_FORMAT_UNKNOWN;
/* client callbacks */
display->clientReshapeCallback = NULL;
display->clientDrawCallback = NULL;
display->client_data = NULL;
display->colorspace_conversion = GST_GL_DISPLAY_CONVERSION_GLSL;
/* 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;
/* 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;
display->uploads = NULL;
display->downloads = NULL;
#if HAVE_GLES2
/* *INDENT-OFF* */
display->redisplay_vertex_shader_str_gles2 =
"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_gles2 =
"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";
/* *INDENT_ON* */
#endif
display->error_message = NULL;
gst_gl_memory_init ();
}
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 (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->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->client_data)
display->client_data = 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;
if (display->error_message) {
g_free (display->error_message);
display->error_message = NULL;
}
if (display->uploads) {
g_slist_free_full (display->uploads, g_object_unref);
display->uploads = NULL;
}
if (display->downloads) {
g_slist_free_full (display->downloads, g_object_unref);
display->downloads = NULL;
}
}
//------------------------------------------------------------
//------------------ BEGIN GL THREAD PROCS -------------------
//------------------------------------------------------------
/* Called in the gl thread */
void
gst_gl_display_set_error (GstGLDisplay * display, const char *format, ...)
{
va_list args;
if (display->error_message)
g_free (display->error_message);
va_start (args, format);
display->error_message = g_strdup_vprintf (format, args);
va_end (args);
GST_WARNING (display->error_message);
display->isAlive = FALSE;
}
#if HAVE_GLES2
static gboolean
_create_context_gles2 (GstGLDisplay * display, gint * gl_major, gint * gl_minor)
{
GLenum gl_err = GL_NO_ERROR;
if (glGetString (GL_VERSION))
GST_INFO ("GL_VERSION: %s", glGetString (GL_VERSION));
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)");
if (glGetString (GL_VENDOR))
GST_INFO ("GL_VENDOR: %s", glGetString (GL_VENDOR));
if (glGetString (GL_RENDERER))
GST_INFO ("GL_RENDERER: %s", glGetString (GL_RENDERER));
gl_err = glGetError ();
if (gl_err != GL_NO_ERROR) {
gst_gl_display_set_error (display, "glGetString error: 0x%x", gl_err);
}
if (!GL_ES_VERSION_2_0)
gst_gl_display_set_error (display, "OpenGL|ES >= 2.0 is required");
display->priv->vtable.gen_fbo = (GstGLWindowCB) _gen_fbo_gles2;
display->priv->vtable.use_fbo = (GstGLWindowCB) _use_fbo_gles2;
display->priv->vtable.use_fbo_v2 = (GstGLWindowCB) _use_fbo_v2_gles2;
display->priv->vtable.del_fbo = (GstGLWindowCB) _del_fbo_gles2;
display->priv->vtable.gen_shader = (GstGLWindowCB) _gen_shader_gles2;
display->priv->vtable.del_shader = (GstGLWindowCB) _del_shader_gles2;
*gl_major = 2;
*gl_minor = 0;
return TRUE;
}
#endif
#if HAVE_OPENGL
gboolean
_create_context_opengl (GstGLDisplay * display, gint * gl_major, gint * gl_minor)
{
GLenum err = GLEW_OK;
GLenum gl_err = GL_NO_ERROR;
GString *opengl_version = NULL;
if (glewInit () != GLEW_OK) {
gst_gl_display_set_error (display, "Failed to init GLEW: %s",
glewGetErrorString (err));
return TRUE;
}
/* OpenGL > 1.2.0 and Glew > 1.4.0 */
if (glGetString (GL_VERSION))
GST_INFO ("GL_VERSION: %s", glGetString (GL_VERSION));
GST_INFO ("GLEW_VERSION: %s", glewGetString (GLEW_VERSION));
if (glGetString (GL_SHADING_LANGUAGE_VERSION))
GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
glGetString (GL_SHADING_LANGUAGE_VERSION));
else
GST_WARNING ("Your driver does not support GLSL (OpenGL Shading Language)");
if (glGetString (GL_VENDOR))
GST_INFO ("GL_VENDOR: %s", glGetString (GL_VENDOR));
if (glGetString (GL_RENDERER))
GST_INFO ("GL_RENDERER: %s", glGetString (GL_RENDERER));
gl_err = glGetError ();
if (gl_err != GL_NO_ERROR) {
gst_gl_display_set_error (display, "glGetString error: 0x%x", gl_err);
}
if (glGetString (GL_VERSION)) {
opengl_version =
g_string_truncate (g_string_new ((gchar *) glGetString (GL_VERSION)), 3);
sscanf (opengl_version->str, "%d.%d", gl_major, gl_minor);
g_string_free (opengl_version, TRUE);
if ((*gl_major < 1) ||
(GLEW_VERSION_MAJOR < 1) ||
(*gl_major < 2 && *gl_major >= 1
&& *gl_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_gl_display_set_error (display,
"OpenGL >= 1.2.0 and Glew >= 1.4.0 is required");
}
}
display->priv->vtable.gen_fbo = (GstGLWindowCB) _gen_fbo_opengl;
display->priv->vtable.use_fbo = (GstGLWindowCB) _use_fbo_opengl;
display->priv->vtable.use_fbo_v2 = (GstGLWindowCB) _use_fbo_v2_opengl;
display->priv->vtable.del_fbo = (GstGLWindowCB) _del_fbo_opengl;
display->priv->vtable.gen_shader = (GstGLWindowCB) _gen_shader_opengl;
display->priv->vtable.del_shader = (GstGLWindowCB) _del_shader_opengl;
return TRUE;
}
#endif
GstGLAPI
_compiled_api (void)
{
GstGLAPI ret = GST_GL_API_NONE;
#if HAVE_OPENGL
ret |= GST_GL_API_OPENGL;
#endif
#if HAVE_GLES2
ret |= GST_GL_API_GLES2;
#endif
return ret;
}
gpointer
gst_gl_display_thread_create_context (GstGLDisplay * display)
{
gint gl_major = 0, gl_minor = 0;
gboolean ret = FALSE;
GError *error = NULL;
GstGLAPI compiled_api;
gchar *api_string;
gchar *compiled_api_s;
gst_gl_display_lock (display);
compiled_api = _compiled_api ();
display->gl_window =
gst_gl_window_new (compiled_api, display->external_gl_context, &error);
if (!display->gl_window || error) {
gst_gl_display_set_error (display, error ? error->message : "Failed to create gl window");
goto failure;
}
GST_INFO ("gl window created");
display->gl_api = gst_gl_window_get_gl_api (display->gl_window);
g_assert (display->gl_api != GST_GL_API_NONE && display->gl_api != GST_GL_API_ANY);
api_string = gst_gl_api_string (display->gl_api);
GST_INFO ("available GL APIs: %s", api_string);
compiled_api_s = gst_gl_api_string (compiled_api);
GST_INFO ("compiled api support: %s", compiled_api_s);
if ((compiled_api & display->gl_api) == GST_GL_API_NONE) {
gst_gl_display_set_error (display, "failed to create_context, window "
"could not provide correct api. compiled api supports:%s, window "
"supports:%s", compiled_api_s, api_string);
goto failure;
}
g_free (api_string);
g_free (compiled_api_s);
/* gl api specific code */
#if HAVE_OPENGL
if (!ret && USING_OPENGL(display))
ret = _create_context_opengl (display, &gl_major, &gl_minor);
#endif
#if HAVE_GLES2
if (!ret && USING_GLES2(display))
ret = _create_context_gles2 (display, &gl_major, &gl_minor);
#endif
if (!ret || !gl_major) {
gst_gl_display_set_error (display, "failed to create context, unknown reason");
goto failure;
}
/* setup callbacks */
gst_gl_window_set_resize_callback (display->gl_window,
GST_GL_WINDOW_RESIZE_CB (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 (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;
failure:
{
if (display->gl_window) {
g_object_unref (display->gl_window);
display->gl_window = NULL;
}
g_cond_signal (display->cond_create_context);
gst_gl_display_unlock (display);
return NULL;
}
}
/* Called in the gl thread */
void
gst_gl_display_thread_destroy_context (GstGLDisplay * display)
{
#if HAVE_GLES2
if (display->redisplay_shader) {
g_object_unref (G_OBJECT (display->redisplay_shader));
display->redisplay_shader = NULL;
}
#endif
GST_INFO ("Context destroyed");
}
void
gst_gl_display_thread_run_generic (GstGLDisplay * display)
{
GST_TRACE ("running function:%p data:%p",
display->generic_callback, display->data);
display->generic_callback (display, display->data);
}
#if HAVE_GLES2
/* Called in the gl thread */
void
gst_gl_display_thread_init_redisplay (GstGLDisplay * display)
{
GError *error = NULL;
display->redisplay_shader = gst_gl_shader_new (display);
gst_gl_shader_set_vertex_source (display->redisplay_shader,
display->redisplay_vertex_shader_str_gles2);
gst_gl_shader_set_fragment_source (display->redisplay_shader,
display->redisplay_fragment_shader_str_gles2);
gst_gl_shader_compile (display->redisplay_shader, &error);
if (error) {
gst_gl_display_set_error (display, "%s", error->message);
g_error_free (error);
error = NULL;
gst_gl_shader_use (NULL);
} 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
#if HAVE_OPENGL
void
_gen_fbo_opengl (GstGLDisplay * display)
{
/* a texture must be attached to the FBO */
GLuint fake_texture = 0;
GST_TRACE ("creating FBO dimensions:%ux%u", display->gen_fbo_width,
display->gen_fbo_height);
/* -- generate frame buffer object */
if (!GLEW_EXT_framebuffer_object) {
gst_gl_display_set_error (display,
"Context, EXT_framebuffer_object not supported");
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);
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
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);
if (USING_OPENGL(display))
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, display->generated_depth_buffer);
if (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT)
gst_gl_display_set_error (display, "GL framebuffer status incomplete");
/* unbind the FBO */
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
glDeleteTextures (1, &fake_texture);
}
#endif
#if HAVE_GLES2
void
_gen_fbo_gles2 (GstGLDisplay * display)
{
/* a texture must be attached to the FBO */
GLuint fake_texture = 0;
GST_TRACE ("creating FBO dimensions:%ux%u", display->gen_fbo_width,
display->gen_fbo_height);
/* -- generate frame buffer object */
if (!GLEW_EXT_framebuffer_object) {
gst_gl_display_set_error (display,
"Context, EXT_framebuffer_object not supported");
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_COMPONENT16,
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);
if (glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT)
gst_gl_display_set_error (display, "GL framebuffer status incomplete");
/* unbind the FBO */
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
glDeleteTextures (1, &fake_texture);
}
#endif
/* Called in the gl thread */
#if HAVE_OPENGL
void
_use_fbo_opengl (GstGLDisplay * display)
{
GST_TRACE ("Binding v1 FBO %u dimensions:%ux%u with texture:%u "
"dimensions:%ux%u", display->use_fbo, display->use_fbo_width,
display->use_fbo_height, display->use_fbo_texture,
display->input_texture_width, display->input_texture_height);
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);
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:
gst_gl_display_set_error (display, "Unknow fbo projection %d",
display->use_fbo_projection);
}
glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
glViewport (0, 0, display->use_fbo_width, display->use_fbo_height);
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
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);
glDrawBuffer (GL_NONE);
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
glPopMatrix ();
glPopAttrib ();
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
}
#endif
#if HAVE_GLES2
void
_use_fbo_gles2 (GstGLDisplay * display)
{
GLint viewport_dim[4];
GST_TRACE ("Binding v1 FBO %u dimensions:%ux%u with texture:%u "
"dimensions:%ux%u", display->use_fbo, display->use_fbo_width,
display->use_fbo_height, display->use_fbo_texture,
display->input_texture_width, display->input_texture_height);
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);
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);
glViewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
viewport_dim[3]);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
}
#endif
/* Called in a gl thread
* Need full shader support */
#if HAVE_OPENGL
void
_use_fbo_v2_opengl (GstGLDisplay * display)
{
GLint viewport_dim[4];
GST_TRACE ("Binding v2 FBO %u dimensions:%ux%u with texture:%u ",
display->use_fbo, display->use_fbo_width,
display->use_fbo_height, display->use_fbo_texture);
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);
glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
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);
glDrawBuffer (GL_NONE);
glViewport (viewport_dim[0], viewport_dim[1],
viewport_dim[2], viewport_dim[3]);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
}
#endif
#if HAVE_GLES2
void
_use_fbo_v2_gles2 (GstGLDisplay * display)
{
GLint viewport_dim[4];
GST_TRACE ("Binding v2 FBO %u dimensions:%ux%u with texture:%u ",
display->use_fbo, display->use_fbo_width,
display->use_fbo_height, display->use_fbo_texture);
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);
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);
glViewport (viewport_dim[0], viewport_dim[1],
viewport_dim[2], viewport_dim[3]);
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
}
#endif
/* Called in the gl thread */
#if HAVE_OPENGL
void
_del_fbo_opengl (GstGLDisplay * display)
{
GST_TRACE ("Deleting FBO %u", display->del_fbo);
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;
}
}
#endif
#if HAVE_GLES2
void
_del_fbo_gles2 (GstGLDisplay * display)
{
GST_TRACE ("Deleting FBO %u", display->del_fbo);
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;
}
}
#endif
/* Called in the gl thread */
void
_gen_shader_opengl (GstGLDisplay * display)
{
GST_TRACE ("Generating shader %" GST_PTR_FORMAT, display->gen_shader);
if (GLEW_ARB_fragment_shader) {
if (display->gen_shader_vertex_source ||
display->gen_shader_fragment_source) {
GError *error = NULL;
display->gen_shader = gst_gl_shader_new (display);
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_gl_display_set_error (display, "%s", error->message);
g_error_free (error);
error = NULL;
gst_gl_shader_use (NULL);
g_object_unref (G_OBJECT (display->gen_shader));
display->gen_shader = NULL;
}
}
} else {
gst_gl_display_set_error (display,
"One of the filter required ARB_fragment_shader");
display->gen_shader = NULL;
}
}
#if HAVE_GLES2
void
_gen_shader_gles2 (GstGLDisplay * display)
{
GST_TRACE ("Generating shader %" GST_PTR_FORMAT, display->gen_shader);
if (GLEW_ARB_fragment_shader) {
if (display->gen_shader_vertex_source ||
display->gen_shader_fragment_source) {
GError *error = NULL;
display->gen_shader = gst_gl_shader_new (display);
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_gl_display_set_error (display, "%s", error->message);
g_error_free (error);
error = NULL;
gst_gl_shader_use (NULL);
g_object_unref (G_OBJECT (display->gen_shader));
display->gen_shader = NULL;
}
}
} else {
gst_gl_display_set_error (display,
"One of the filter required ARB_fragment_shader");
display->gen_shader = NULL;
}
}
#endif
/* Called in the gl thread */
#if HAVE_OPENGL
void
_del_shader_opengl (GstGLDisplay * display)
{
GST_TRACE ("Deleting shader %" GST_PTR_FORMAT, display->del_shader);
if (display->del_shader) {
g_object_unref (G_OBJECT (display->del_shader));
display->del_shader = NULL;
}
}
#endif
#if HAVE_GLES2
void
_del_shader_gles2 (GstGLDisplay * display)
{
GST_TRACE ("Deleting shader %" GST_PTR_FORMAT, display->del_shader);
if (display->del_shader) {
g_object_unref (G_OBJECT (display->del_shader));
display->del_shader = NULL;
}
}
#endif
//------------------------------------------------------------
//------------------ BEGIN GL THREAD ACTIONS -----------------
//------------------------------------------------------------
//------------------------------------------------------------
//---------------------- BEGIN PRIVATE -----------------------
//------------------------------------------------------------
void
gst_gl_display_on_resize (GstGLDisplay * display, gint width, gint height)
{
GST_TRACE ("GL Window resized to %ux%u", width, height);
//check if a client reshape callback is registered
if (display->clientReshapeCallback)
display->clientReshapeCallback (width, height, display->client_data);
//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);
}
#if HAVE_OPENGL
if (USING_OPENGL(display)) {
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluOrtho2D (0, width, 0, height);
glMatrixMode (GL_MODELVIEW);
}
#endif
}
}
void
gst_gl_display_on_draw (GstGLDisplay * display)
{
/* check if texture is ready for being drawn */
if (!display->redisplay_texture)
return;
/* opengl scene */
GST_TRACE ("on draw");
/* make sure that the environnement is clean */
if (display->colorspace_conversion == GST_GL_DISPLAY_CONVERSION_GLSL)
glUseProgramObjectARB (0);
#if HAVE_OPENGL
if (USING_OPENGL(display))
glDisable (GL_TEXTURE_RECTANGLE_ARB);
#endif
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,
display->client_data);
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 {
#if HAVE_OPENGL
if (USING_OPENGL(display)) {
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);
/* gst images are top-down while opengl plane is bottom-up */
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);
/*glTexCoord2i (display->redisplay_texture_width, 0);
glVertex2i (1, -1);
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);
}
#endif
#if HAVE_GLES2
if (USING_GLES2(display)) {
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_gl_display_set_error (display, "Output window was closed");
}
void
gst_gl_display_gen_texture_window_cb (GstGLDisplay * display)
{
gst_gl_display_gen_texture_thread (display, &display->gen_texture,
display->gen_texture_video_format, display->gen_texture_width,
display->gen_texture_height);
}
/* Generate a texture if no one is available in the pool
* Called in the gl thread */
void
gst_gl_display_gen_texture_thread (GstGLDisplay * display, GLuint * pTexture,
GstVideoFormat v_format, GLint width, GLint height)
{
GST_TRACE ("Generating texture format:%u dimensions:%ux%u", v_format,
width, height);
glGenTextures (1, pTexture);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, *pTexture);
switch (v_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->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;
#if 0
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;
#endif
default:
gst_gl_display_set_error (display, "Unknow colorspace conversion %d",
display->colorspace_conversion);
}
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:
gst_gl_display_set_error (display, "Unsupported upload video format %d",
v_format);
break;
}
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);
}
void
gst_gl_display_del_texture_thread (GstGLDisplay * display, GLuint * pTexture)
{
//glDeleteTextures (1, pTexture);
}
/*------------------------------------------------------------
--------------------- END PRIVATE -------------------------
----------------------------------------------------------*/
/*------------------------------------------------------------
--------------------- BEGIN PUBLIC -------------------------
----------------------------------------------------------*/
void
gst_gl_display_lock (GstGLDisplay * display)
{
g_mutex_lock (display->mutex);
}
void
gst_gl_display_unlock (GstGLDisplay * display)
{
g_mutex_unlock (display->mutex);
}
/* 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;
#if HAVE_OPENGL
case GL_FRAMEBUFFER_UNDEFINED:
GST_ERROR ("GL_FRAMEBUFFER_UNDEFINED");
break;
#endif
default:
GST_ERROR ("General FBO error");
}
}
/* Called by the first gl element of a video/x-raw-gl flow */
GstGLDisplay *
gst_gl_display_new (void)
{
return g_object_new (GST_GL_TYPE_DISPLAY, NULL);
}
/* Create an opengl context (one context for one GstGLDisplay) */
gboolean
gst_gl_display_create_context (GstGLDisplay * display,
gulong external_gl_context)
{
gboolean isAlive = FALSE;
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");
}
isAlive = display->isAlive;
gst_gl_display_unlock (display);
return isAlive;
}
/* Called by the glimagesink element */
gboolean
gst_gl_display_redisplay (GstGLDisplay * display, GLuint texture,
gint gl_width, gint gl_height, gint window_width, gint window_height,
gboolean keep_aspect_ratio)
{
gboolean isAlive;
gst_gl_display_lock (display);
if (display->isAlive) {
#if HAVE_GLES2
if (USING_GLES2(display)) {
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 = gl_width;
display->redisplay_texture_height = gl_height;
}
display->keep_aspect_ratio = keep_aspect_ratio;
if (display->gl_window)
gst_gl_window_draw (display->gl_window, window_width, window_height);
}
isAlive = display->isAlive;
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,
GstVideoFormat v_format, GLint width, GLint height)
{
gst_gl_display_lock (display);
if (display->isAlive) {
display->gen_texture_width = width;
display->gen_texture_height = height;
display->gen_texture_video_format = v_format;
gst_gl_window_send_message (display->gl_window,
GST_GL_WINDOW_CB (gst_gl_display_gen_texture_window_cb), 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 * pTexture)
{
gst_gl_display_lock (display);
if (*pTexture) {
gst_gl_display_del_texture_thread (display, pTexture);
}
gst_gl_display_unlock (display);
}
/* Called by gltestsrc and glfilter */
gboolean
gst_gl_display_gen_fbo (GstGLDisplay * display, gint width, gint height,
GLuint * fbo, GLuint * depthbuffer)
{
gboolean isAlive = FALSE;
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 (display->priv->vtable.gen_fbo), display);
*fbo = display->generated_fbo;
*depthbuffer = display->generated_depth_buffer;
}
isAlive = display->isAlive;
gst_gl_display_unlock (display);
return isAlive;
}
/* 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;
gst_gl_display_lock (display);
if (display->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, display->priv->vtable.use_fbo, display);
}
isAlive = display->isAlive;
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;
gst_gl_display_lock (display);
if (display->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, display->priv->vtable.use_fbo_v2, display);
}
isAlive = display->isAlive;
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);
if (display->isAlive) {
display->del_fbo = fbo;
display->del_depth_buffer = depth_buffer;
gst_gl_window_send_message (display->gl_window, display->priv->vtable.del_fbo, display);
}
gst_gl_display_unlock (display);
}
/* Called by glfilter */
gboolean
gst_gl_display_gen_shader (GstGLDisplay * display,
const gchar * shader_vertex_source,
const gchar * shader_fragment_source, GstGLShader ** shader)
{
gboolean isAlive;
gst_gl_display_lock (display);
if (display->isAlive) {
display->gen_shader_vertex_source = shader_vertex_source;
display->gen_shader_fragment_source = shader_fragment_source;
gst_gl_window_send_message (display->gl_window, display->priv->vtable.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;
}
isAlive = display->isAlive;
gst_gl_display_unlock (display);
return isAlive;
}
/* Called by glfilter */
void
gst_gl_display_del_shader (GstGLDisplay * display, GstGLShader * shader)
{
gst_gl_display_lock (display);
if (display->isAlive) {
display->del_shader = shader;
gst_gl_window_send_message (display->gl_window, display->priv->vtable.del_shader, display);
}
gst_gl_display_unlock (display);
}
/* Called by the glimagesink */
void
gst_gl_display_set_window_id (GstGLDisplay * display, guintptr window_id)
{
gst_gl_display_lock (display);
gst_gl_window_set_window_handle (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);
}
void
gst_gl_display_set_client_data (GstGLDisplay * display, gpointer data)
{
gst_gl_display_lock (display);
display->client_data = data;
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_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 (display->gl_window, activate);
if (activate)
gst_gl_display_unlock (display);
}
GstGLAPI
gst_gl_display_get_gl_api_unlocked (GstGLDisplay * display)
{
return display->gl_api;
}
GstGLAPI
gst_gl_display_get_gl_api (GstGLDisplay * display)
{
GstGLAPI api;
gst_gl_display_lock (display);
api = gst_gl_display_get_gl_api_unlocked (display);
gst_gl_display_unlock (display);
return api;
}
//------------------------------------------------------------
//------------------------ END PUBLIC ------------------------
//------------------------------------------------------------