| /* |
| * 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. |
| */ |
| |
| #ifdef 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 |
| |
| #include "gstglcontext.h" |
| #include <gst/gl/gl.h> |
| |
| #include <gmodule.h> |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "gstglcontext_private.h" |
| #include "gstglfeature.h" |
| #include "gstglfeature_private.h" |
| #include "gstglfuncs.h" |
| |
| #ifndef GL_NUM_EXTENSIONS |
| #define GL_NUM_EXTENSIONS 0x0000821d |
| #endif |
| |
| #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_CGL |
| #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 |
| |
| extern void _gst_gl_debug_enable (GstGLContext * context); |
| |
| static GPrivate current_context_key; |
| |
| static GModule *module_self; |
| static GOnce module_self_gonce = G_ONCE_INIT; |
| |
| #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 |
| /* On Linux the .so is only in -dev packages, try with a real soname |
| * Proper compilers will optimize away the strcmp */ |
| if (g_strcmp0 (G_MODULE_SUFFIX, "so") == 0) |
| module_opengl = g_module_open ("libGL.so.1", G_MODULE_BIND_LAZY); |
| |
| /* This automatically handles the suffix and even .la files */ |
| if (!module_opengl) |
| module_opengl = g_module_open ("libGL", 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 |
| /* On Linux the .so is only in -dev packages, try with a real soname |
| * Proper compilers will optimize away the strcmp */ |
| if (g_strcmp0 (G_MODULE_SUFFIX, "so") == 0) |
| module_gles2 = g_module_open ("libGLESv2.so.2", G_MODULE_BIND_LAZY); |
| |
| /* This automatically handles the suffix and even .la files */ |
| if (!module_gles2) |
| module_gles2 = g_module_open ("libGLESv2", G_MODULE_BIND_LAZY); |
| |
| #endif |
| |
| return NULL; |
| } |
| #endif |
| |
| static gpointer |
| load_self_module (gpointer user_data) |
| { |
| module_self = g_module_open (NULL, G_MODULE_BIND_LAZY); |
| |
| return NULL; |
| } |
| |
| /* Context sharedness is tracked by a refcounted pointer stored in each context |
| * object to track complex creation/deletion scenarios. As a result, |
| * sharedness can only be successfully validated between two GstGLContext's |
| * where one is not a wrapped context. |
| * |
| * As there is no API at the winsys level to tell whether two OpenGL contexts |
| * can share GL resources, this is the next best thing. |
| * |
| * XXX: we may need a way to associate two wrapped GstGLContext's as being |
| * shared however I have not come across a use case that requries this yet. |
| */ |
| struct ContextShareGroup |
| { |
| volatile int refcount; |
| }; |
| |
| static struct ContextShareGroup * |
| _context_share_group_new (void) |
| { |
| struct ContextShareGroup *ret = g_new0 (struct ContextShareGroup, 1); |
| |
| ret->refcount = 1; |
| |
| return ret; |
| } |
| |
| static struct ContextShareGroup * |
| _context_share_group_ref (struct ContextShareGroup *share) |
| { |
| g_atomic_int_inc (&share->refcount); |
| return share; |
| } |
| |
| static void |
| _context_share_group_unref (struct ContextShareGroup *share) |
| { |
| if (g_atomic_int_dec_and_test (&share->refcount)) |
| g_free (share); |
| } |
| |
| static gboolean |
| _context_share_group_is_shared (struct ContextShareGroup *share) |
| { |
| return g_atomic_int_get (&share->refcount) > 1; |
| } |
| |
| #define GST_CAT_DEFAULT gst_gl_context_debug |
| GST_DEBUG_CATEGORY (GST_CAT_DEFAULT); |
| GST_DEBUG_CATEGORY_STATIC (gst_gl_debug); |
| |
| #define gst_gl_context_parent_class parent_class |
| G_DEFINE_ABSTRACT_TYPE (GstGLContext, gst_gl_context, GST_TYPE_OBJECT); |
| |
| #define GST_GL_CONTEXT_GET_PRIVATE(o) \ |
| (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_GL_CONTEXT, GstGLContextPrivate)) |
| |
| static void _init_debug (void); |
| |
| static gpointer gst_gl_context_create_thread (GstGLContext * context); |
| static void gst_gl_context_finalize (GObject * object); |
| static void gst_gl_context_default_get_gl_platform_version (GstGLContext * |
| context, gint * major, gint * minor); |
| |
| struct _GstGLContextPrivate |
| { |
| GThread *gl_thread; |
| GThread *active_thread; |
| |
| /* conditions */ |
| GMutex render_lock; |
| GCond create_cond; |
| GCond destroy_cond; |
| |
| gboolean created; |
| gboolean alive; |
| |
| GWeakRef other_context_ref; |
| struct ContextShareGroup *sharegroup; |
| GError **error; |
| |
| gint gl_major; |
| gint gl_minor; |
| |
| gchar *gl_exts; |
| }; |
| |
| typedef struct |
| { |
| GstGLContext parent; |
| |
| guintptr handle; |
| GstGLPlatform platform; |
| GstGLAPI available_apis; |
| } GstGLWrappedContext; |
| |
| typedef struct |
| { |
| GstGLContextClass parent; |
| } GstGLWrappedContextClass; |
| |
| #define GST_TYPE_GL_WRAPPED_CONTEXT (gst_gl_wrapped_context_get_type()) |
| static GType gst_gl_wrapped_context_get_type (void); |
| G_DEFINE_TYPE (GstGLWrappedContext, gst_gl_wrapped_context, |
| GST_TYPE_GL_CONTEXT); |
| |
| #define GST_GL_WRAPPED_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_GL_WRAPPED_CONTEXT, GstGLWrappedContext)) |
| #define GST_GL_WRAPPED_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS((k), GST_TYPE_GL_CONTEXT, GstGLContextClass)) |
| #define GST_IS_GL_WRAPPED_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_GL_WRAPPED_CONTEXT)) |
| #define GST_IS_GL_WRAPPED_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_GL_WRAPPED_CONTEXT)) |
| #define GST_GL_WRAPPED_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_GL_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_display_create_window (context->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; |
| |
| g_weak_ref_init (&context->priv->other_context_ref, NULL); |
| } |
| |
| static void |
| gst_gl_context_class_init (GstGLContextClass * klass) |
| { |
| g_type_class_add_private (klass, sizeof (GstGLContextPrivate)); |
| |
| klass->get_proc_address = |
| GST_DEBUG_FUNCPTR (gst_gl_context_default_get_proc_address); |
| klass->get_gl_platform_version = |
| GST_DEBUG_FUNCPTR (gst_gl_context_default_get_gl_platform_version); |
| |
| G_OBJECT_CLASS (klass)->finalize = gst_gl_context_finalize; |
| |
| _init_debug (); |
| } |
| |
| 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"); |
| GST_DEBUG_CATEGORY_INIT (gst_gl_debug, "gldebug", 0, "OpenGL Debugging"); |
| 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 |
| * |
| * Since: 1.4 |
| */ |
| 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 for display %" GST_PTR_FORMAT |
| ", user choice:%s", display, user_choice); |
| #if GST_GL_HAVE_PLATFORM_CGL |
| if (!context && (!user_choice || g_strstr_len (user_choice, 3, "cgl"))) |
| context = GST_GL_CONTEXT (gst_gl_context_cocoa_new (display)); |
| #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 (display)); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EGL |
| if (!context && (!user_choice || g_strstr_len (user_choice, 3, "egl"))) |
| context = GST_GL_CONTEXT (gst_gl_context_egl_new (display)); |
| #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 (display)); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EAGL |
| if (!context && (!user_choice || g_strstr_len (user_choice, 4, "eagl"))) |
| context = GST_GL_CONTEXT (gst_gl_context_eagl_new (display)); |
| #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->display = gst_object_ref (display); |
| |
| GST_DEBUG_OBJECT (context, |
| "Done creating context for display %" GST_PTR_FORMAT " (user_choice:%s)", |
| display, user_choice); |
| |
| 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. |
| * |
| * Note: The caller is responsible for ensuring that the OpenGL context |
| * represented by @handle stays alive while the returned #GstGLContext is |
| * active. |
| * |
| * Returns: (transfer full): a #GstGLContext wrapping @handle |
| * |
| * Since: 1.4 |
| */ |
| GstGLContext * |
| gst_gl_context_new_wrapped (GstGLDisplay * display, guintptr handle, |
| GstGLPlatform context_type, GstGLAPI available_apis) |
| { |
| GstGLContext *context; |
| GstGLWrappedContext *context_wrap = NULL; |
| GstGLContextClass *context_class; |
| GstGLAPI display_api; |
| |
| _init_debug (); |
| |
| display_api = gst_gl_display_get_gl_api (display); |
| g_return_val_if_fail ((display_api & available_apis) != GST_GL_API_NONE, |
| NULL); |
| |
| context_wrap = g_object_new (GST_TYPE_GL_WRAPPED_CONTEXT, NULL); |
| gst_object_ref_sink (context_wrap); |
| |
| if (!context_wrap) { |
| /* subclass returned a NULL context */ |
| GST_ERROR ("Could not wrap existing context"); |
| |
| return NULL; |
| } |
| |
| context = (GstGLContext *) context_wrap; |
| |
| context->display = gst_object_ref (display); |
| context->priv->sharegroup = _context_share_group_new (); |
| context_wrap->handle = handle; |
| context_wrap->platform = context_type; |
| context_wrap->available_apis = available_apis; |
| |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| |
| #if GST_GL_HAVE_PLATFORM_GLX |
| if (context_type == GST_GL_PLATFORM_GLX) { |
| context_class->get_current_context = gst_gl_context_glx_get_current_context; |
| context_class->get_proc_address = gst_gl_context_glx_get_proc_address; |
| } |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EGL |
| if (context_type == GST_GL_PLATFORM_EGL) { |
| context_class->get_current_context = gst_gl_context_egl_get_current_context; |
| context_class->get_proc_address = gst_gl_context_egl_get_proc_address; |
| } |
| #endif |
| #if GST_GL_HAVE_PLATFORM_CGL |
| if (context_type == GST_GL_PLATFORM_CGL) { |
| context_class->get_current_context = |
| gst_gl_context_cocoa_get_current_context; |
| context_class->get_proc_address = gst_gl_context_default_get_proc_address; |
| } |
| #endif |
| #if GST_GL_HAVE_PLATFORM_WGL |
| if (context_type == GST_GL_PLATFORM_WGL) { |
| context_class->get_current_context = gst_gl_context_wgl_get_current_context; |
| context_class->get_proc_address = gst_gl_context_wgl_get_proc_address; |
| } |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EAGL |
| if (context_type == GST_GL_PLATFORM_EAGL) { |
| context_class->get_current_context = |
| gst_gl_context_eagl_get_current_context; |
| context_class->get_proc_address = gst_gl_context_default_get_proc_address; |
| } |
| #endif |
| |
| if (!context_class->get_current_context) { |
| /* we don't have API support */ |
| gst_object_unref (context); |
| return NULL; |
| } |
| |
| return context; |
| } |
| |
| /** |
| * gst_gl_context_get_current_gl_context: |
| * @context_type: a #GstGLPlatform specifying the type of context to retrieve |
| * |
| * Returns: The OpenGL context handle current in the calling thread or %NULL |
| * |
| * Since: 1.6 |
| */ |
| guintptr |
| gst_gl_context_get_current_gl_context (GstGLPlatform context_type) |
| { |
| guintptr handle = 0; |
| |
| _init_debug (); |
| |
| #if GST_GL_HAVE_PLATFORM_GLX |
| if (!handle && (context_type & GST_GL_PLATFORM_GLX) != 0) |
| handle = gst_gl_context_glx_get_current_context (); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EGL |
| if (!handle && (context_type & GST_GL_PLATFORM_EGL) != 0) |
| handle = gst_gl_context_egl_get_current_context (); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_CGL |
| if (!handle && (context_type & GST_GL_PLATFORM_CGL) != 0) |
| handle = gst_gl_context_cocoa_get_current_context (); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_WGL |
| if (!handle && (context_type & GST_GL_PLATFORM_WGL) != 0) |
| handle = gst_gl_context_wgl_get_current_context (); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EAGL |
| if (!handle && (context_type & GST_GL_PLATFORM_EAGL) != 0) |
| handle = gst_gl_context_eagl_get_current_context (); |
| #endif |
| |
| if (!handle) |
| GST_WARNING ("Could not retrieve current context"); |
| |
| return handle; |
| } |
| |
| /** |
| * gst_gl_context_get_proc_address_with_platform: |
| * @context_type: a #GstGLPlatform |
| * @gl_api: a #GstGLAPI |
| * @name: the name of the function to retrieve |
| * |
| * Attempts to use the @context_type specific GetProcAddress implementations |
| * to retreive @name. |
| * |
| * See also gst_gl_context_get_proc_address(). |
| * |
| * Returns: a function pointer for @name, or %NULL |
| * |
| * Since: 1.6 |
| */ |
| gpointer |
| gst_gl_context_get_proc_address_with_platform (GstGLPlatform context_type, |
| GstGLAPI gl_api, const gchar * name) |
| { |
| gpointer ret = NULL; |
| |
| #if GST_GL_HAVE_PLATFORM_GLX |
| if (!ret && (context_type & GST_GL_PLATFORM_GLX) != 0) |
| ret = gst_gl_context_glx_get_proc_address (gl_api, name); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_EGL |
| if (!ret && (context_type & GST_GL_PLATFORM_EGL) != 0) |
| ret = gst_gl_context_egl_get_proc_address (gl_api, name); |
| #endif |
| #if GST_GL_HAVE_PLATFORM_WGL |
| if (!ret && (context_type & GST_GL_PLATFORM_WGL) != 0) |
| ret = gst_gl_context_wgl_get_proc_address (gl_api, name); |
| #endif |
| /* CGL and EAGL rely on the default impl */ |
| |
| if (!ret) |
| ret = gst_gl_context_default_get_proc_address (gl_api, name); |
| |
| return ret; |
| } |
| |
| /** |
| * gst_gl_context_get_current_gl_api: |
| * @platform: the #GstGLPlatform to retrieve the API for |
| * @major: (out) (allow-none): the major version |
| * @minor: (out) (allow-none): the minor version |
| * |
| * If an error occurs, @major and @minor are not modified and %GST_GL_API_NONE is |
| * returned. |
| * |
| * Returns: The version supported by the OpenGL context current in the calling |
| * thread or %GST_GL_API_NONE |
| * |
| * Since: 1.6 |
| */ |
| GstGLAPI |
| gst_gl_context_get_current_gl_api (GstGLPlatform platform, guint * major, |
| guint * minor) |
| { |
| const GLubyte *(GSTGLAPI * GetString) (GLenum name); |
| #if GST_GL_HAVE_OPENGL |
| void (GSTGLAPI * GetIntegerv) (GLenum name, GLuint * n); |
| #endif |
| const gchar *version; |
| gint maj, min, n; |
| GstGLAPI ret = (1U << 31); |
| |
| _init_debug (); |
| |
| while (ret != GST_GL_API_NONE) { |
| /* FIXME: attempt to delve into the platform specific GetProcAddress */ |
| GetString = |
| gst_gl_context_get_proc_address_with_platform (platform, ret, |
| "glGetString"); |
| #if GST_GL_HAVE_OPENGL |
| GetIntegerv = |
| gst_gl_context_get_proc_address_with_platform (platform, ret, |
| "glGetIntegerv"); |
| #endif |
| if (!GetString) { |
| goto next; |
| } |
| |
| version = (const gchar *) GetString (GL_VERSION); |
| if (!version) |
| goto next; |
| |
| /* strlen (x.x) == 3 */ |
| n = strlen (version); |
| if (n < 3) |
| goto next; |
| |
| if (g_strstr_len (version, 9, "OpenGL ES")) { |
| /* strlen (OpenGL ES x.x) == 13 */ |
| if (n < 13) |
| goto next; |
| |
| sscanf (&version[10], "%d.%d", &maj, &min); |
| |
| if (maj <= 0 || min < 0) |
| goto next; |
| |
| if (maj == 1) { |
| ret = GST_GL_API_GLES1; |
| break; |
| } else if (maj == 2 || maj == 3) { |
| ret = GST_GL_API_GLES2; |
| break; |
| } |
| |
| goto next; |
| } else { |
| sscanf (version, "%d.%d", &maj, &min); |
| |
| if (maj <= 0 || min < 0) |
| goto next; |
| |
| #if GST_GL_HAVE_OPENGL |
| if (GetIntegerv && (maj > 3 || (maj == 3 && min > 1))) { |
| GLuint context_flags = 0; |
| |
| ret = GST_GL_API_NONE; |
| GetIntegerv (GL_CONTEXT_PROFILE_MASK, &context_flags); |
| if (context_flags & GL_CONTEXT_CORE_PROFILE_BIT) |
| ret |= GST_GL_API_OPENGL3; |
| if (context_flags & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) |
| ret |= GST_GL_API_OPENGL; |
| break; |
| } |
| #endif |
| ret = GST_GL_API_OPENGL; |
| break; |
| } |
| |
| next: |
| /* iterate through the apis */ |
| ret >>= 1; |
| } |
| |
| if (ret == GST_GL_API_NONE) |
| return GST_GL_API_NONE; |
| |
| if (major) |
| *major = maj; |
| if (minor) |
| *minor = min; |
| |
| return ret; |
| } |
| |
| 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); |
| |
| g_mutex_lock (&context->priv->render_lock); |
| if (context->priv->alive) { |
| GST_INFO_OBJECT (context, "send quit gl window loop"); |
| gst_gl_window_quit (context->window); |
| |
| GST_INFO_OBJECT (context, "joining gl thread"); |
| while (context->priv->alive) |
| g_cond_wait (&context->priv->destroy_cond, &context->priv->render_lock); |
| GST_INFO_OBJECT (context, "gl thread joined"); |
| |
| if (context->priv->gl_thread) { |
| g_thread_unref (context->priv->gl_thread); |
| context->priv->gl_thread = NULL; |
| } |
| } |
| g_mutex_unlock (&context->priv->render_lock); |
| |
| gst_gl_window_set_close_callback (context->window, NULL, NULL, NULL); |
| gst_object_unref (context->window); |
| context->window = NULL; |
| } |
| |
| if (context->priv->active_thread) { |
| g_thread_unref (context->priv->active_thread); |
| context->priv->active_thread = NULL; |
| } |
| |
| if (context->priv->gl_thread) { |
| g_thread_unref (context->priv->gl_thread); |
| context->priv->gl_thread = NULL; |
| } |
| |
| if (context->priv->sharegroup) { |
| _context_share_group_unref (context->priv->sharegroup); |
| context->priv->sharegroup = NULL; |
| } |
| |
| if (context->display) { |
| gst_object_unref (context->display); |
| context->display = NULL; |
| } |
| |
| 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->create_cond); |
| g_cond_clear (&context->priv->destroy_cond); |
| |
| g_free (context->priv->gl_exts); |
| g_weak_ref_clear (&context->priv->other_context_ref); |
| |
| GST_DEBUG_OBJECT (context, "End of finalize"); |
| 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 |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_gl_context_activate (GstGLContext * context, gboolean activate) |
| { |
| GstGLContextClass *context_class; |
| gboolean result; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| g_return_val_if_fail (context_class->activate != NULL, FALSE); |
| |
| GST_DEBUG_OBJECT (context, "activate:%d", activate); |
| |
| GST_OBJECT_LOCK (context); |
| result = context_class->activate (context, activate); |
| |
| if (result && activate) { |
| GThread *old_thread = context->priv->active_thread; |
| context->priv->active_thread = g_thread_ref (g_thread_self ()); |
| if (old_thread) { |
| g_thread_unref (old_thread); |
| } |
| |
| g_private_set (¤t_context_key, context); |
| } else { |
| if (context->priv->active_thread) { |
| g_thread_unref (context->priv->active_thread); |
| context->priv->active_thread = NULL; |
| } |
| g_private_set (¤t_context_key, NULL); |
| } |
| GST_OBJECT_UNLOCK (context); |
| |
| return result; |
| } |
| |
| /** |
| * gst_gl_context_get_thread: |
| * @context: a #GstGLContext |
| * |
| * Returns: (transfer full): The #GThread, @context is current in or NULL |
| * |
| * Since: 1.6 |
| */ |
| GThread * |
| gst_gl_context_get_thread (GstGLContext * context) |
| { |
| GThread *ret; |
| |
| GST_OBJECT_LOCK (context); |
| ret = context->priv->active_thread; |
| if (ret) |
| g_thread_ref (ret); |
| GST_OBJECT_UNLOCK (context); |
| |
| return ret; |
| } |
| |
| /** |
| * 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 available OpenGL api |
| * |
| * Since: 1.4 |
| */ |
| GstGLAPI |
| gst_gl_context_get_gl_api (GstGLContext * context) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_val_if_fail (GST_IS_GL_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 retrieved using this method. |
| * |
| * Note: This function may return valid function pointers that may not be valid |
| * to call in @context. The caller is responsible for ensuring that the |
| * returned function is a valid function to call in @context by either checking |
| * the OpenGL API and version or for an appropriate OpenGL extension. |
| * |
| * Note: On success, you need to cast the returned function pointer to the |
| * correct type to be able to call it correctly. On 32-bit Windows, this will |
| * include the %GSTGLAPI identifier to use the correct calling convention. |
| * e.g. |
| * |
| * |[<!-- language="C" --> |
| * void (GSTGLAPI *PFN_glGetIntegerv) (GLenum name, GLint * ret) |
| * ]| |
| * |
| * Returns: a function pointer or %NULL |
| * |
| * Since: 1.4 |
| */ |
| gpointer |
| gst_gl_context_get_proc_address (GstGLContext * context, const gchar * name) |
| { |
| gpointer ret; |
| GstGLContextClass *context_class; |
| GstGLAPI gl_api; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL); |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| g_return_val_if_fail (context_class->get_proc_address != NULL, NULL); |
| |
| gl_api = gst_gl_context_get_gl_api (context); |
| ret = context_class->get_proc_address (gl_api, name); |
| |
| return ret; |
| } |
| |
| /** |
| * gst_gl_context_default_get_proc_address: |
| * @gl_api: a #GstGLAPI |
| * @name: then function to get the address of |
| * |
| * A default implementation of the various GetProcAddress functions that looks |
| * for @name in the OpenGL shared libraries or in the current process. |
| * |
| * See also: gst_gl_context_get_proc_address() |
| * |
| * Returns: an address pointing to @name or %NULL |
| * |
| * Since: 1.4 |
| */ |
| gpointer |
| gst_gl_context_default_get_proc_address (GstGLAPI gl_api, const gchar * name) |
| { |
| gpointer ret = NULL; |
| |
| /* 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 */ |
| g_once (&module_self_gonce, load_self_module, NULL); |
| 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 |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_gl_context_set_window (GstGLContext * context, GstGLWindow * window) |
| { |
| g_return_val_if_fail (!GST_IS_GL_WRAPPED_CONTEXT (context), FALSE); |
| |
| GST_DEBUG_OBJECT (context, "window:%" GST_PTR_FORMAT, window); |
| |
| /* we can't change the window while we are running */ |
| if (context->priv->alive) |
| return FALSE; |
| |
| if (window) |
| 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: (transfer full) (nullable): the currently set window |
| * |
| * Since: 1.4 |
| */ |
| GstGLWindow * |
| gst_gl_context_get_window (GstGLContext * context) |
| { |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL); |
| |
| if (GST_IS_GL_WRAPPED_CONTEXT (context)) { |
| GST_WARNING_OBJECT (context, "context is not toplevel, returning NULL"); |
| return NULL; |
| } |
| |
| _ensure_window (context); |
| |
| return gst_object_ref (context->window); |
| } |
| |
| /** |
| * gst_gl_context_can_share: |
| * @context: a #GstGLContext |
| * @other_context: another #GstGLContext |
| * |
| * Note: This will always fail for two wrapped #GstGLContext's |
| * |
| * Returns: whether @context and @other_context are able to share OpenGL |
| * resources. |
| * |
| * Since: 1.6 |
| */ |
| gboolean |
| gst_gl_context_can_share (GstGLContext * context, GstGLContext * other_context) |
| { |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (other_context), FALSE); |
| |
| /* check if the contexts are descendants or the root nodes are the same */ |
| return context->priv->sharegroup != NULL |
| && context->priv->sharegroup == other_context->priv->sharegroup; |
| } |
| |
| /** |
| * 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 with the specified @other_context as a context |
| * to share shareable OpenGL objects with. See the OpenGL specification for |
| * what is shared between OpenGL 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 |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_gl_context_create (GstGLContext * context, |
| GstGLContext * other_context, GError ** error) |
| { |
| gboolean alive = FALSE; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| g_return_val_if_fail (!GST_IS_GL_WRAPPED_CONTEXT (context), FALSE); |
| |
| GST_DEBUG_OBJECT (context, " other_context:%" GST_PTR_FORMAT, other_context); |
| |
| _ensure_window (context); |
| |
| g_mutex_lock (&context->priv->render_lock); |
| |
| if (!context->priv->created) { |
| g_weak_ref_set (&context->priv->other_context_ref, other_context); |
| context->priv->error = error; |
| if (other_context == NULL) |
| context->priv->sharegroup = _context_share_group_new (); |
| else |
| context->priv->sharegroup = |
| _context_share_group_ref (other_context->priv->sharegroup); |
| |
| context->priv->gl_thread = g_thread_new ("gstglcontext", |
| (GThreadFunc) gst_gl_context_create_thread, context); |
| |
| while (!context->priv->created) |
| g_cond_wait (&context->priv->create_cond, &context->priv->render_lock); |
| |
| GST_INFO_OBJECT (context, "gl thread created"); |
| } |
| |
| alive = context->priv->alive; |
| |
| g_mutex_unlock (&context->priv->render_lock); |
| |
| return alive; |
| } |
| |
| static gboolean |
| _create_context_info (GstGLContext * context, GstGLAPI gl_api, gint * gl_major, |
| gint * gl_minor, GError ** error) |
| { |
| const GstGLFuncs *gl; |
| guint maj = 0, min = 0; |
| GLenum gl_err = GL_NO_ERROR; |
| const gchar *opengl_version = NULL; |
| |
| gl = context->gl_vtable; |
| |
| if (!gl->GetString || !gl->GetString (GL_VERSION)) { |
| g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED, |
| "glGetString not defined or returned invalid value"); |
| return FALSE; |
| } |
| |
| if (!gl->GetString (GL_SHADING_LANGUAGE_VERSION)) { |
| g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED, |
| "No GL shader support available"); |
| return FALSE; |
| } |
| |
| GST_INFO_OBJECT (context, "GL_VERSION: %s", |
| GST_STR_NULL ((const gchar *) gl->GetString (GL_VERSION))); |
| GST_INFO_OBJECT (context, "GL_SHADING_LANGUAGE_VERSION: %s", |
| GST_STR_NULL ((const gchar *) |
| gl->GetString (GL_SHADING_LANGUAGE_VERSION))); |
| GST_INFO_OBJECT (context, "GL_VENDOR: %s", |
| GST_STR_NULL ((const gchar *) gl->GetString (GL_VENDOR))); |
| GST_INFO_OBJECT (context, "GL_RENDERER: %s", |
| GST_STR_NULL ((const gchar *) 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 = (const gchar *) gl->GetString (GL_VERSION); |
| if (opengl_version && gl_api & GST_GL_API_GLES2) |
| /* gles starts with "OpenGL ES " */ |
| opengl_version = &opengl_version[10]; |
| |
| if (opengl_version) |
| sscanf (opengl_version, "%d.%d", &maj, &min); |
| |
| /* OpenGL > 1.2.0 */ |
| if (gl_api & GST_GL_API_OPENGL || gl_api & GST_GL_API_OPENGL3) { |
| 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) |
| { |
| context->priv->created = TRUE; |
| GST_INFO_OBJECT (context, "gl thread running"); |
| g_cond_signal (&context->priv->create_cond); |
| 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; |
| GLint i = 0; |
| GLint n = 0; |
| |
| 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; |
| GstGLAPI compiled_api, user_api, gl_api, display_api; |
| gchar *api_string; |
| gchar *compiled_api_s; |
| gchar *user_api_s; |
| gchar *display_api_s; |
| const gchar *user_choice; |
| GError **error; |
| GstGLContext *other_context; |
| |
| g_mutex_lock (&context->priv->render_lock); |
| |
| GST_DEBUG_OBJECT (context, "Creating thread"); |
| |
| error = context->priv->error; |
| other_context = g_weak_ref_get (&context->priv->other_context_ref); |
| |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| window_class = GST_GL_WINDOW_GET_CLASS (context->window); |
| |
| display_api = gst_gl_display_get_gl_api_unlocked (context->display); |
| if (display_api == GST_GL_API_NONE) { |
| g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_WRONG_API, |
| "Cannot create context with satisfying requested apis " |
| "(display has no GL api!)"); |
| goto failure; |
| } |
| |
| if (window_class->open) { |
| if (!window_class->open (context->window, error)) { |
| GST_WARNING_OBJECT (context, "Failed to open window"); |
| g_assert (error == NULL || *error != NULL); |
| goto failure; |
| } |
| } |
| |
| compiled_api = _compiled_api (); |
| compiled_api_s = gst_gl_api_to_string (compiled_api); |
| |
| user_choice = g_getenv ("GST_GL_API"); |
| user_api = gst_gl_api_from_string (user_choice); |
| user_api_s = gst_gl_api_to_string (user_api); |
| |
| display_api_s = gst_gl_api_to_string (display_api); |
| |
| if ((user_api & compiled_api & display_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), display api (%s)", user_api_s, |
| compiled_api_s, display_api_s); |
| g_free (user_api_s); |
| g_free (compiled_api_s); |
| g_free (display_api_s); |
| goto failure; |
| } |
| |
| if (context_class->choose_format && |
| !context_class->choose_format (context, error)) { |
| GST_WARNING_OBJECT (context, "Failed to choose format"); |
| g_assert (error == NULL || *error != NULL); |
| g_free (compiled_api_s); |
| g_free (user_api_s); |
| g_free (display_api_s); |
| goto failure; |
| } |
| |
| GST_INFO_OBJECT (context, |
| "Attempting to create opengl context. user chosen api(s) (%s), " |
| "compiled api support (%s) display api (%s)", user_api_s, compiled_api_s, |
| display_api_s); |
| |
| if (!context_class->create_context (context, |
| compiled_api & user_api & display_api, other_context, error)) { |
| GST_WARNING_OBJECT (context, "Failed to create context"); |
| g_assert (error == NULL || *error != NULL); |
| g_free (compiled_api_s); |
| g_free (user_api_s); |
| g_free (display_api_s); |
| goto failure; |
| } |
| GST_INFO_OBJECT (context, "created context"); |
| |
| if (!gst_gl_context_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_s); |
| g_free (display_api_s); |
| goto failure; |
| } |
| |
| gl_api = gst_gl_context_get_gl_api (context); |
| g_assert (gl_api != GST_GL_API_NONE && gl_api != GST_GL_API_ANY); |
| |
| api_string = gst_gl_api_to_string (gl_api); |
| GST_INFO_OBJECT (context, "available GL APIs: %s", api_string); |
| |
| if (((compiled_api & gl_api & display_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_s, compiled_api_s, api_string); |
| g_free (api_string); |
| g_free (compiled_api_s); |
| g_free (user_api_s); |
| g_free (display_api_s); |
| goto failure; |
| } |
| |
| g_free (api_string); |
| g_free (compiled_api_s); |
| g_free (user_api_s); |
| g_free (display_api_s); |
| |
| GST_DEBUG_OBJECT (context, "Filling info"); |
| if (!gst_gl_context_fill_info (context, error)) { |
| g_assert (error == NULL || *error != NULL); |
| goto failure; |
| } |
| |
| context->priv->alive = TRUE; |
| |
| #if !defined(GST_DISABLE_GST_DEBUG) |
| _gst_gl_debug_enable (context); |
| #endif |
| |
| if (other_context) { |
| GST_DEBUG_OBJECT (context, "Unreffing other_context %" GST_PTR_FORMAT, |
| other_context); |
| gst_object_unref (other_context); |
| } |
| |
| /* unlocking of the render_lock happens when the |
| * context's loop is running from inside that loop */ |
| gst_gl_window_send_message_async (context->window, |
| (GstGLWindowCB) _unlock_create_thread, context, NULL); |
| |
| gst_gl_window_run (context->window); |
| |
| GST_INFO_OBJECT (context, "loop exited"); |
| |
| g_mutex_lock (&context->priv->render_lock); |
| context->priv->alive = FALSE; |
| |
| gst_gl_context_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); |
| } |
| |
| context->priv->created = FALSE; |
| g_cond_signal (&context->priv->destroy_cond); |
| g_mutex_unlock (&context->priv->render_lock); |
| |
| return NULL; |
| |
| failure: |
| { |
| if (other_context) |
| gst_object_unref (other_context); |
| |
| /* A context that fails to be created is considered created but not alive |
| * and will never be able to be alive as creation can't happen */ |
| context->priv->created = TRUE; |
| g_cond_signal (&context->priv->create_cond); |
| g_mutex_unlock (&context->priv->render_lock); |
| return NULL; |
| } |
| } |
| |
| /** |
| * gst_gl_context_destroy: |
| * @context: a #GstGLContext: |
| * |
| * Destroys an OpenGL context. |
| * |
| * Should only be called after gst_gl_context_create() has been successfully |
| * called for this context. |
| * |
| * Since: 1.6 |
| */ |
| void |
| gst_gl_context_destroy (GstGLContext * context) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| g_return_if_fail (context_class->destroy_context != NULL); |
| |
| context_class->destroy_context (context); |
| } |
| |
| /** |
| * gst_gl_context_fill_info: |
| * @context: a #GstGLContext: |
| * @error: (allow-none): a #GError to fill on failure |
| * |
| * Fills @context's info (version, extensions, vtable, etc) from the GL |
| * context in the current thread. Typically used with wrapped contexts to |
| * allow wrapped contexts to be used as regular #GstGLContext's. |
| * |
| * Since: 1.6 |
| */ |
| gboolean |
| gst_gl_context_fill_info (GstGLContext * context, GError ** error) |
| { |
| GstGLFuncs *gl; |
| GString *ext_g_str = NULL; |
| const gchar *ext_const_c_str = NULL; |
| GstGLAPI gl_api; |
| gboolean ret; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| g_return_val_if_fail (context->priv->active_thread == g_thread_self (), |
| FALSE); |
| |
| gl = context->gl_vtable; |
| gl_api = gst_gl_context_get_gl_api (context); |
| |
| 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 */ |
| ret = _create_context_info (context, gl_api, &context->priv->gl_major, |
| &context->priv->gl_minor, error); |
| |
| if (!ret) { |
| g_assert (error == NULL || *error != NULL); |
| 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); |
| |
| context->priv->gl_exts = g_string_free (ext_g_str, FALSE); |
| } 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); |
| |
| context->priv->gl_exts = g_strdup (ext_const_c_str); |
| } |
| |
| if (gl_api & GST_GL_API_OPENGL3 |
| && !gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 4, 1) |
| && !gst_gl_check_extension ("GL_ARB_ES2_compatibility", |
| context->priv->gl_exts)) { |
| g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED, |
| "An opengl3 context created but the required ES2 compatibility was not found"); |
| goto failure; |
| } |
| |
| /* Does not implement OES_vertex_array_object properly, see |
| * https://bugzilla.gnome.org/show_bug.cgi?id=750185 */ |
| if (g_strcmp0 ((const gchar *) gl->GetString (GL_VENDOR), |
| "Imagination Technologies") == 0 |
| && g_strcmp0 ((const gchar *) gl->GetString (GL_RENDERER), |
| "PowerVR SGX 544MP") == 0) { |
| gl->GenVertexArrays = NULL; |
| gl->DeleteVertexArrays = NULL; |
| gl->BindVertexArray = NULL; |
| gl->IsVertexArray = NULL; |
| } |
| |
| return TRUE; |
| |
| failure: |
| return FALSE; |
| } |
| |
| /** |
| * gst_gl_context_get_gl_context: |
| * @context: a #GstGLContext: |
| * |
| * Gets the backing OpenGL context used by @context. |
| * |
| * Returns: The platform specific backing OpenGL context |
| * |
| * Since: 1.4 |
| */ |
| guintptr |
| gst_gl_context_get_gl_context (GstGLContext * context) |
| { |
| GstGLContextClass *context_class; |
| guintptr result; |
| |
| g_return_val_if_fail (GST_IS_GL_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 |
| * |
| * Since: 1.4 |
| */ |
| GstGLPlatform |
| gst_gl_context_get_gl_platform (GstGLContext * context) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_val_if_fail (GST_IS_GL_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: (transfer full): the #GstGLDisplay associated with this @context |
| * |
| * Since: 1.4 |
| */ |
| GstGLDisplay * |
| gst_gl_context_get_display (GstGLContext * context) |
| { |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL); |
| |
| return gst_object_ref (context->display); |
| } |
| |
| typedef struct |
| { |
| GstGLContext *context; |
| GstGLContextThreadFunc func; |
| gpointer data; |
| } RunGenericData; |
| |
| static void |
| _gst_gl_context_thread_run_generic (RunGenericData * data) |
| { |
| GST_TRACE_OBJECT (data->context, "running function:%p data:%p", data->func, |
| data->data); |
| |
| data->func (data->context, data->data); |
| } |
| |
| /** |
| * gst_gl_context_thread_add: |
| * @context: a #GstGLContext |
| * @func: (scope call): a #GstGLContextThreadFunc |
| * @data: (closure): user data to call @func with |
| * |
| * Execute @func in the OpenGL thread of @context with @data |
| * |
| * MT-safe |
| * |
| * Since: 1.4 |
| */ |
| void |
| gst_gl_context_thread_add (GstGLContext * context, |
| GstGLContextThreadFunc func, gpointer data) |
| { |
| GstGLWindow *window; |
| RunGenericData rdata; |
| |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| g_return_if_fail (func != NULL); |
| |
| if (GST_IS_GL_WRAPPED_CONTEXT (context)) |
| g_return_if_fail (context->priv->active_thread == g_thread_self ()); |
| |
| if (context->priv->active_thread == g_thread_self ()) { |
| func (context, data); |
| return; |
| } |
| |
| 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. |
| * |
| * Since: 1.4 |
| */ |
| void |
| gst_gl_context_get_gl_version (GstGLContext * context, gint * maj, gint * min) |
| { |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| g_return_if_fail (!(maj == NULL && min == NULL)); |
| |
| if (maj) |
| *maj = context->priv->gl_major; |
| |
| if (min) |
| *min = context->priv->gl_minor; |
| } |
| |
| /** |
| * gst_gl_context_check_gl_version: |
| * @context: a #GstGLContext |
| * @api: api type required |
| * @maj: major version required |
| * @min: minor version required |
| * |
| * Returns: whether OpenGL context implements the required api and specified |
| * version. |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_gl_context_check_gl_version (GstGLContext * context, GstGLAPI api, |
| gint maj, gint min) |
| { |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| |
| if (maj > context->priv->gl_major) |
| return FALSE; |
| |
| if ((gst_gl_context_get_gl_api (context) & api) == GST_GL_API_NONE) |
| return FALSE; |
| |
| if (maj < context->priv->gl_major) |
| return TRUE; |
| |
| if (min > context->priv->gl_minor) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_gl_context_check_feature: |
| * @context: a #GstGLContext |
| * @feature: a platform specific feature |
| * |
| * Check for an OpenGL @feature being supported. |
| * |
| * Note: Most features require that the context be created before it is |
| * possible to determine their existence and so will fail if that is not the |
| * case. |
| * |
| * Returns: Whether @feature is supported by @context |
| * |
| * Since: 1.4 |
| */ |
| gboolean |
| gst_gl_context_check_feature (GstGLContext * context, const gchar * feature) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| g_return_val_if_fail (feature != NULL, FALSE); |
| |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| |
| if (g_strstr_len (feature, 3, "GL_")) |
| return gst_gl_check_extension (feature, context->priv->gl_exts); |
| |
| if (!context_class->check_feature) |
| return FALSE; |
| |
| return context_class->check_feature (context, feature); |
| } |
| |
| /** |
| * gst_gl_context_get_current: |
| * |
| * See also gst_gl_context_activate(). |
| * |
| * Returns: (transfer none): the #GstGLContext active in the current thread or %NULL |
| * |
| * Since: 1.6 |
| */ |
| GstGLContext * |
| gst_gl_context_get_current (void) |
| { |
| return g_private_get (¤t_context_key); |
| } |
| |
| /** |
| * gst_gl_context_is_shared: |
| * @context: a #GstGLContext |
| * |
| * Returns: Whether the #GstGLContext has been shared with another #GstGLContext |
| * |
| * Since: 1.8 |
| */ |
| gboolean |
| gst_gl_context_is_shared (GstGLContext * context) |
| { |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| |
| if (!context->priv->sharegroup) |
| return FALSE; |
| |
| if (GST_IS_GL_WRAPPED_CONTEXT (context)) |
| g_return_val_if_fail (context->priv->active_thread, FALSE); |
| else |
| g_return_val_if_fail (context->priv->alive, FALSE); |
| |
| return _context_share_group_is_shared (context->priv->sharegroup); |
| } |
| |
| /** |
| * gst_gl_context_set_shared_with: |
| * @context: a wrapped #GstGLContext |
| * @share: another #GstGLContext |
| * |
| * Will internally set @context as shared with @share |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_gl_context_set_shared_with (GstGLContext * context, GstGLContext * share) |
| { |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| g_return_if_fail (GST_IS_GL_CONTEXT (share)); |
| g_return_if_fail (!gst_gl_context_is_shared (context)); |
| /* XXX: may be a little too strict */ |
| g_return_if_fail (GST_IS_GL_WRAPPED_CONTEXT (context)); |
| |
| if (context->priv->sharegroup) |
| _context_share_group_unref (context->priv->sharegroup); |
| context->priv->sharegroup = |
| _context_share_group_ref (share->priv->sharegroup); |
| } |
| |
| static void |
| gst_gl_context_default_get_gl_platform_version (GstGLContext * context, |
| gint * major, gint * minor) |
| { |
| if (major) |
| *major = 0; |
| if (minor) |
| *minor = 0; |
| } |
| |
| /** |
| * gst_gl_context_get_gl_platform_version: |
| * @context: a #GstGLContext |
| * @major: (out): return for the major version |
| * @minor: (out): return for the minor version |
| * |
| * Get the version of the OpenGL platform (GLX, EGL, etc) used. Only valid |
| * after a call to gst_gl_context_create_context(). |
| */ |
| void |
| gst_gl_context_get_gl_platform_version (GstGLContext * context, gint * major, |
| gint * minor) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| g_return_if_fail (major != NULL); |
| g_return_if_fail (minor != NULL); |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| g_return_if_fail (context_class->get_gl_platform_version != NULL); |
| |
| context_class->get_gl_platform_version (context, major, minor); |
| } |
| |
| /** |
| * gst_gl_context_swap_buffers: |
| * @context: a #GstGLContext |
| * |
| * Swap the front and back buffers on the window attached to @context. |
| * This will display the frame on the next refresh cycle. |
| */ |
| void |
| gst_gl_context_swap_buffers (GstGLContext * context) |
| { |
| GstGLContextClass *context_class; |
| |
| g_return_if_fail (GST_IS_GL_CONTEXT (context)); |
| context_class = GST_GL_CONTEXT_GET_CLASS (context); |
| g_return_if_fail (context_class->swap_buffers != NULL); |
| |
| context_class->swap_buffers (context); |
| } |
| |
| 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) |
| { |
| if (activate) { |
| GThread *old_thread = context->priv->gl_thread; |
| context->priv->gl_thread = g_thread_ref (g_thread_self ()); |
| if (old_thread) { |
| g_thread_unref (old_thread); |
| } |
| } else { |
| if (context->priv->gl_thread) { |
| g_thread_unref (context->priv->gl_thread); |
| context->priv->gl_thread = NULL; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| 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) |
| { |
| } |