mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 17:20:36 +00:00
waylandsink: rework the mechanism for keeping buffers out of the pool until wl_buffer::release
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.
This commit is contained in:
parent
b8927d848c
commit
9807d58b01
8 changed files with 316 additions and 202 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "waylandpool.h"
|
||||
#include "wldisplay.h"
|
||||
#include "wlvideoformat.h"
|
||||
#include "wlbuffer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -38,44 +39,6 @@
|
|||
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 */
|
||||
|
|
|
@ -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__*/
|
||||
|
|
192
ext/wayland/wlbuffer.c
Normal file
192
ext/wayland/wlbuffer.c
Normal file
|
@ -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;
|
||||
}
|
67
ext/wayland/wlbuffer.h
Normal file
67
ext/wayland/wlbuffer.h
Normal file
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in a new issue