waylandsink: rework the mechanism for keeping buffers out of the pool until wl_buffer...
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Fri, 20 Jun 2014 11:47:57 +0000 (14:47 +0300)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Sat, 11 Oct 2014 12:57:13 +0000 (14:57 +0200)
This also removes the GstWlMeta and adds a wrapper class for wl_buffer
which is saved in the GstBuffer qdata instead of being a GstMeta.

The motivation behind this is mainly to allow attaching wl_buffers on
GstBuffers that have not been allocated inside the GstWaylandBufferPool,
so that if for example an upstream element is sending us a buffer
from a different pool, which however does not need to be copied
to a buffer from our pool because it may be a hardware buffer
(hello dmabuf!), we can create a wl_buffer directly from it and first,
attach it on it so that we don't have to re-create a wl_buffer every
time the same GstBuffer arrives and second, force the whole mechanism
for keeping the buffer out of the pool until there is a wl_buffer::release
on that foreign GstBuffer.

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

index 58c1e99e1baa269f1b6b4434a91764e439b47ab3..73aae2cd4b435ab0ed00dd9520f68b33cb585b50 100644 (file)
@@ -3,6 +3,7 @@ plugin_LTLIBRARIES = libgstwaylandsink.la
 libgstwaylandsink_la_SOURCES =  \
        gstwaylandsink.c \
        waylandpool.c \
+       wlbuffer.c \
        wldisplay.c \
        wlwindow.c \
        wlvideoformat.c \
@@ -21,6 +22,7 @@ libgstwaylandsink_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 noinst_HEADERS = \
        gstwaylandsink.h \
        waylandpool.h \
+       wlbuffer.h \
        wldisplay.h \
        wlwindow.h \
        wlvideoformat.h \
@@ -39,6 +41,8 @@ gstwaylandsink.c: scaler-client-protocol.h
 
 waylandpool.c: scaler-client-protocol.h
 
+wlbuffer.c: scaler-client-protocol.h
+
 wldisplay.c: scaler-client-protocol.h
 
 wlwindow.c: scaler-client-protocol.h
index d029d8342b52492544558630232ec018abd16d8e..1e2c11ddd31b9150ce7de47ccf4d76a4a8b1385b 100644 (file)
@@ -44,6 +44,7 @@
 #include "gstwaylandsink.h"
 #include "wlvideoformat.h"
 #include "waylandpool.h"
+#include "wlbuffer.h"
 
 #include <gst/wayland/wayland.h>
 #include <gst/video/videooverlay.h>
@@ -212,11 +213,9 @@ gst_wayland_sink_finalize (GObject * object)
   if (sink->last_buffer)
     gst_buffer_unref (sink->last_buffer);
   if (sink->display) {
-    /* see comment about this call in gst_wayland_sink_change_state() */
-    if (sink->pool) {
-      gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
-          (sink->pool));
-    }
+    /* the display must be stopped before droping our reference to it
+     * - see the comment on wlbuffer.c for details */
+    gst_wl_display_stop (sink->display);
     g_object_unref (sink->display);
   }
   if (sink->window)
@@ -359,23 +358,9 @@ gst_wayland_sink_change_state (GstElement * element, GstStateChange transition)
        * restarted (GstVideoOverlay behaves like that in other sinks)
        */
       if (sink->display && !sink->window) {     /* -> the window was toplevel */
-        /* Force all buffers to return to the pool, regardless of
-         * whether the compositor has released them or not. We are
-         * going to kill the display, so we need to return all buffers
-         * to be destroyed before this happens.
-         * Note that this is done here instead of the pool destructor
-         * because the buffers hold a reference to the pool. Also,
-         * the buffers can only be unref'ed from the display's event loop
-         * and the pool holds a reference to the display. If we drop
-         * our references here, when the compositor releases the buffers,
-         * they will be unref'ed from the event loop thread, which will
-         * unref the pool and therefore the display, which will try to
-         * stop the thread from within itself and cause a deadlock.
-         */
-        if (sink->pool) {
-          gst_wayland_compositor_release_all_buffers (GST_WAYLAND_BUFFER_POOL
-              (sink->pool));
-        }
+        /* the display must be stopped before droping our reference to it
+         * - see the comment on wlbuffer.c for details */
+        gst_wl_display_stop (sink->display);
         g_clear_object (&sink->display);
         g_clear_object (&sink->pool);
       }
