gstreamer/ext/wayland/wlbuffer.c
George Kiagiadakis 5b1c5dbf99 waylandsink: stack the video subsurface into another subsurface that covers the whole render rectangle
The main reason behind this is that when the video caps change and the video
subsurface needs to resize and change position, the wl_subsurface.set_position
call needs a commit in its parent in order to take effect. Previously,
the parent was the application's surface, over which there is no control.
Now, the parent is inside the sink, so we can commit it and change size smoothly.

As a side effect, this also allows the sink to draw its black borders on
its own, without the need for the application to do that. And another side
effect is that this can now allow resizing the sink when it is in top-level
mode and have it respect the aspect ratio.
2014-10-11 14:57:14 +02:00

188 lines
6.7 KiB
C

/* 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
* ----------------- ------------- ---------------
* | GstBufferPool | --> | 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 the pool's GstBuffers and
* also the GstWlBuffers. This will all happen in the same context of the last
* gst_buffer_unref, which will be called from the buffer_release() callback.
*
* The problem here lies in the fact that buffer_release() will be called
* from the event loop thread of GstWlDisplay, so it's as if the display
* holds a reference to the GstWlBuffer, but without having an actual reference.
* When we kill the display, there is no way for the GstWlBuffer, the associated
* GstBuffer and the GstBufferPool to get destroyed, so we are going to leak a
* fair ammount of memory.
*
* 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 registering all the GstWlBuffers with the display and explicitly
* releasing all the buffer references and destroying the GstWlBuffers as soon
* as the display is destroyed.
*/
#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
};
GstWlBuffer *
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);
return self;
}
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, struct wl_surface *surface)
{
g_return_if_fail (self->used_by_compositor == FALSE);
wl_surface_attach (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;
}