GstGLWindow: Introduce navigation thread

This thread dispatches navigation events. It is needed to avoid deadlocks
between window backend threads that emit navigation events (e.g. X11/GMainLoop
thread) and consumers of navigation events such as glimagesink, see
https://bugzilla.gnome.org/show_bug.cgi?id=733661

GstGlWindow_x11 thread is changed to invoke the navigation thread for navigation
dispatching, instead of emiting the event itself. Othe backends beside X11 do
not dispatch navigation events yet, but should use this thread when dispatching
these events in the future.

The navigation thread is currently part of GstGLWindow and not implemented in
separate subclasses / backends. This will be needed in the future.

gst_gl_window_x11_get_surface_dimensions is also changed to use a cached value
of the window's width, height. These values are now retrieved in the X11
thread, function gst_gl_window_x11_handle_event. This change is needed because
otherwise the XGetWindowAttributes gets called from the navigation thread,
leading to xlib aborting due to multithreaded access (if XInitThreads is not
called before, as is the case for gst-launch)
This commit is contained in:
Vasilis Liaskovitis 2014-09-09 12:01:47 +02:00 committed by Matthew Waters
parent 10ece74f10
commit 3c3b78508f
4 changed files with 191 additions and 13 deletions

View file

@ -79,10 +79,16 @@ G_DEFINE_ABSTRACT_TYPE (GstGLWindow, gst_gl_window, GST_TYPE_OBJECT);
static void gst_gl_window_default_send_message (GstGLWindow * window,
GstGLWindowCB callback, gpointer data);
static gpointer gst_gl_window_navigation_thread (GstGLWindow * window);
void gst_gl_window_run_navigation (GstGLWindow * window);
void gst_gl_window_open_navigation (GstGLWindow * window);
void gst_gl_window_close_navigation (GstGLWindow * window);
void gst_gl_window_quit_navigation (GstGLWindow * window);
struct _GstGLWindowPrivate
{
GThread *gl_thread;
GThread *navigation_thread;
gboolean alive;
};
@ -128,6 +134,11 @@ gst_gl_window_init (GstGLWindow * window)
window->priv = GST_GL_WINDOW_GET_PRIVATE (window);
g_mutex_init (&window->lock);
g_mutex_init (&window->nav_lock);
g_cond_init (&window->nav_create_cond);
g_cond_init (&window->nav_destroy_cond);
window->nav_created = FALSE;
window->nav_alive = FALSE;
window->is_drawing = FALSE;
g_weak_ref_init (&window->context_ref, NULL);
@ -235,6 +246,17 @@ gst_gl_window_new (GstGLDisplay * display)
window->display = gst_object_ref (display);
g_mutex_lock (&window->nav_lock);
if (!window->nav_created) {
window->priv->navigation_thread = g_thread_new ("gstglnavigation",
(GThreadFunc) gst_gl_window_navigation_thread, window);
g_cond_wait (&window->nav_create_cond, &window->nav_lock);
window->nav_created = TRUE;
}
g_mutex_unlock (&window->nav_lock);
return window;
}
@ -243,9 +265,22 @@ gst_gl_window_finalize (GObject * object)
{
GstGLWindow *window = GST_GL_WINDOW (object);
if (window->nav_alive) {
g_mutex_lock (&window->nav_lock);
GST_INFO ("send quit navigation loop");
gst_gl_window_quit_navigation (window);
while (window->nav_alive) {
g_cond_wait (&window->nav_destroy_cond, &window->nav_lock);
}
g_mutex_unlock (&window->nav_lock);
}
g_weak_ref_clear (&window->context_ref);
g_mutex_clear (&window->lock);
g_mutex_clear (&window->nav_lock);
g_cond_clear (&window->nav_create_cond);
g_cond_clear (&window->nav_destroy_cond);
gst_object_unref (window->display);
G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object);
@ -336,6 +371,21 @@ gst_gl_window_run (GstGLWindow * window)
window_class->run (window);
}
/**
* gst_gl_window_run_navigation:
* @window: a #GstGLWindow
*
* Start the execution of the navigation runloop.
*/
void
gst_gl_window_run_navigation (GstGLWindow * window)
{
g_return_if_fail (GST_GL_IS_WINDOW (window));
g_return_if_fail (window->navigation_context != NULL);
g_return_if_fail (window->navigation_loop != NULL);
g_main_loop_run (window->navigation_loop);
}
/**
* gst_gl_window_quit:
* @window: a #GstGLWindow
@ -613,7 +663,6 @@ gst_gl_window_get_surface_dimensions (GstGLWindow * window, guint * width,
guint * height)
{
GstGLWindowClass *window_class;
g_return_if_fail (GST_GL_IS_WINDOW (window));
window_class = GST_GL_WINDOW_GET_CLASS (window);
g_return_if_fail (window_class->get_surface_dimensions != NULL);
@ -660,6 +709,54 @@ gst_gl_dummy_window_run (GstGLWindow * window)
g_main_loop_run (dummy->loop);
}
void
gst_gl_window_open_navigation (GstGLWindow * window)
{
g_return_if_fail (GST_GL_IS_WINDOW (window));
g_mutex_lock (&window->nav_lock);
window->navigation_context = g_main_context_new ();
window->navigation_loop = g_main_loop_new (window->navigation_context, FALSE);
g_main_context_push_thread_default (window->navigation_context);
window->nav_alive = TRUE;
g_cond_signal (&window->nav_create_cond);
g_mutex_unlock (&window->nav_lock);
}
void
gst_gl_window_close_navigation (GstGLWindow * window)
{
g_return_if_fail (GST_GL_IS_WINDOW (window));
g_return_if_fail (window->navigation_context != NULL);
g_return_if_fail (window->navigation_loop != NULL);
g_mutex_lock (&window->nav_lock);
window->nav_alive = FALSE;
g_main_context_pop_thread_default (window->navigation_context);
g_main_loop_unref (window->navigation_loop);
g_main_context_unref (window->navigation_context);
g_cond_signal (&window->nav_destroy_cond);
g_mutex_unlock (&window->nav_lock);
}
void
gst_gl_window_quit_navigation (GstGLWindow * window)
{
g_return_if_fail (GST_GL_IS_WINDOW (window));
g_main_loop_quit (window->navigation_loop);
}
static gpointer
gst_gl_window_navigation_thread (GstGLWindow * window)
{
gst_gl_window_open_navigation (window);
gst_gl_window_run_navigation (window);
GST_INFO ("navigation loop exited\n");
gst_gl_window_close_navigation (window);
return NULL;
}
typedef struct _GstGLMessage
{
GstGLWindowCB callback;
@ -797,6 +894,20 @@ gst_gl_dummy_window_new (void)
return g_object_new (gst_gl_dummy_window_get_type (), NULL);
}
gboolean
gst_gl_window_key_event_cb (gpointer data)
{
struct key_event *key_data = (struct key_event *) data;
GST_DEBUG
("%s called data struct %p window %p key %s event %s ",
__func__, key_data, key_data->window, key_data->key_str,
key_data->event_type);
gst_gl_window_send_key_event (GST_GL_WINDOW (key_data->window),
key_data->event_type, key_data->key_str);
g_slice_free (struct key_event, key_data);
return G_SOURCE_REMOVE;
}
void
gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
const char *key_str)
@ -805,6 +916,20 @@ gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
event_type, key_str);
}
gboolean
gst_gl_window_mouse_event_cb (gpointer data)
{
struct mouse_event *mouse_data = (struct mouse_event *) data;
GST_DEBUG ("%s called data struct %p mouse event %s button %d at %g, %g",
__func__, mouse_data, mouse_data->event_type, mouse_data->button,
mouse_data->posx, mouse_data->posy);
gst_gl_window_send_mouse_event (GST_GL_WINDOW (mouse_data->window),
mouse_data->event_type, mouse_data->button, mouse_data->posx,
mouse_data->posy);
g_slice_free (struct mouse_event, mouse_data);
return G_SOURCE_REMOVE;
}
void
gst_gl_window_send_mouse_event (GstGLWindow * window, const char *event_type,
int button, double posx, double posy)

View file

@ -67,6 +67,11 @@ struct _GstGLWindow {
GstObject parent;
GMutex lock;
GMutex nav_lock;
GCond nav_create_cond;
GCond nav_destroy_cond;
gboolean nav_created;
gboolean nav_alive;
GstGLDisplay *display;
GWeakRef context_ref;
@ -87,6 +92,8 @@ struct _GstGLWindow {
/*< private >*/
gpointer _reserved[GST_PADDING];
GMainContext *navigation_context;
GMainLoop *navigation_loop;
GstGLWindowPrivate *priv;
};
@ -129,6 +136,21 @@ struct _GstGLWindowClass {
gpointer _reserved[GST_PADDING];
};
struct key_event
{
GstGLWindow *window;
const char *event_type;
const char *key_str;
};
struct mouse_event
{
GstGLWindow *window;
const char *event_type;
int button;
double posx;
double posy;
};
/* methods */
GQuark gst_gl_window_error_quark (void);
@ -156,6 +178,12 @@ GstGLContext * gst_gl_window_get_context (GstGLWindow *window);
gboolean gst_gl_window_is_running (GstGLWindow *window);
gboolean
gst_gl_window_key_event_cb (gpointer data);
gboolean
gst_gl_window_mouse_event_cb (gpointer data);
void gst_gl_window_send_key_event(GstGLWindow * window, const char * event_type,
const char * key_str);
void gst_gl_window_send_mouse_event(GstGLWindow * window, const char *

View file

@ -507,6 +507,9 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
gboolean ret = TRUE;
const char *key_str = NULL;
KeySym keysym;
struct mouse_event *mouse_data;
struct key_event *key_data;
XWindowAttributes attr;
window = GST_GL_WINDOW (window_x11);
@ -516,6 +519,10 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
/* XSendEvent (which are called in other threads) are done from another display structure */
XNextEvent (window_x11->device, &event);
XGetWindowAttributes (window_x11->device, window_x11->internal_win_id,
&attr);
window_x11->current_width = attr.width;
window_x11->current_height = attr.height;
window_x11->allow_extra_expose_events = XPending (window_x11->device) <= 2;
@ -583,26 +590,44 @@ gst_gl_window_x11_handle_event (GstGLWindowX11 * window_x11)
keysym = XkbKeycodeToKeysym (window_x11->device,
event.xkey.keycode, 0, 0);
key_str = XKeysymToString (keysym);
key_data = g_slice_new (struct key_event);
key_data->window = window;
key_data->key_str = XKeysymToString (keysym);
key_data->event_type =
event.type == KeyPress ? "key-press" : "key-release";
GST_DEBUG ("input event key %d pressed over window at %d,%d (%s)",
event.xkey.keycode, event.xkey.x, event.xkey.y, key_str);
gst_gl_window_send_key_event (window,
event.type == KeyPress ? "key-press" : "key-release", key_str);
g_main_context_invoke (window->navigation_context,
(GSourceFunc) gst_gl_window_key_event_cb, key_data);
break;
case ButtonPress:
case ButtonRelease:
GST_DEBUG ("input event mouse button %d pressed over window at %d,%d",
event.xbutton.button, event.xbutton.x, event.xbutton.y);
gst_gl_window_send_mouse_event (window,
mouse_data = g_slice_new (struct mouse_event);
mouse_data->window = window;
mouse_data->event_type =
event.type ==
ButtonPress ? "mouse-button-press" : "mouse-button-release",
event.xbutton.button, (double) event.xbutton.x,
(double) event.xbutton.y);
ButtonPress ? "mouse-button-press" : "mouse-button-release";
mouse_data->button = event.xbutton.button;
mouse_data->posx = (double) event.xbutton.x;
mouse_data->posy = (double) event.xbutton.y;
g_main_context_invoke (window->navigation_context,
(GSourceFunc) gst_gl_window_mouse_event_cb, mouse_data);
break;
case MotionNotify:
GST_DEBUG ("input event pointer moved over window at %d,%d",
event.xmotion.x, event.xmotion.y);
gst_gl_window_send_mouse_event (window, "mouse-move", 0,
(double) event.xmotion.x, (double) event.xmotion.y);
mouse_data = g_slice_new (struct mouse_event);
mouse_data->window = window;
mouse_data->event_type = "mouse-move";
mouse_data->button = 0;
mouse_data->posx = (double) event.xbutton.x;
mouse_data->posy = (double) event.xbutton.y;
g_main_context_invoke (window->navigation_context, (GSourceFunc)
gst_gl_window_mouse_event_cb, mouse_data);
break;
default:
GST_DEBUG ("unknown XEvent type: %u", event.type);
@ -715,10 +740,8 @@ gst_gl_window_x11_get_surface_dimensions (GstGLWindow * window, guint * width,
guint * height)
{
GstGLWindowX11 *window_x11 = GST_GL_WINDOW_X11 (window);
XWindowAttributes attr;
XGetWindowAttributes (window_x11->device, window_x11->internal_win_id, &attr);
if (width != NULL)
*width = attr.width;
*width = window_x11->current_width;
if (height != NULL)
*height = attr.height;
*height = window_x11->current_height;
}

View file

@ -64,6 +64,8 @@ struct _GstGLWindowX11
gint depth;
gint device_width;
gint device_height;
gint current_width;
gint current_height;
gint connection;
XVisualInfo *visual_info;
Window parent_win;