@@ -637,24 +622,18 @@ static const struct wl_callback_listener frame_callback_listener = {
 static void
 render_last_buffer (GstWaylandSink * sink)
 {
-  GstWlMeta *meta;
+  GstWlBuffer *wlbuffer;
   struct wl_surface *surface;
   struct wl_callback *callback;
 
-  meta = gst_buffer_get_wl_meta (sink->last_buffer);
+  wlbuffer = gst_buffer_get_wl_buffer (sink->last_buffer);
   surface = gst_wl_window_get_wl_surface (sink->window);
 
   g_atomic_int_set (&sink->redraw_pending, TRUE);
   callback = wl_surface_frame (surface);
   wl_callback_add_listener (callback, &frame_callback_listener, sink);
 
-  /* Here we essentially add a reference to the buffer. This represents
-   * the fact that the compositor is using the buffer and it should
-   * not return back to the pool and be reused until the compositor
-   * releases it. The release is handled internally in the pool */
-  gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer);
-
-  wl_surface_attach (surface, meta->wbuffer, 0, 0);
+  gst_wl_buffer_attach (wlbuffer, sink->window);
   wl_surface_damage (surface, 0, 0, sink->window->surface_width,
       sink->window->surface_height);
 
@@ -667,7 +646,7 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
 {
   GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
   GstBuffer *to_render;
-  GstWlMeta *meta;
+  GstWlBuffer *wlbuffer;
   GstFlowReturn ret = GST_FLOW_OK;
 
   g_mutex_lock (&sink->render_lock);
@@ -710,9 +689,9 @@ gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
           sink->window->surface_height == 0))
     goto no_window_size;
 
-  meta = gst_buffer_get_wl_meta (buffer);
+  wlbuffer = gst_buffer_get_wl_buffer (buffer);
 
