waylandsink: process display events in a separate thread
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Wed, 12 Feb 2014 10:28:40 +0000 (11:28 +0100)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Tue, 17 Jun 2014 11:51:20 +0000 (13:51 +0200)
This also moves the display-related code into a new GstWlDisplay class,
which takes care of the new thread

ext/wayland/Makefile.am
ext/wayland/gstwaylandsink.c
ext/wayland/gstwaylandsink.h
ext/wayland/waylandpool.c
ext/wayland/wldisplay.c [new file with mode: 0644]
ext/wayland/wldisplay.h [new file with mode: 0644]

index e8edf73..80b3614 100644 (file)
@@ -1,6 +1,6 @@
 plugin_LTLIBRARIES = libgstwaylandsink.la
 
-libgstwaylandsink_la_SOURCES =  gstwaylandsink.c waylandpool.c
+libgstwaylandsink_la_SOURCES =  gstwaylandsink.c waylandpool.c wldisplay.c
 libgstwaylandsink_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
                                $(WAYLAND_CFLAGS)
 libgstwaylandsink_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
@@ -9,4 +9,4 @@ libgstwaylandsink_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
 libgstwaylandsink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstwaylandsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 
-noinst_HEADERS = gstwaylandsink.h waylandpool.h
+noinst_HEADERS = gstwaylandsink.h waylandpool.h wldisplay.h
index e286f32..50c3d52 100644 (file)
@@ -91,12 +91,9 @@ gst_wayland_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query);
 static gboolean gst_wayland_sink_render (GstBaseSink * bsink,
     GstBuffer * buffer);
 
-static struct display *create_display (void);
-static void registry_handle_global (void *data, struct wl_registry *registry,
-    uint32_t id, const char *interface, uint32_t version);
 static void frame_redraw_callback (void *data,
     struct wl_callback *callback, uint32_t time);
