mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
cad1bb32c8
This commit makes the loading of the GModules threadsafe, and always first tries to load the symbol for the GL library that is selected for the current context. Only then it falls back to looking into the current module (NULL), and only as a last resort the context specific function (e.g. eglGetProcAddress()) is called. Also add configure parameters to select the names of the library modules instead of using the defaults, and let the defaults be independent of the G_MODULE_SUFFIX. https://bugzilla.gnome.org/show_bug.cgi?id=728753
1089 lines
29 KiB
C
1089 lines
29 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2013 Matthew Waters <ystreet00@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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstglcontext
|
|
* @short_description: OpenGL context abstraction
|
|
* @title: GstGLContext
|
|
* @see_also: #GstGLDisplay, #GstGLWindow
|
|
*
|
|
* #GstGLContext wraps an OpenGL context object in a uniform API. As a result
|
|
* of the limitation on OpenGL context, this object is not thread safe unless
|
|
* specified and must only be activated in a single thread.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#if defined(ANDROID) || defined(__ANDROID__)
|
|
/* Avoid a linker error with _isoc99_sscanf() when building a shared library
|
|
* for android
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#ifndef GL_NUM_EXTENSIONS
|
|
#define GL_NUM_EXTENSIONS 0x0000821d
|
|
#endif
|
|
|
|
#include <gmodule.h>
|
|
|
|
#include "gl.h"
|
|
#include "gstglcontext.h"
|
|
|
|
#if GST_GL_HAVE_PLATFORM_GLX
|
|
#include "x11/gstglcontext_glx.h"
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_EGL
|
|
#include "egl/gstglcontext_egl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_COCOA
|
|
#include "cocoa/gstglcontext_cocoa.h"
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_WGL
|
|
#include "win32/gstglcontext_wgl.h"
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_EAGL
|
|
#include "eagl/gstglcontext_eagl.h"
|
|
#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)
|
|
|
|
static GModule *module_self;
|
|
|
|
#if GST_GL_HAVE_OPENGL
|
|
static GOnce module_opengl_gonce = G_ONCE_INIT;
|
|
static GModule *module_opengl;
|
|
|
|
static gpointer
|
|
load_opengl_module (gpointer user_data)
|
|
{
|
|
#ifdef GST_GL_LIBGL_MODULE_NAME
|
|
module_opengl = g_module_open (GST_GL_LIBGL_MODULE_NAME, G_MODULE_BIND_LAZY);
|
|
#else
|
|
/* This automatically handles the suffix and even .la files */
|
|
module_opengl = g_module_open ("libGL", G_MODULE_BIND_LAZY);
|
|
|
|
/* On Linux the .so is only in -dev packages, try with a real soname
|
|
* Proper compilers will optimize away the strcmp */
|
|
if (!module_opengl && strcmp (G_MODULE_SUFFIX, "so") == 0)
|
|
module_opengl = g_module_open ("libGL.so.1", G_MODULE_BIND_LAZY);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if GST_GL_HAVE_GLES2
|
|
static GOnce module_gles2_gonce = G_ONCE_INIT;
|
|
static GModule *module_gles2;
|
|
|
|
static gpointer
|
|
load_gles2_module (gpointer user_data)
|
|
{
|
|
#ifdef GST_GL_LIBGLESV2_MODULE_NAME
|
|
module_gles2 =
|
|
g_module_open (GST_GL_LIBGLESV2_MODULE_NAME, G_MODULE_BIND_LAZY);
|
|
#else
|
|
/* This automatically handles the suffix and even .la files */
|
|
module_gles2 = g_module_open ("libGLESv2", G_MODULE_BIND_LAZY);
|
|
|
|
/* On Linux the .so is only in -dev packages, try with a real soname
|
|
* Proper compilers will optimize away the strcmp */
|
|
if (!module_gles2 && strcmp (G_MODULE_SUFFIX, "so") == 0)
|
|
module_gles2 = g_module_open ("libGLESv2.so.2", G_MODULE_BIND_LAZY);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if GST_GL_HAVE_GLES3
|
|
#error "Add module loading support for GLES3"
|
|
#endif
|
|
|
|
#define GST_CAT_DEFAULT gst_gl_context_debug
|
|
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
|
|
|
|
#define gst_gl_context_parent_class parent_class
|
|
G_DEFINE_ABSTRACT_TYPE (GstGLContext, gst_gl_context, G_TYPE_OBJECT);
|
|
|
|
#define GST_GL_CONTEXT_GET_PRIVATE(o) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_CONTEXT, GstGLContextPrivate))
|
|
|
|
static gpointer gst_gl_context_create_thread (GstGLContext * context);
|
|
static void gst_gl_context_finalize (GObject * object);
|
|
|
|
struct _GstGLContextPrivate
|
|
{
|
|
GstGLDisplay *display;
|
|
|
|
GThread *gl_thread;
|
|
|
|
/* conditions */
|
|
GMutex render_lock;
|
|
GCond create_cond;
|
|
GCond destroy_cond;
|
|
|
|
gboolean created;
|
|
gboolean alive;
|
|
|
|
GstGLContext *other_context;
|
|
GError **error;
|
|
|
|
gint gl_major;
|
|
gint gl_minor;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
GstGLContext parent;
|
|
|
|
guintptr handle;
|
|
GstGLPlatform platform;
|
|
GstGLAPI available_apis;
|
|
} GstGLWrappedContext;
|
|
|
|
typedef struct
|
|
{
|
|
GstGLContextClass parent;
|
|
} GstGLWrappedContextClass;
|
|
|
|
#define GST_GL_TYPE_WRAPPED_CONTEXT (gst_gl_wrapped_context_get_type())
|
|
GType gst_gl_wrapped_context_get_type (void);
|
|
G_DEFINE_TYPE (GstGLWrappedContext, gst_gl_wrapped_context,
|
|
GST_GL_TYPE_CONTEXT);
|
|
|
|
#define GST_GL_WRAPPED_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_GL_TYPE_WRAPPED_CONTEXT, GstGLWrappedContext))
|
|
#define GST_GL_WRAPPED_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_GL_TYPE_CONTEXT, GstGLContextClass))
|
|
#define GST_GL_IS_WRAPPED_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_GL_TYPE_WRAPPED_CONTEXT))
|
|
#define GST_GL_IS_WRAPPED_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_GL_TYPE_WRAPPED_CONTEXT))
|
|
#define GST_GL_WRAPPED_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_GL_TYPE_WRAPPED_CONTEXT, GstGLWrappedContextClass))
|
|
|
|
GQuark
|
|
gst_gl_context_error_quark (void)
|
|
{
|
|
return g_quark_from_static_string ("gst-gl-context-error-quark");
|
|
}
|
|
|
|
static void
|
|
_ensure_window (GstGLContext * context)
|
|
{
|
|
GstGLWindow *window;
|
|
|
|
if (context->window)
|
|
return;
|
|
|
|
window = gst_gl_window_new (context->priv->display);
|
|
|
|
gst_gl_context_set_window (context, window);
|
|
|
|
gst_object_unref (window);
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_init (GstGLContext * context)
|
|
{
|
|
context->priv = GST_GL_CONTEXT_GET_PRIVATE (context);
|
|
|
|
context->window = NULL;
|
|
context->gl_vtable = g_slice_alloc0 (sizeof (GstGLFuncs));
|
|
|
|
g_mutex_init (&context->priv->render_lock);
|
|
|
|
g_cond_init (&context->priv->create_cond);
|
|
g_cond_init (&context->priv->destroy_cond);
|
|
context->priv->created = FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_class_init (GstGLContextClass * klass)
|
|
{
|
|
g_type_class_add_private (klass, sizeof (GstGLContextPrivate));
|
|
|
|
module_self = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
|
|
|
klass->get_proc_address =
|
|
GST_DEBUG_FUNCPTR (gst_gl_context_default_get_proc_address);
|
|
|
|
G_OBJECT_CLASS (klass)->finalize = gst_gl_context_finalize;
|
|
}
|
|
|
|
static void
|
|
_init_debug (void)
|
|
{
|
|
static volatile gsize _init = 0;
|
|
|
|
if (g_once_init_enter (&_init)) {
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_context_debug, "glcontext", 0,
|
|
"glcontext element");
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_new:
|
|
* @display: a #GstGLDisplay
|
|
*
|
|
* Create a new #GstGLContext with the specified @display
|
|
*
|
|
* Returns: a new #GstGLContext
|
|
*/
|
|
GstGLContext *
|
|
gst_gl_context_new (GstGLDisplay * display)
|
|
{
|
|
GstGLContext *context = NULL;
|
|
const gchar *user_choice;
|
|
|
|
_init_debug ();
|
|
|
|
user_choice = g_getenv ("GST_GL_PLATFORM");
|
|
GST_INFO ("creating a context, user choice:%s", user_choice);
|
|
#if GST_GL_HAVE_PLATFORM_EGL
|
|
if (!context && (!user_choice || g_strstr_len (user_choice, 7, "egl")))
|
|
context = GST_GL_CONTEXT (gst_gl_context_egl_new ());
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_COCOA
|
|
if (!context && (!user_choice || g_strstr_len (user_choice, 5, "cocoa")))
|
|
context = GST_GL_CONTEXT (gst_gl_context_cocoa_new ());
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_GLX
|
|
if (!context && (!user_choice || g_strstr_len (user_choice, 3, "glx")))
|
|
context = GST_GL_CONTEXT (gst_gl_context_glx_new ());
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_WGL
|
|
if (!context && (!user_choice || g_strstr_len (user_choice, 3, "wgl"))) {
|
|
context = GST_GL_CONTEXT (gst_gl_context_wgl_new ());
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_EAGL
|
|
if (!context && (!user_choice || g_strstr_len (user_choice, 5, "eagl")))
|
|
context = GST_GL_CONTEXT (gst_gl_context_eagl_new ());
|
|
#endif
|
|
|
|
if (!context) {
|
|
/* subclass returned a NULL context */
|
|
GST_WARNING ("Could not create context. user specified %s",
|
|
user_choice ? user_choice : "(null)");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
context->priv->display = gst_object_ref (display);
|
|
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_new_wrapped:
|
|
* @display: a #GstGLDisplay
|
|
* @handle: the OpenGL context to wrap
|
|
* @context_type: a #GstGLPlatform specifying the type of context in @handle
|
|
* @available_apis: a #GstGLAPI containing the available OpenGL apis in @handle
|
|
*
|
|
* Wraps an existing OpenGL context into a #GstGLContext.
|
|
*
|
|
* Returns: a #GstGLContext wrapping @handle
|
|
*/
|
|
GstGLContext *
|
|
gst_gl_context_new_wrapped (GstGLDisplay * display, guintptr handle,
|
|
GstGLPlatform context_type, GstGLAPI available_apis)
|
|
{
|
|
GstGLContext *context;
|
|
GstGLWrappedContext *context_wrap = NULL;
|
|
|
|
_init_debug ();
|
|
|
|
context_wrap = g_object_new (GST_GL_TYPE_WRAPPED_CONTEXT, NULL);
|
|
|
|
if (!context_wrap) {
|
|
/* subclass returned a NULL context */
|
|
GST_ERROR ("Could not wrap existing context");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
context = (GstGLContext *) context_wrap;
|
|
|
|
context->priv->display = gst_object_ref (display);
|
|
context_wrap->handle = handle;
|
|
context_wrap->platform = context_type;
|
|
context_wrap->available_apis = available_apis;
|
|
|
|
return context;
|
|
}
|
|
|
|
static void
|
|
gst_gl_context_finalize (GObject * object)
|
|
{
|
|
GstGLContext *context = GST_GL_CONTEXT (object);
|
|
|
|
if (context->window) {
|
|
gst_gl_window_set_resize_callback (context->window, NULL, NULL, NULL);
|
|
gst_gl_window_set_draw_callback (context->window, NULL, NULL, NULL);
|
|
|
|
if (context->priv->alive) {
|
|
g_mutex_lock (&context->priv->render_lock);
|
|
GST_INFO ("send quit gl window loop");
|
|
gst_gl_window_quit (context->window);
|
|
while (context->priv->alive) {
|
|
g_cond_wait (&context->priv->destroy_cond, &context->priv->render_lock);
|
|
}
|
|
g_mutex_unlock (&context->priv->render_lock);
|
|
}
|
|
|
|
gst_gl_window_set_close_callback (context->window, NULL, NULL, NULL);
|
|
|
|
if (context->priv->gl_thread) {
|
|
gpointer ret = g_thread_join (context->priv->gl_thread);
|
|
GST_INFO ("gl thread joined");
|
|
if (ret != NULL)
|
|
GST_ERROR ("gl thread returned a non-null pointer");
|
|
context->priv->gl_thread = NULL;
|
|
}
|
|
|
|
gst_object_unref (context->window);
|
|
}
|
|
|
|
gst_object_unref (context->priv->display);
|
|
|
|
if (context->gl_vtable) {
|
|
g_slice_free (GstGLFuncs, context->gl_vtable);
|
|
context->gl_vtable = NULL;
|
|
}
|
|
|
|
g_mutex_clear (&context->priv->render_lock);
|
|
|
|
g_cond_clear (&context->priv->destroy_cond);
|
|
g_cond_clear (&context->priv->create_cond);
|
|
|
|
G_OBJECT_CLASS (gst_gl_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_activate:
|
|
* @context: a #GstGLContext
|
|
* @activate: %TRUE to activate, %FALSE to deactivate
|
|
*
|
|
* (De)activate the OpenGL context represented by this @context.
|
|
*
|
|
* In OpenGL terms, calls eglMakeCurrent or similar with this context and the
|
|
* currently set window. See gst_gl_context_set_window() for details.
|
|
*
|
|
* Returns: Whether the activation succeeded
|
|
*/
|
|
gboolean
|
|
gst_gl_context_activate (GstGLContext * context, gboolean activate)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
gboolean result;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
g_return_val_if_fail (context_class->activate != NULL, FALSE);
|
|
|
|
result = context_class->activate (context, activate);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_gl_api:
|
|
* @context: a #GstGLContext
|
|
*
|
|
* Get the currently enabled OpenGL api.
|
|
*
|
|
* The currently available API may be limited by the #GstGLDisplay in use and/or
|
|
* the #GstGLWindow chosen.
|
|
*
|
|
* Returns: the currently available OpenGL api
|
|
*/
|
|
GstGLAPI
|
|
gst_gl_context_get_gl_api (GstGLContext * context)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), GST_GL_API_NONE);
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
g_return_val_if_fail (context_class->get_gl_api != NULL, GST_GL_API_NONE);
|
|
|
|
return context_class->get_gl_api (context);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_proc_address:
|
|
* @context: a #GstGLContext
|
|
* @name: an opengl function name
|
|
*
|
|
* Get a function pointer to a specified opengl function, @name. If the the
|
|
* specific function does not exist, NULL is returned instead.
|
|
*
|
|
* Platform specfic functions (names starting 'egl', 'glX', 'wgl', etc) can also
|
|
* be retreived using this method.
|
|
*
|
|
* Returns: a function pointer or NULL
|
|
*/
|
|
gpointer
|
|
gst_gl_context_get_proc_address (GstGLContext * context, const gchar * name)
|
|
{
|
|
gpointer ret;
|
|
GstGLContextClass *context_class;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), NULL);
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
g_return_val_if_fail (context_class->get_proc_address != NULL, NULL);
|
|
|
|
ret = context_class->get_proc_address (context, name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gpointer
|
|
gst_gl_context_default_get_proc_address (GstGLContext * context,
|
|
const gchar * name)
|
|
{
|
|
gpointer ret = NULL;
|
|
GstGLAPI gl_api = gst_gl_context_get_gl_api (context);
|
|
|
|
/* First try to load symbol from the selected GL API for this context */
|
|
#if GST_GL_HAVE_GLES2
|
|
if (!ret && (gl_api & GST_GL_API_GLES2)) {
|
|
g_once (&module_gles2_gonce, load_gles2_module, NULL);
|
|
if (module_gles2)
|
|
g_module_symbol (module_gles2, name, &ret);
|
|
}
|
|
#endif
|
|
|
|
#if GST_GL_HAVE_OPENGL
|
|
if (!ret && (gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3))) {
|
|
g_once (&module_opengl_gonce, load_opengl_module, NULL);
|
|
if (module_opengl)
|
|
g_module_symbol (module_opengl, name, &ret);
|
|
}
|
|
#endif
|
|
|
|
/* Otherwise fall back to the current module */
|
|
if (!ret)
|
|
g_module_symbol (module_self, name, &ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_set_window:
|
|
* @context: a #GstGLContext
|
|
* @window: (transfer full): a #GstGLWindow
|
|
*
|
|
* Set's the current window on @context to @window. The window can only be
|
|
* changed before gst_gl_context_create() has been called and the @window is not
|
|
* already running.
|
|
*
|
|
* Returns: Whether the window was successfully updated
|
|
*/
|
|
gboolean
|
|
gst_gl_context_set_window (GstGLContext * context, GstGLWindow * window)
|
|
{
|
|
g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), FALSE);
|
|
|
|
/* we can't change the window while we are running */
|
|
if (context->priv->alive)
|
|
return FALSE;
|
|
|
|
if (window) {
|
|
if (gst_gl_window_is_running (window))
|
|
return FALSE;
|
|
|
|
g_weak_ref_set (&window->context_ref, context);
|
|
}
|
|
|
|
if (context->window)
|
|
gst_object_unref (context->window);
|
|
|
|
context->window = window ? gst_object_ref (window) : NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_window:
|
|
* @context: a #GstGLContext
|
|
*
|
|
* Returns: the currently set window
|
|
*/
|
|
GstGLWindow *
|
|
gst_gl_context_get_window (GstGLContext * context)
|
|
{
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
|
|
|
|
if (GST_GL_IS_WRAPPED_CONTEXT (context))
|
|
return NULL;
|
|
|
|
_ensure_window (context);
|
|
|
|
return gst_object_ref (context->window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_create:
|
|
* @context: a #GstGLContext:
|
|
* @other_context: (allow-none): a #GstGLContext to share OpenGL objects with
|
|
* @error: (allow-none): a #GError
|
|
*
|
|
* Creates an OpenGL context in the current thread with the specified
|
|
* @other_context as a context to share shareable OpenGL objects with. See the
|
|
* OpenGL specification for what is shared between contexts.
|
|
*
|
|
* If an error occurs, and @error is not %NULL, then error will contain details
|
|
* of the error and %FALSE will be returned.
|
|
*
|
|
* Should only be called once.
|
|
*
|
|
* Returns: whether the context could successfully be created
|
|
*/
|
|
gboolean
|
|
gst_gl_context_create (GstGLContext * context,
|
|
GstGLContext * other_context, GError ** error)
|
|
{
|
|
gboolean alive = FALSE;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), FALSE);
|
|
g_return_val_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context), FALSE);
|
|
_ensure_window (context);
|
|
|
|
g_mutex_lock (&context->priv->render_lock);
|
|
|
|
if (!context->priv->created) {
|
|
context->priv->other_context = other_context;
|
|
context->priv->error = error;
|
|
|
|
context->priv->gl_thread = g_thread_new ("gstglcontext",
|
|
(GThreadFunc) gst_gl_context_create_thread, context);
|
|
|
|
g_cond_wait (&context->priv->create_cond, &context->priv->render_lock);
|
|
|
|
context->priv->created = TRUE;
|
|
|
|
GST_INFO ("gl thread created");
|
|
}
|
|
|
|
alive = context->priv->alive;
|
|
|
|
g_mutex_unlock (&context->priv->render_lock);
|
|
|
|
return alive;
|
|
}
|
|
|
|
static gboolean
|
|
_create_context_gles2 (GstGLContext * context, gint * gl_major, gint * gl_minor,
|
|
GError ** error)
|
|
{
|
|
const GstGLFuncs *gl;
|
|
GLenum gl_err = GL_NO_ERROR;
|
|
|
|
gl = context->gl_vtable;
|
|
|
|
GST_INFO ("GL_VERSION: %s", gl->GetString (GL_VERSION));
|
|
GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
|
|
gl->GetString (GL_SHADING_LANGUAGE_VERSION));
|
|
GST_INFO ("GL_VENDOR: %s", gl->GetString (GL_VENDOR));
|
|
GST_INFO ("GL_RENDERER: %s", gl->GetString (GL_RENDERER));
|
|
|
|
gl_err = gl->GetError ();
|
|
if (gl_err != GL_NO_ERROR) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"glGetString error: 0x%x", gl_err);
|
|
return FALSE;
|
|
}
|
|
#if GST_GL_HAVE_GLES2
|
|
if (!GL_ES_VERSION_2_0) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_OLD_LIBS,
|
|
"OpenGL|ES >= 2.0 is required");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (gl_major)
|
|
*gl_major = 2;
|
|
if (gl_minor)
|
|
*gl_minor = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_create_context_opengl (GstGLContext * context, gint * gl_major,
|
|
gint * gl_minor, GError ** error)
|
|
{
|
|
const GstGLFuncs *gl;
|
|
guint maj, min;
|
|
GLenum gl_err = GL_NO_ERROR;
|
|
GString *opengl_version = NULL;
|
|
|
|
gl = context->gl_vtable;
|
|
|
|
GST_INFO ("GL_VERSION: %s", gl->GetString (GL_VERSION));
|
|
GST_INFO ("GL_SHADING_LANGUAGE_VERSION: %s",
|
|
gl->GetString (GL_SHADING_LANGUAGE_VERSION));
|
|
GST_INFO ("GL_VENDOR: %s", gl->GetString (GL_VENDOR));
|
|
GST_INFO ("GL_RENDERER: %s", gl->GetString (GL_RENDERER));
|
|
|
|
gl_err = gl->GetError ();
|
|
if (gl_err != GL_NO_ERROR) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"glGetString error: 0x%x", gl_err);
|
|
return FALSE;
|
|
}
|
|
opengl_version =
|
|
g_string_truncate (g_string_new ((gchar *) gl->GetString (GL_VERSION)),
|
|
3);
|
|
|
|
sscanf (opengl_version->str, "%d.%d", &maj, &min);
|
|
|
|
g_string_free (opengl_version, TRUE);
|
|
|
|
/* OpenGL > 1.2.0 */
|
|
if ((maj < 1) || (maj < 2 && maj >= 1 && min < 2)) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_OLD_LIBS,
|
|
"OpenGL >= 1.2.0 required, found %u.%u", maj, min);
|
|
return FALSE;
|
|
}
|
|
|
|
if (gl_major)
|
|
*gl_major = maj;
|
|
if (gl_minor)
|
|
*gl_minor = min;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstGLAPI
|
|
_compiled_api (void)
|
|
{
|
|
GstGLAPI ret = GST_GL_API_NONE;
|
|
|
|
#if GST_GL_HAVE_OPENGL
|
|
ret |= GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
|
|
#endif
|
|
#if GST_GL_HAVE_GLES2
|
|
ret |= GST_GL_API_GLES2;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
_unlock_create_thread (GstGLContext * context)
|
|
{
|
|
g_mutex_unlock (&context->priv->render_lock);
|
|
}
|
|
|
|
static GString *
|
|
_build_extension_string (GstGLContext * context)
|
|
{
|
|
const GstGLFuncs *gl = context->gl_vtable;
|
|
GString *ext_g_str = g_string_sized_new (1024);
|
|
const gchar *ext_const_c_str = NULL;
|
|
int i, n;
|
|
|
|
gl->GetIntegerv (GL_NUM_EXTENSIONS, &n);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
ext_const_c_str = (const gchar *) gl->GetStringi (GL_EXTENSIONS, i);
|
|
if (ext_const_c_str)
|
|
g_string_append_printf (ext_g_str, "%s ", ext_const_c_str);
|
|
}
|
|
|
|
return ext_g_str;
|
|
}
|
|
|
|
//gboolean
|
|
//gst_gl_context_create (GstGLContext * context, GstGLContext * other_context, GError ** error)
|
|
static gpointer
|
|
gst_gl_context_create_thread (GstGLContext * context)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
GstGLWindowClass *window_class;
|
|
GstGLDisplay *display;
|
|
GstGLFuncs *gl;
|
|
gboolean ret = FALSE;
|
|
GstGLAPI compiled_api, user_api;
|
|
gchar *api_string;
|
|
gchar *compiled_api_s;
|
|
gchar *user_api_string;
|
|
const gchar *user_choice;
|
|
GError **error;
|
|
GstGLContext *other_context;
|
|
GString *ext_g_str = NULL;
|
|
const gchar *ext_const_c_str = NULL;
|
|
|
|
g_mutex_lock (&context->priv->render_lock);
|
|
|
|
error = context->priv->error;
|
|
other_context = context->priv->other_context;
|
|
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
window_class = GST_GL_WINDOW_GET_CLASS (context->window);
|
|
|
|
if (window_class->open) {
|
|
if (!window_class->open (context->window, error))
|
|
goto failure;
|
|
}
|
|
|
|
display = context->priv->display;
|
|
gl = context->gl_vtable;
|
|
compiled_api = _compiled_api ();
|
|
|
|
user_choice = g_getenv ("GST_GL_API");
|
|
|
|
user_api = gst_gl_api_from_string (user_choice);
|
|
user_api_string = gst_gl_api_to_string (user_api);
|
|
|
|
compiled_api_s = gst_gl_api_to_string (compiled_api);
|
|
|
|
if ((user_api & compiled_api) == GST_GL_API_NONE) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API,
|
|
"Cannot create context with the user requested api (%s). "
|
|
"We have support for (%s)", user_api_string, compiled_api_s);
|
|
g_free (user_api_string);
|
|
g_free (compiled_api_s);
|
|
goto failure;
|
|
}
|
|
|
|
if (context_class->choose_format &&
|
|
!context_class->choose_format (context, error)) {
|
|
g_assert (error == NULL || *error != NULL);
|
|
g_free (compiled_api_s);
|
|
g_free (user_api_string);
|
|
goto failure;
|
|
}
|
|
|
|
GST_INFO ("Attempting to create opengl context. user chosen api(s) (%s), "
|
|
"compiled api support (%s)", user_api_string, compiled_api_s);
|
|
|
|
if (!context_class->create_context (context, compiled_api & user_api,
|
|
other_context, error)) {
|
|
g_assert (error == NULL || *error != NULL);
|
|
g_free (compiled_api_s);
|
|
g_free (user_api_string);
|
|
goto failure;
|
|
}
|
|
GST_INFO ("created context");
|
|
|
|
if (!context_class->activate (context, TRUE)) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR,
|
|
GST_GL_CONTEXT_ERROR_RESOURCE_UNAVAILABLE,
|
|
"Failed to activate the GL Context");
|
|
g_free (compiled_api_s);
|
|
g_free (user_api_string);
|
|
goto failure;
|
|
}
|
|
|
|
display->gl_api = gst_gl_context_get_gl_api (context);
|
|
g_assert (display->gl_api != GST_GL_API_NONE
|
|
&& display->gl_api != GST_GL_API_ANY);
|
|
|
|
api_string = gst_gl_api_to_string (display->gl_api);
|
|
GST_INFO ("available GL APIs: %s", api_string);
|
|
|
|
if (((compiled_api & display->gl_api) & user_api) == GST_GL_API_NONE) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API,
|
|
"failed to create context, context "
|
|
"could not provide correct api. user (%s), compiled (%s), context (%s)",
|
|
user_api_string, compiled_api_s, api_string);
|
|
g_free (api_string);
|
|
g_free (compiled_api_s);
|
|
g_free (user_api_string);
|
|
goto failure;
|
|
}
|
|
|
|
g_free (api_string);
|
|
g_free (compiled_api_s);
|
|
g_free (user_api_string);
|
|
|
|
gl->GetError = gst_gl_context_get_proc_address (context, "glGetError");
|
|
gl->GetString = gst_gl_context_get_proc_address (context, "glGetString");
|
|
gl->GetStringi = gst_gl_context_get_proc_address (context, "glGetStringi");
|
|
gl->GetIntegerv = gst_gl_context_get_proc_address (context, "glGetIntegerv");
|
|
|
|
if (!gl->GetError || !gl->GetString) {
|
|
g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
|
|
"could not GetProcAddress core opengl functions");
|
|
goto failure;
|
|
}
|
|
|
|
/* gl api specific code */
|
|
if (!ret && USING_OPENGL (display))
|
|
ret = _create_context_opengl (context, &context->priv->gl_major,
|
|
&context->priv->gl_minor, error);
|
|
if (!ret && USING_GLES2 (display))
|
|
ret =
|
|
_create_context_gles2 (context, &context->priv->gl_major,
|
|
&context->priv->gl_minor, error);
|
|
|
|
if (!ret)
|
|
goto failure;
|
|
|
|
/* GL core contexts and GLES3 */
|
|
if (gl->GetIntegerv && gl->GetStringi && context->priv->gl_major >= 3)
|
|
ext_g_str = _build_extension_string (context);
|
|
|
|
if (ext_g_str && ext_g_str->len) {
|
|
GST_DEBUG_OBJECT (context, "GL_EXTENSIONS: %s", ext_g_str->str);
|
|
_gst_gl_feature_check_ext_functions (context, context->priv->gl_major,
|
|
context->priv->gl_minor, ext_g_str->str);
|
|
} else {
|
|
ext_const_c_str = (const gchar *) gl->GetString (GL_EXTENSIONS);
|
|
if (!ext_const_c_str)
|
|
ext_const_c_str = "";
|
|
GST_DEBUG_OBJECT (context, "GL_EXTENSIONS: %s", ext_const_c_str);
|
|
_gst_gl_feature_check_ext_functions (context, context->priv->gl_major,
|
|
context->priv->gl_minor, ext_const_c_str);
|
|
}
|
|
|
|
if (ext_g_str)
|
|
g_string_free (ext_g_str, TRUE);
|
|
|
|
context->priv->alive = TRUE;
|
|
|
|
g_cond_signal (&context->priv->create_cond);
|
|
|
|
// g_mutex_unlock (&context->priv->render_lock);
|
|
gst_gl_window_send_message_async (context->window,
|
|
(GstGLWindowCB) _unlock_create_thread, context, NULL);
|
|
|
|
gst_gl_window_run (context->window);
|
|
|
|
GST_INFO ("loop exited\n");
|
|
|
|
g_mutex_lock (&context->priv->render_lock);
|
|
|
|
context->priv->alive = FALSE;
|
|
|
|
context_class->activate (context, FALSE);
|
|
|
|
context_class->destroy_context (context);
|
|
|
|
/* User supplied callback */
|
|
if (context->window->close)
|
|
context->window->close (context->window->close_data);
|
|
|
|
/* window specific shutdown */
|
|
if (window_class->close) {
|
|
window_class->close (context->window);
|
|
}
|
|
|
|
g_cond_signal (&context->priv->destroy_cond);
|
|
|
|
g_mutex_unlock (&context->priv->render_lock);
|
|
|
|
return NULL;
|
|
|
|
failure:
|
|
{
|
|
g_cond_signal (&context->priv->create_cond);
|
|
g_mutex_unlock (&context->priv->render_lock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_gl_context:
|
|
* @context: a #GstGLContext:
|
|
*
|
|
* Gets the backing OpenGL context used by @context.
|
|
*
|
|
* Returns: The platform specific backing OpenGL context
|
|
*/
|
|
guintptr
|
|
gst_gl_context_get_gl_context (GstGLContext * context)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
guintptr result;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), 0);
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
g_return_val_if_fail (context_class->get_gl_context != NULL, 0);
|
|
|
|
result = context_class->get_gl_context (context);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_gl_platform:
|
|
* @context: a #GstGLContext:
|
|
*
|
|
* Gets the OpenGL platform that used by @context.
|
|
*
|
|
* Returns: The platform specific backing OpenGL context
|
|
*/
|
|
GstGLPlatform
|
|
gst_gl_context_get_gl_platform (GstGLContext * context)
|
|
{
|
|
GstGLContextClass *context_class;
|
|
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), 0);
|
|
context_class = GST_GL_CONTEXT_GET_CLASS (context);
|
|
g_return_val_if_fail (context_class->get_gl_platform != NULL, 0);
|
|
|
|
return context_class->get_gl_platform (context);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_display:
|
|
* @context: a #GstGLContext:
|
|
*
|
|
* Returns: the #GstGLDisplay associated with this @context
|
|
*/
|
|
GstGLDisplay *
|
|
gst_gl_context_get_display (GstGLContext * context)
|
|
{
|
|
g_return_val_if_fail (GST_GL_IS_CONTEXT (context), NULL);
|
|
|
|
return gst_object_ref (context->priv->display);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstGLContext *context;
|
|
GstGLContextThreadFunc func;
|
|
gpointer data;
|
|
} RunGenericData;
|
|
|
|
static void
|
|
_gst_gl_context_thread_run_generic (RunGenericData * data)
|
|
{
|
|
GST_TRACE ("running function:%p data:%p", data->func, data->data);
|
|
|
|
data->func (data->context, data->data);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_thread_add:
|
|
* @context: a #GstGLContext
|
|
* @func: a #GstGLContextThreadFunc
|
|
* @data: (closure): user data to call @func with
|
|
*
|
|
* Execute @func in the OpenGL thread of @context with @data
|
|
*
|
|
* MT-safe
|
|
*/
|
|
void
|
|
gst_gl_context_thread_add (GstGLContext * context,
|
|
GstGLContextThreadFunc func, gpointer data)
|
|
{
|
|
GstGLWindow *window;
|
|
RunGenericData rdata;
|
|
|
|
g_return_if_fail (GST_GL_IS_CONTEXT (context));
|
|
g_return_if_fail (func != NULL);
|
|
g_return_if_fail (!GST_GL_IS_WRAPPED_CONTEXT (context));
|
|
|
|
rdata.context = context;
|
|
rdata.data = data;
|
|
rdata.func = func;
|
|
|
|
window = gst_gl_context_get_window (context);
|
|
|
|
gst_gl_window_send_message (window,
|
|
GST_GL_WINDOW_CB (_gst_gl_context_thread_run_generic), &rdata);
|
|
|
|
gst_object_unref (window);
|
|
}
|
|
|
|
/**
|
|
* gst_gl_context_get_gl_version:
|
|
* @context: a #GstGLContext
|
|
* @maj: (out): resulting major version
|
|
* @min: (out): resulting minor version
|
|
*
|
|
* Returns the OpenGL version implemented by @context. See
|
|
* gst_gl_context_get_gl_api() for retreiving the OpenGL api implemented by
|
|
* @context.
|
|
*/
|
|
void
|
|
gst_gl_context_get_gl_version (GstGLContext * context, gint * maj, gint * min)
|
|
{
|
|
g_return_if_fail (GST_GL_IS_CONTEXT (context));
|
|
g_return_if_fail (maj == NULL && min == NULL);
|
|
|
|
if (maj)
|
|
*maj = context->priv->gl_major;
|
|
|
|
if (min)
|
|
*min = context->priv->gl_minor;
|
|
}
|
|
|
|
static GstGLAPI
|
|
gst_gl_wrapped_context_get_gl_api (GstGLContext * context)
|
|
{
|
|
GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
|
|
|
|
return context_wrap->available_apis;
|
|
}
|
|
|
|
static guintptr
|
|
gst_gl_wrapped_context_get_gl_context (GstGLContext * context)
|
|
{
|
|
GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
|
|
|
|
return context_wrap->handle;
|
|
}
|
|
|
|
static GstGLPlatform
|
|
gst_gl_wrapped_context_get_gl_platform (GstGLContext * context)
|
|
{
|
|
GstGLWrappedContext *context_wrap = GST_GL_WRAPPED_CONTEXT (context);
|
|
|
|
return context_wrap->platform;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_wrapped_context_activate (GstGLContext * context, gboolean activate)
|
|
{
|
|
g_assert_not_reached ();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_gl_wrapped_context_class_init (GstGLWrappedContextClass * klass)
|
|
{
|
|
GstGLContextClass *context_class = (GstGLContextClass *) klass;
|
|
|
|
context_class->get_gl_context =
|
|
GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_context);
|
|
context_class->get_gl_api =
|
|
GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_api);
|
|
context_class->get_gl_platform =
|
|
GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_get_gl_platform);
|
|
context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_wrapped_context_activate);
|
|
}
|
|
|
|
static void
|
|
gst_gl_wrapped_context_init (GstGLWrappedContext * context)
|
|
{
|
|
}
|