-  if (meta && meta->pool->display == sink->display) {
+  if (wlbuffer && wlbuffer->display == sink->display) {
     GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
     to_render = buffer;
   } else {
index de2060294e9cadec023c6e7a19a1b8fae0bcedeb..94758c8f35f74648761c6b25bcfda7e75c9083bd 100644 (file)
@@ -26,6 +26,7 @@
 #include "waylandpool.h"
 #include "wldisplay.h"
 #include "wlvideoformat.h"
+#include "wlbuffer.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
 #define GST_CAT_DEFAULT gstwayland_debug
 
-/* wl metadata */
-GType
-gst_wl_meta_api_get_type (void)
-{
-  static volatile GType type;
-  static const gchar *tags[] =
-      { "memory", "size", "colorspace", "orientation", NULL };
-
-  if (g_once_init_enter (&type)) {
-    GType _type = gst_meta_api_type_register ("GstWlMetaAPI", tags);
-    g_once_init_leave (&type, _type);
-  }
-  return type;
-}
-
-static void
-gst_wl_meta_free (GstWlMeta * meta, GstBuffer * buffer)
-{
-  GST_DEBUG ("destroying wl_buffer %p", meta->wbuffer);
-  wl_buffer_destroy (meta->wbuffer);
-}
-
-const GstMetaInfo *
-gst_wl_meta_get_info (void)
-{
-  static const GstMetaInfo *wl_meta_info = NULL;
-
-  if (g_once_init_enter (&wl_meta_info)) {
-    const GstMetaInfo *meta =
-        gst_meta_register (GST_WL_META_API_TYPE, "GstWlMeta",
-        sizeof (GstWlMeta), (GstMetaInitFunction) NULL,
-        (GstMetaFreeFunction) gst_wl_meta_free,
-        (GstMetaTransformFunction) NULL);
-    g_once_init_leave (&wl_meta_info, meta);
-  }
-  return wl_meta_info;
-}
-
 /* bufferpool */
 static void gst_wayland_buffer_pool_finalize (GObject * object);
 static gboolean gst_wayland_buffer_pool_set_config (GstBufferPool * pool,
@@ -107,8 +70,6 @@ static void
 gst_wayland_buffer_pool_init (GstWaylandBufferPool * self)
 {
   gst_video_info_init (&self->info);
-  g_mutex_init (&self->buffers_map_mutex);
-  self->buffers_map = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -119,86 +80,11 @@ gst_wayland_buffer_pool_finalize (GObject * object)
   if (pool->wl_pool)
     gst_wayland_buffer_pool_stop (GST_BUFFER_POOL (pool));
 
-  g_mutex_clear (&pool->buffers_map_mutex);
-  g_hash_table_unref (pool->buffers_map);
-
   g_object_unref (pool->display);
 
   G_OBJECT_CLASS (gst_wayland_buffer_pool_parent_class)->finalize (object);
 }
 
-static void
-buffer_release (void *data, struct wl_buffer *wl_buffer)
-{
-  GstWaylandBufferPool *self = data;
-  GstBuffer *buffer;
-  GstWlMeta *meta;
-
-  g_mutex_lock (&self->buffers_map_mutex);
-  buffer = g_hash_table_lookup (self->buffers_map, wl_buffer);
-
-  GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", buffer);
-
-  if (buffer) {
-    meta = gst_buffer_get_wl_meta (buffer);
-    if (meta->used_by_compositor) {
-      meta->used_by_compositor = FALSE;
-      /* unlock before unref because stop() may be called from here */
-      g_mutex_unlock (&self->buffers_map_mutex);
-      gst_buffer_unref (buffer);
-      return;
-    }
-  }
-  g_mutex_unlock (&self->buffers_map_mutex);
-}
-
-static const struct wl_buffer_listener buffer_listener = {
-  buffer_release
-};
-
-void
-gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self,
-    GstBuffer * buffer)
-{
-  GstWlMeta *meta;
-
-  meta = gst_buffer_get_wl_meta (buffer);
-  g_return_if_fail (meta != NULL);
-  g_return_if_fail (meta->pool == self);
-  g_return_if_fail (meta->used_by_compositor == FALSE);
-
-  meta->used_by_compositor = TRUE;
-  gst_buffer_ref (buffer);
-}
-
-static void
-unref_used_buffers (gpointer key, gpointer value, gpointer data)
-{
-  GstBuffer *buffer = value;
-  GstWlMeta *meta = gst_buffer_get_wl_meta (buffer);
-  GList **to_unref = data;
-
-  if (meta->used_by_compositor) {
-    meta->used_by_compositor = FALSE;
-    *to_unref = g_list_prepend (*to_unref, buffer);
-  }
-}
-
-void
-gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self)
-{
-  GList *to_unref = NULL;
-
-  g_mutex_lock (&self->buffers_map_mutex);
-  g_hash_table_foreach (self->buffers_map, unref_used_buffers, &to_unref);
-  g_mutex_unlock (&self->buffers_map_mutex);
-
-  /* unref without the lock because stop() may be called from here */
-  if (to_unref) {
-    g_list_free_full (to_unref, (GDestroyNotify) gst_buffer_unref);
-  }
-}
-
 static gboolean
 gst_wayland_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
 {
@@ -302,12 +188,6 @@ gst_wayland_buffer_pool_stop (GstBufferPool * pool)
   self->size = 0;
   self->used = 0;
 
-  /* all buffers are about to be destroyed;
-   * we should no longer do anything with them */
-  g_mutex_lock (&self->buffers_map_mutex);
-  g_hash_table_remove_all (self->buffers_map);
-  g_mutex_unlock (&self->buffers_map_mutex);
-
   return GST_BUFFER_POOL_CLASS (parent_class)->stop (pool);
 }
 
@@ -321,7 +201,7 @@ gst_wayland_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
   enum wl_shm_format format;
   gint offset;
   void *data;
-  GstWlMeta *meta;
+  struct wl_buffer *wbuffer;
 
   width = GST_VIDEO_INFO_WIDTH (&self->info);
   height = GST_VIDEO_INFO_HEIGHT (&self->info);
@@ -342,26 +222,18 @@ gst_wayland_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
   self->used += size;
   data = ((gchar *) self->data) + offset;
 
-  /* create buffer and its metadata object */
   *buffer = gst_buffer_new ();
-  meta = (GstWlMeta *) gst_buffer_add_meta (*buffer, GST_WL_META_INFO, NULL);
-  meta->pool = self;
-  meta->wbuffer = wl_shm_pool_create_buffer (self->wl_pool, offset,
-      width, height, stride, format);
-  meta->used_by_compositor = FALSE;
-
-  /* configure listening to wl_buffer.release */
-  g_mutex_lock (&self->buffers_map_mutex);
-  g_hash_table_insert (self->buffers_map, meta->wbuffer, *buffer);
-  g_mutex_unlock (&self->buffers_map_mutex);
-
-  wl_buffer_add_listener (meta->wbuffer, &buffer_listener, self);
 
   /* add the allocated memory on the GstBuffer */
   gst_buffer_append_memory (*buffer,
       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, data,
           size, 0, size, NULL, NULL));
 