-static void create_window (GstWaylandSink * sink, struct display *display,
+static void create_window (GstWaylandSink * sink, GstWlDisplay * display,
     int width, int height);
 static void shm_pool_destroy (struct shm_pool *pool);
 
@@ -225,23 +222,6 @@ gst_wayland_sink_set_property (GObject * object,
 }
 
 static void
-destroy_display (struct display *display)
-{
-  if (display->shm)
-    wl_shm_destroy (display->shm);
-
-  if (display->shell)
-    wl_shell_destroy (display->shell);
-
-  if (display->compositor)
-    wl_compositor_destroy (display->compositor);
-
-  wl_display_flush (display->display);
-  wl_display_disconnect (display->display);
-  free (display);
-}
-
-static void
 destroy_window (struct window *window)
 {
   if (window->callback)
@@ -277,7 +257,7 @@ gst_wayland_sink_finalize (GObject * object)
   if (sink->window)
     destroy_window (sink->window);
   if (sink->display)
-    destroy_display (sink->display);
+    g_object_unref (sink->display);
   if (sink->shm_pool)
     shm_pool_destroy (sink->shm_pool);
 
@@ -306,68 +286,6 @@ gst_wayland_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
   return caps;
 }
 
-static void
-shm_format (void *data, struct wl_shm *wl_shm, uint32_t format)
-{
-  struct display *d = data;
-
-  d->formats |= (1 << format);
-}
-
-struct wl_shm_listener shm_listenter = {
-  shm_format
-};
-
-static void
-registry_handle_global (void *data, struct wl_registry *registry,
-    uint32_t id, const char *interface, uint32_t version)
-{
-  struct display *d = data;
-
-  if (strcmp (interface, "wl_compositor") == 0) {
-    d->compositor =
-        wl_registry_bind (registry, id, &wl_compositor_interface, 1);
-  } else if (strcmp (interface, "wl_shell") == 0) {
-    d->shell = wl_registry_bind (registry, id, &wl_shell_interface, 1);
-  } else if (strcmp (interface, "wl_shm") == 0) {
-    d->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1);
-    wl_shm_add_listener (d->shm, &shm_listenter, d);
-  }
-}
-
-static const struct wl_registry_listener registry_listener = {
-  registry_handle_global
-};
-
-static struct display *
-create_display (void)
-{
-  struct display *display;
-
-  display = malloc (sizeof *display);
-  display->display = wl_display_connect (NULL);
-
-  if (display->display == NULL) {
-    free (display);
-    return NULL;
-  }
-
-  display->registry = wl_display_get_registry (display->display);
-  wl_registry_add_listener (display->registry, &registry_listener, display);
-
-  wl_display_roundtrip (display->display);
-  if (display->shm == NULL) {
-    GST_ERROR ("No wl_shm global..");
-    return NULL;
-  }
-
-  wl_display_roundtrip (display->display);
-
-  wl_display_get_fd (display->display);
-
-  return display;
-}
-
 static gboolean
 gst_wayland_sink_format_from_caps (uint32_t * wl_format, GstCaps * caps)
 {
@@ -473,7 +391,7 @@ static const struct wl_shell_surface_listener shell_surface_listener = {
 };
 
 static void
-create_window (GstWaylandSink * sink, struct display *display, int width,
+create_window (GstWaylandSink * sink, GstWlDisplay * display, int width,
     int height)
 {
   struct window *window;
@@ -484,7 +402,6 @@ create_window (GstWaylandSink * sink, struct display *display, int width,
   g_mutex_lock (&sink->wayland_lock);
 
   window = malloc (sizeof *window);
-  window->display = display;
   window->width = width;
   window->height = height;
   window->redraw_pending = FALSE;
@@ -511,16 +428,17 @@ gst_wayland_sink_start (GstBaseSink * bsink)
 {
   GstWaylandSink *sink = (GstWaylandSink *) bsink;
   gboolean result = TRUE;
+  GError *error = NULL;
 
   GST_DEBUG_OBJECT (sink, "start");
 
   if (!sink->display)
-    sink->display = create_display ();
+    sink->display = gst_wl_display_new (NULL, &error);
 
   if (sink->display == NULL) {
-    GST_ELEMENT_ERROR (bsink, RESOURCE, OPEN_READ_WRITE,
+    GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_READ_WRITE,
         ("Could not initialise Wayland output"),
-        ("Could not create Wayland display"));
+        ("Failed to create GstWlDisplay: '%s'", error->message));
     return FALSE;
   }
 
@@ -617,6 +535,7 @@ frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
 {
   struct window *window = (struct window *) data;
   window->redraw_pending = FALSE;
+  GST_LOG ("frame_redraw_cb");
   wl_callback_destroy (callback);
 }
 
@@ -633,21 +552,15 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
   GstWlMeta *meta;
   GstFlowReturn ret;
   struct window *window;
-  struct display *display;
 
   GST_LOG_OBJECT (sink, "render buffer %p", buffer);
   if (!sink->window)
     create_window (sink, sink->display, sink->video_width, sink->video_height);
 
   window = sink->window;
-  display = sink->display;
 
   meta = gst_buffer_get_wl_meta (buffer);
 
-  if (window->redraw_pending) {
-    wl_display_dispatch (display->display);
-  }
-
   if (meta && meta->sink == sink) {
     GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
     to_render = buffer;
@@ -685,7 +598,7 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
   window->callback = wl_surface_frame (window->surface);
   wl_callback_add_listener (window->callback, &frame_callback_listener, window);
   wl_surface_commit (window->surface);
-  wl_display_dispatch (display->display);
+  wl_display_flush (sink->display->display);
 
   if (buffer != to_render)
     gst_buffer_unref (to_render);
index 9e9b011..617e51f 100644 (file)
@@ -42,6 +42,8 @@
 
 #include <wayland-client.h>
 
+#include "wldisplay.h"
+
 G_BEGIN_DECLS
 
 #define GST_TYPE_WAYLAND_SINK \
@@ -57,19 +59,8 @@ G_BEGIN_DECLS
 #define GST_WAYLAND_SINK_GET_CLASS(inst) \
         (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_WAYLAND_SINK, GstWaylandSinkClass))
 
-struct  display
-{
-  struct wl_display *display;
-  struct wl_registry *registry;
-  struct wl_compositor *compositor;
-  struct wl_shell *shell;
-  struct wl_shm *shm;
-  uint32_t formats;
-};
-
 struct window
 {
-  struct display *display;
   int width, height;
   struct wl_surface *surface;
   struct wl_shell_surface *shell_surface;
@@ -97,7 +88,7 @@ struct _GstWaylandSink
 {
   GstVideoSink parent;
 
-  struct display *display;
+  GstWlDisplay *display;
   struct window *window;
   struct shm_pool *shm_pool;
 
index 9eb028a..9ab993c 100644 (file)
@@ -24,6 +24,7 @@
 
 /* Object header */
 #include "gstwaylandsink.h"
+#include "wldisplay.h"
 
 /* Debugging category */
 #include <gst/gstinfo.h>
@@ -127,7 +128,7 @@ wrong_caps:
 }
 
 static struct wl_shm_pool *
-make_shm_pool (struct display *display, int size, void **data)
+make_shm_pool (GstWlDisplay * display, int size, void **data)
 {
   struct wl_shm_pool *pool;
   int fd;
@@ -162,7 +163,7 @@ make_shm_pool (struct display *display, int size, void **data)
 }
 
 static struct shm_pool *
-shm_pool_create (struct display *display, size_t size)
+shm_pool_create (GstWlDisplay * display, size_t size)
 {
   struct shm_pool *pool = malloc (sizeof *pool);
 
diff --git a/ext/wayland/wldisplay.c b/ext/wayland/wldisplay.c
new file mode 100644 (file)
index 0000000..5f142fb
--- /dev/null
@@ -0,0 +1,233 @@
+/* GStreamer Wayland video sink
+ *
+ * Copyright (C) 2014 Collabora Ltd.
+ *
+ * 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 Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#include "wldisplay.h"
+
+#include <errno.h>
+
+G_DEFINE_TYPE (GstWlDisplay, gst_wl_display, G_TYPE_OBJECT);
+
+static void gst_wl_display_finalize (GObject * gobject);
+
+static void
+gst_wl_display_class_init (GstWlDisplayClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = gst_wl_display_finalize;
+}
+
+static void
+gst_wl_display_init (GstWlDisplay * self)
+{
+  self->wl_fd_poll = gst_poll_new (TRUE);
+}
+
+static void
+gst_wl_display_finalize (GObject * gobject)
+{
+  GstWlDisplay *self = GST_WL_DISPLAY (gobject);
+
+  gst_poll_set_flushing (self->wl_fd_poll, TRUE);
+
+  if (self->thread)
+    g_thread_join (self->thread);
+
+  gst_poll_free (self->wl_fd_poll);
+
+  if (self->shm)
+    wl_shm_destroy (self->shm);
+
+  if (self->shell)
+    wl_shell_destroy (self->shell);
+
+  if (self->compositor)
+    wl_compositor_destroy (self->compositor);
+
+  if (self->registry)
+    wl_registry_destroy (self->registry);
+
+  if (self->queue)
+    wl_event_queue_destroy (self->queue);
+
+  if (self->own_display) {
+    wl_display_flush (self->display);
+    wl_display_disconnect (self->display);
+  }
+
+  G_OBJECT_CLASS (gst_wl_display_parent_class)->finalize (gobject);
+}
+
+static void
+sync_callback (void *data, struct wl_callback *callback, uint32_t serial)
+{
+  gboolean *done = data;
+  *done = TRUE;
+}
+
+static const struct wl_callback_listener sync_listener = {
+  sync_callback
+};
+
+static gint
+gst_wl_display_roundtrip (GstWlDisplay * self)
+{
+  struct wl_callback *callback;
+  gint ret = 0;
+  gboolean done = FALSE;
+
+  g_return_val_if_fail (self != NULL, -1);
+
+  /* We don't own the display, process only our queue */
+  callback = wl_display_sync (self->display);
+  wl_callback_add_listener (callback, &sync_listener, &done);
+  wl_proxy_set_queue ((struct wl_proxy *) callback, self->queue);
+  while (ret != -1 && !done)
+    ret = wl_display_dispatch_queue (self->display, self->queue);
+  wl_callback_destroy (callback);
+
+  return ret;
+}
+
+static void
+shm_format (void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+  GstWlDisplay *self = data;
+
+  self->formats |= (1 << format);
+}
+
+static const struct wl_shm_listener shm_listener = {
+  shm_format
+};
+
+static void
+registry_handle_global (void *data, struct wl_registry *registry,
+    uint32_t id, const char *interface, uint32_t version)
+{
+  GstWlDisplay *self = data;
+
+  if (g_strcmp0 (interface, "wl_compositor") == 0) {
+    self->compositor =
+        wl_registry_bind (registry, id, &wl_compositor_interface, 1);
+  } else if (g_strcmp0 (interface, "wl_shell") == 0) {
+    self->shell = wl_registry_bind (registry, id, &wl_shell_interface, 1);
+  } else if (g_strcmp0 (interface, "wl_shm") == 0) {
+    self->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1);
+    wl_shm_add_listener (self->shm, &shm_listener, self);
+  }
+}
+
+static const struct wl_registry_listener registry_listener = {
+  registry_handle_global
+};
+
+static gpointer
+gst_wl_display_thread_run (gpointer data)
+{
+  GstWlDisplay *self = data;
+  GstPollFD pollfd = GST_POLL_FD_INIT;
+
+  pollfd.fd = wl_display_get_fd (self->display);
+  gst_poll_add_fd (self->wl_fd_poll, &pollfd);
+  gst_poll_fd_ctl_read (self->wl_fd_poll, &pollfd, TRUE);
+
+  /* main loop */
+  while (1) {
+    while (wl_display_prepare_read_queue (self->display, self->queue) != 0)
+      wl_display_dispatch_queue_pending (self->display, self->queue);
+    wl_display_flush (self->display);
+
+    if (gst_poll_wait (self->wl_fd_poll, GST_CLOCK_TIME_NONE) < 0) {
+      gboolean normal = (errno == EBUSY);
+      wl_display_cancel_read (self->display);
+      if (normal)
+        break;
+      else
+        goto error;
+    } else {
+      wl_display_read_events (self->display);
+      wl_display_dispatch_queue_pending (self->display, self->queue);
+    }
+  }
+
+  return NULL;
+
+error:
+  GST_ERROR ("Error communicating with the wayland server");
+  return NULL;
+}
+
+GstWlDisplay *
+gst_wl_display_new (const gchar * name, GError ** error)
+{
+  struct wl_display *display;
+
+  display = wl_display_connect (name);
+
+  if (!display) {
+    *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0,
+        "Failed to connect to the wayland display '%s'",
+        name ? name : "(default)");
+    return NULL;
+  } else {
+    return gst_wl_display_new_existing (display, TRUE, error);
+  }
+}
+
+GstWlDisplay *
+gst_wl_display_new_existing (struct wl_display * display,
+    gboolean take_ownership, GError ** error)
+{
+  GstWlDisplay *self;
+  GError *err = NULL;
+  gint i;
+
+  g_return_val_if_fail (display != NULL, NULL);
+
+  self = g_object_new (GST_TYPE_WL_DISPLAY, NULL);
+  self->display = display;
+  self->own_display = take_ownership;
+
+  self->queue = wl_display_create_queue (self->display);
+  self->registry = wl_display_get_registry (self->display);
+  wl_proxy_set_queue ((struct wl_proxy *) self->registry, self->queue);
+  wl_registry_add_listener (self->registry, &registry_listener, self);
+
+  /* we need exactly 2 roundtrips to discover global objects and their state */
+  for (i = 0; i < 2; i++) {
+    if (gst_wl_display_roundtrip (self) < 0) {
+      *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0,
+          "Error communicating with the wayland display");
+      g_object_unref (self);
+      return NULL;
+    }
+  }
+
+  self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run,
+      self, &err);
+  if (err) {
+    g_propagate_prefixed_error (error, err,
+        "Failed to start thread for the display's events");
+    g_object_unref (self);
+    return NULL;
+  }
+
+  return self;
+}
diff --git a/ext/wayland/wldisplay.h b/ext/wayland/wldisplay.h
new file mode 100644 (file)
index 0000000..149b921
--- /dev/null
@@ -0,0 +1,69 @@
+/* GStreamer Wayland video sink
+ *
+ * Copyright (C) 2014 Collabora Ltd.
+ *
+ * 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 Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GST_WL_DISPLAY_H__
+#define __GST_WL_DISPLAY_H__
+
+#include <gst/gst.h>
+#include <wayland-client.h>
+
+#define GST_TYPE_WL_DISPLAY                  (gst_wl_display_get_type ())
+#define GST_WL_DISPLAY(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplay))
+#define GST_IS_WL_DISPLAY(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_DISPLAY))
+#define GST_WL_DISPLAY_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_DISPLAY, GstWlDisplayClass))
+#define GST_IS_WL_DISPLAY_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_DISPLAY))
+#define GST_WL_DISPLAY_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_DISPLAY, GstWlDisplayClass))
+
+typedef struct _GstWlDisplay GstWlDisplay;
+typedef struct _GstWlDisplayClass GstWlDisplayClass;
+
+struct _GstWlDisplay
+{
+  GObject parent_instance;
+
+  /* public objects */
+  struct wl_display *display;
+  struct wl_event_queue *queue;
+
+  /* globals */
+  struct wl_registry *registry;
+  struct wl_compositor *compositor;
+  struct wl_shell *shell;
+  struct wl_shm *shm;
+  guint32 formats;
+
+  /* private */
+  gboolean own_display;
+  GThread *thread;
+  GstPoll *wl_fd_poll;
+};
+
+struct _GstWlDisplayClass
+{
+  GObjectClass parent_class;
+};
+
+GType gst_wl_display_get_type (void);
+
+GstWlDisplay *gst_wl_display_new (const gchar * name, GError ** error);
+GstWlDisplay *gst_wl_display_new_existing (struct wl_display * display,
+    gboolean take_ownership, GError ** error);
+
+#endif /* __GST_WL_DISPLAY_H__ */