libgstwaylandsink_la_SOURCES = \
gstwaylandsink.c \
waylandpool.c \
+ wlbuffer.c \
wldisplay.c \
wlwindow.c \
wlvideoformat.c \
noinst_HEADERS = \
gstwaylandsink.h \
waylandpool.h \
+ wlbuffer.h \
wldisplay.h \
wlwindow.h \
wlvideoformat.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
#include "gstwaylandsink.h"
#include "wlvideoformat.h"
#include "waylandpool.h"
+#include "wlbuffer.h"
#include <gst/wayland/wayland.h>
#include <gst/video/videooverlay.h>
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)
* 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);
}
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);
{
GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
GstBuffer *to_render;
- GstWlMeta *meta;
+ GstWlBuffer *wlbuffer;
GstFlowReturn ret = GST_FLOW_OK;
g_mutex_lock (&sink->render_lock);
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 {
#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,
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
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)
{
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);
}
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);
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 */
#define __GST_WAYLAND_BUFFER_POOL_H__
#include <gst/video/video.h>
-#include <gst/video/gstvideometa.h>
#include "wldisplay.h"
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
{
size_t size;
size_t used;
void *data;
-
- GMutex buffers_map_mutex;
- GHashTable *buffers_map;
};
struct _GstWaylandBufferPoolClass
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__*/
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
#endif
#include "wldisplay.h"
+#include "wlbuffer.h"
#include <errno.h>
{
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
{
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);
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);
+}
gboolean own_display;
GThread *thread;
GstPoll *wl_fd_poll;
+
+ GHashTable *buffers;
};
struct _GstWlDisplayClass
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__ */