+  /* create wl_buffer and attach it on the GstBuffer via GstWlBuffer */
+  wbuffer = wl_shm_pool_create_buffer (self->wl_pool, offset, width, height,
+      stride, format);
+  gst_buffer_add_wl_buffer (*buffer, wbuffer, self->display);
+
   return GST_FLOW_OK;
 
   /* ERROR */
index ad5020b21681ee63edd5ca9417407240a03c6b35..e0944d025226495221e599e71869667d03c3adef 100644 (file)
@@ -23,7 +23,6 @@
 #define __GST_WAYLAND_BUFFER_POOL_H__
 
 #include <gst/video/video.h>
-#include <gst/video/gstvideometa.h>
 
 #include "wldisplay.h"
 
@@ -37,25 +36,6 @@ G_BEGIN_DECLS
 typedef struct _GstWaylandBufferPool GstWaylandBufferPool;
 typedef struct _GstWaylandBufferPoolClass GstWaylandBufferPoolClass;
 
-/* buffer meta */
-typedef struct _GstWlMeta GstWlMeta;
-
-GType gst_wl_meta_api_get_type (void);
-#define GST_WL_META_API_TYPE  (gst_wl_meta_api_get_type())
-
-const GstMetaInfo * gst_wl_meta_get_info (void);
-#define GST_WL_META_INFO  (gst_wl_meta_get_info())
-
-#define gst_buffer_get_wl_meta(b) ((GstWlMeta*)gst_buffer_get_meta((b),GST_WL_META_API_TYPE))
-
-struct _GstWlMeta {
-  GstMeta meta;
-
-  GstWaylandBufferPool *pool;
-  struct wl_buffer *wbuffer;
-  gboolean used_by_compositor;
-};
-
 /* buffer pool */
 struct _GstWaylandBufferPool
 {
@@ -70,9 +50,6 @@ struct _GstWaylandBufferPool
   size_t size;
   size_t used;
   void *data;
-
-  GMutex buffers_map_mutex;
-  GHashTable *buffers_map;
 };
 
 struct _GstWaylandBufferPoolClass
@@ -84,11 +61,6 @@ GType gst_wayland_buffer_pool_get_type (void);
 
 GstBufferPool *gst_wayland_buffer_pool_new (GstWlDisplay * display);
 
-
-void gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self,
-    GstBuffer * buffer);
-void gst_wayland_compositor_release_all_buffers (GstWaylandBufferPool * self);
-
 G_END_DECLS
 
 #endif /*__GST_WAYLAND_BUFFER_POOL_H__*/
diff --git a/ext/wayland/wlbuffer.c b/ext/wayland/wlbuffer.c
new file mode 100644 (file)
index 0000000..1806b17
--- /dev/null
@@ -0,0 +1,192 @@
+/* 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.
+ */
+
+/* GstWlBuffer wraps wl_buffer and provides a mechanism for preventing
+ * buffers from being re-used while the compositor is using them. This
+ * is achieved by adding a reference to the GstBuffer as soon as its
+ * associated wl_buffer is sent to the compositor and by removing this
+ * reference as soon as the compositor sends a wl_buffer::release message.
+ *
+ * This mechanism is a bit complicated, though, because it adds cyclic
+ * references that can be dangerous. The reference cycles looks like:
+ *
+ *   ----------------
+ *   | GstWlDisplay | ---------------------------------->
+ *   ----------------                                    |
+ *         ^                                             |
+ *         |                                             V
+ *   ------------------------     -------------     ---------------
+ *   | GstWaylandBufferPool | --> | GstBuffer | ==> | GstWlBuffer |
+ *   |                      | <-- |           | <-- |             |
+ *   ------------------------     -------------     ---------------
+ *
+ * A GstBufferPool normally holds references to its GstBuffers and each buffer
+ * holds a reference to a GstWlBuffer (saved in the GstMiniObject qdata).
+ * When a GstBuffer is in use, it holds a reference back to the pool and the
+ * pool doesn't hold a reference to the GstBuffer. When the GstBuffer is unrefed
+ * externally, it returns back to the pool and the pool holds again a reference
+ * to the buffer.
+ *
+ * Now when the compositor is using a buffer, the GstWlBuffer also holds a ref
+ * to the GstBuffer, which prevents it from returning to the pool. When the
+ * last GstWlBuffer receives a release event and unrefs the last GstBuffer,
+ * the GstBufferPool will be able to stop and if no-one is holding a strong
+ * ref to it, it will be destroyed. This will destroy that last GstBuffer and
+ * also the GstWlBuffer. This will all happen in the same context of the
+ * gst_buffer_unref, which will be called from the buffer_release() callback.
+ *
+ * The big problem here lies in the fact that buffer_release() will be called
+ * from the event loop thread of GstWlDisplay and the second big problem is
+ * that the GstWaylandBufferPool holds a strong ref to the GstWlDisplay.
+ * Therefore, if the buffer_release() causes the pool to be destroyed, it may
+ * also cause the GstWlDisplay to be destroyed and that will happen in the
+ * context of the event loop thread that GstWlDisplay runs. Destroying the
+ * GstWlDisplay will need to join the thread (from inside the thread!) and boom.
+ *
+ * Normally, this will never happen, even if we don't take special care for it,
+ * because the compositor releases buffers almost immediately and when
+ * waylandsink stops, they are already released.
+ *
+ * However, we want to be absolutely certain, so a solution is introduced
+ * by explicitly releasing all the buffer references and destroying the
+ * GstWlBuffers as soon as we know that we are not going to use them again.
+ * All the GstWlBuffers are registered in a hash set inside GstWlDisplay
+ * and there is gst_wl_display_stop(), which stops the event loop thread
+ * and releases all the buffers explicitly. This gets called from GstWaylandSink
+ * right before dropping its own reference to the GstWlDisplay, leaving
+ * a possible last (but safe now!) reference to the pool, which may be
+ * referenced by an upstream element.
+ */
+
+#include "wlbuffer.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
+#define GST_CAT_DEFAULT gstwayland_debug
+
+G_DEFINE_TYPE (GstWlBuffer, gst_wl_buffer, G_TYPE_OBJECT);
+
+static G_DEFINE_QUARK (GstWlBufferQDataQuark, gst_wl_buffer_qdata);
+
+static void
+gst_wl_buffer_finalize (GObject * gobject)
+{
+  GstWlBuffer *self = GST_WL_BUFFER (gobject);
+
+  if (self->display)
+    gst_wl_display_unregister_buffer (self->display, self);
+  wl_buffer_destroy (self->wlbuffer);
+
+  G_OBJECT_CLASS (gst_wl_buffer_parent_class)->finalize (gobject);
+}
+
+static void
+gst_wl_buffer_class_init (GstWlBufferClass * klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->finalize = gst_wl_buffer_finalize;
+}
+
+static void
+gst_wl_buffer_init (GstWlBuffer * self)
+{
+}
+
+static void
+buffer_release (void *data, struct wl_buffer *wl_buffer)
+{
+  GstWlBuffer *self = data;
+
+  GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", self->gstbuffer);
+
+  self->used_by_compositor = FALSE;
+
+  /* unref should be last, because it may end up destroying the GstWlBuffer */
+  gst_buffer_unref (self->gstbuffer);
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+  buffer_release
+};
+
+void
+gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer,
+    GstWlDisplay * display)
+{
+  GstWlBuffer *self;
+
+  self = g_object_new (GST_TYPE_WL_BUFFER, NULL);
+  self->gstbuffer = gstbuffer;
+  self->wlbuffer = wlbuffer;
+  self->display = display;
+
+  gst_wl_display_register_buffer (self->display, self);
+
+  wl_buffer_add_listener (self->wlbuffer, &buffer_listener, self);
+
+  gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer,
+      gst_wl_buffer_qdata_quark (), self, g_object_unref);
+}
+
+GstWlBuffer *
+gst_buffer_get_wl_buffer (GstBuffer * gstbuffer)
+{
+  return gst_mini_object_get_qdata ((GstMiniObject *) gstbuffer,
+      gst_wl_buffer_qdata_quark ());
+}
+
+void
+gst_wl_buffer_force_release_and_unref (GstWlBuffer * self)
+{
+  /* detach from the GstBuffer */
+  (void) gst_mini_object_steal_qdata ((GstMiniObject *) self->gstbuffer,
+      gst_wl_buffer_qdata_quark ());
+
+  /* force a buffer release
+   * at this point, the GstWlDisplay has killed its event loop,
+   * so we don't need to worry about buffer_release() being called
+   * at the same time from the event loop thread */
+  if (self->used_by_compositor) {
+    GST_DEBUG_OBJECT (self, "forcing wl_buffer::release (GstBuffer: %p)",
+        self->gstbuffer);
+    gst_buffer_unref (self->gstbuffer);
+    self->used_by_compositor = FALSE;
+  }
+
+  /* avoid unregistering from the display in finalize() because this
+   * function is being called from a hash table foreach function,
+   * which would be modified in gst_wl_display_unregister_buffer() */
+  self->display = NULL;
+  g_object_unref (self);
+}
+
+void
+gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target)
+{
+  g_return_if_fail (self->used_by_compositor == FALSE);
+
+  wl_surface_attach (target->surface, self->wlbuffer, 0, 0);
+
+  /* Add a reference to the buffer. This represents the fact that
+   * the compositor is using the buffer and it should not return
+   * back to the pool and be re-used until the compositor releases it. */
+  gst_buffer_ref (self->gstbuffer);
+  self->used_by_compositor = TRUE;
+}
diff --git a/ext/wayland/wlbuffer.h b/ext/wayland/wlbuffer.h
new file mode 100644 (file)
index 0000000..b434df4
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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_BUFFER_H__
+#define __GST_WL_BUFFER_H__
+
+#include "wlwindow.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_WL_BUFFER                  (gst_wl_buffer_get_type ())
+#define GST_WL_BUFFER(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WL_BUFFER, GstWlBuffer))
+#define GST_IS_WL_BUFFER(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WL_BUFFER))
+#define GST_WL_BUFFER_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WL_BUFFER, GstWlBufferClass))
+#define GST_IS_WL_BUFFER_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WL_BUFFER))
+#define GST_WL_BUFFER_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_WL_BUFFER, GstWlBufferClass))
+
+typedef struct _GstWlBuffer GstWlBuffer;
+typedef struct _GstWlBufferClass GstWlBufferClass;
+
+struct _GstWlBuffer
+{
+  GObject parent_instance;
+
+  struct wl_buffer * wlbuffer;
+  GstBuffer *gstbuffer;
+
+  GstWlDisplay *display;
+
+  gboolean used_by_compositor;
+};
+
+struct _GstWlBufferClass
+{
+  GObjectClass parent_class;
+};
+
+GType gst_wl_buffer_get_type (void);
+
+void gst_buffer_add_wl_buffer (GstBuffer * gstbuffer,
+    struct wl_buffer * wlbuffer, GstWlDisplay * display);
+GstWlBuffer * gst_buffer_get_wl_buffer (GstBuffer * gstbuffer);
+
+void gst_wl_buffer_force_release_and_unref (GstWlBuffer * self);
+
+void gst_wl_buffer_attach (GstWlBuffer * self, GstWlWindow * target);
+
+G_END_DECLS
+
+#endif /* __GST_WL_BUFFER_H__ */
index 46efab95143ae306df8c72fe41122e18a4d4ba0b..17bfa8c1d1b820cbbf87b4e7ac9d334bf0a04f1d 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 
 #include "wldisplay.h"
+#include "wlbuffer.h"
 
 #include <errno.h>
 
@@ -45,6 +46,7 @@ gst_wl_display_init (GstWlDisplay * self)
 {
   self->formats = g_array_new (FALSE, FALSE, sizeof (uint32_t));
   self->wl_fd_poll = gst_poll_new (TRUE);
+  self->buffers = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 static void
@@ -52,13 +54,9 @@ 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);
-
   g_array_unref (self->formats);
   gst_poll_free (self->wl_fd_poll);
+  g_hash_table_unref (self->buffers);
 
   if (self->shm)
     wl_shm_destroy (self->shm);
@@ -266,3 +264,26 @@ gst_wl_display_new_existing (struct wl_display * display,
 
   return self;
 }
+
+void
+gst_wl_display_stop (GstWlDisplay * self)
+{
+  gst_poll_set_flushing (self->wl_fd_poll, TRUE);
+  g_thread_join (self->thread);
+
+  g_hash_table_foreach (self->buffers,
+      (GHFunc) gst_wl_buffer_force_release_and_unref, NULL);
+  g_hash_table_remove_all (self->buffers);
+}
+
+void
+gst_wl_display_register_buffer (GstWlDisplay * self, gpointer buf)
+{
+  g_hash_table_add (self->buffers, buf);
+}
+
+void
+gst_wl_display_unregister_buffer (GstWlDisplay * self, gpointer buf)
+{
+  g_hash_table_remove (self->buffers, buf);
+}
index 02244066729bbb879488406f3a09f7958b3cff6f..ac1df84036ddf8763d6077554238f738723cf3fd 100644 (file)
@@ -58,6 +58,8 @@ struct _GstWlDisplay
   gboolean own_display;
   GThread *thread;
   GstPoll *wl_fd_poll;
+
+  GHashTable *buffers;
 };
 
 struct _GstWlDisplayClass
@@ -71,6 +73,11 @@ 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);
 
+/* see wlbuffer.c for explanation */
+void gst_wl_display_stop (GstWlDisplay * self);
+void gst_wl_display_register_buffer (GstWlDisplay * self, gpointer buf);
+void gst_wl_display_unregister_buffer (GstWlDisplay * self, gpointer buf);
+
 G_END_DECLS
 
 #endif /* __GST_WL_DISPLAY_H__ */