mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +00:00
ximage: rewrite the buffer pool in ximagesink
Rewrite the pooling in ximagesink to extend from the bufferpool base class in core. Move some code to a comon place and refactor.
This commit is contained in:
parent
6aa22111a1
commit
ea05fe1278
6 changed files with 748 additions and 580 deletions
|
@ -1,6 +1,6 @@
|
|||
plugin_LTLIBRARIES = libgstximagesink.la
|
||||
|
||||
libgstximagesink_la_SOURCES = ximagesink.c ximage.c
|
||||
libgstximagesink_la_SOURCES = ximagesink.c ximage.c ximagepool.c
|
||||
libgstximagesink_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(X_CFLAGS)
|
||||
libgstximagesink_la_LIBADD = \
|
||||
$(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-$(GST_MAJORMINOR).la \
|
||||
|
@ -11,4 +11,4 @@ libgstximagesink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
|||
libgstximagesink_la_DEPENDENCIES = $(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la
|
||||
libgstximagesink_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
noinst_HEADERS = ximagesink.h
|
||||
noinst_HEADERS = ximagesink.h ximagepool.h
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "ximagesink.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (gst_debug_ximagepool);
|
||||
GST_DEBUG_CATEGORY (gst_debug_ximagesink);
|
||||
|
||||
static gboolean
|
||||
|
@ -36,6 +37,8 @@ plugin_init (GstPlugin * plugin)
|
|||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_debug_ximagesink, "ximagesink", 0,
|
||||
"ximagesink element");
|
||||
GST_DEBUG_CATEGORY_INIT (gst_debug_ximagepool, "ximagepool", 0,
|
||||
"ximagepool object");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
529
sys/ximage/ximagepool.c
Normal file
529
sys/ximage/ximagepool.c
Normal file
|
@ -0,0 +1,529 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2005> Julien Moutte <julien@moutte.net>
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Object header */
|
||||
#include "ximagesink.h"
|
||||
|
||||
/* Debugging category */
|
||||
#include <gst/gstinfo.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagepool);
|
||||
#define GST_CAT_DEFAULT gst_debug_ximagepool
|
||||
|
||||
static void gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer);
|
||||
|
||||
/* ximage buffers */
|
||||
const GstMetaInfo *
|
||||
gst_meta_ximage_get_info (void)
|
||||
{
|
||||
static const GstMetaInfo *meta_ximage_info = NULL;
|
||||
|
||||
if (meta_ximage_info == NULL) {
|
||||
meta_ximage_info = gst_meta_register ("GstMetaXImage", "GstMetaXImage",
|
||||
sizeof (GstMetaXImage),
|
||||
(GstMetaInitFunction) NULL,
|
||||
(GstMetaFreeFunction) gst_meta_ximage_free,
|
||||
(GstMetaCopyFunction) NULL,
|
||||
(GstMetaSubFunction) NULL,
|
||||
(GstMetaSerializeFunction) NULL, (GstMetaDeserializeFunction) NULL);
|
||||
}
|
||||
return meta_ximage_info;
|
||||
}
|
||||
|
||||
/* X11 stuff */
|
||||
static gboolean error_caught;
|
||||
|
||||
static int
|
||||
gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
|
||||
{
|
||||
char error_msg[1024];
|
||||
|
||||
XGetErrorText (display, xevent->error_code, error_msg, 1024);
|
||||
GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
|
||||
error_caught = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GstBuffer *
|
||||
gst_ximage_buffer_new (GstXImageSink * ximagesink, gint width, gint height)
|
||||
{
|
||||
GstBuffer *ximage;
|
||||
int (*handler) (Display *, XErrorEvent *);
|
||||
gboolean success = FALSE;
|
||||
GstXContext *xcontext;
|
||||
GstMetaXImage *meta;
|
||||
|
||||
xcontext = ximagesink->xcontext;
|
||||
|
||||
ximage = gst_buffer_new ();
|
||||
meta = GST_META_XIMAGE_ADD (ximage);
|
||||
#ifdef HAVE_XSHM
|
||||
meta->SHMInfo.shmaddr = ((void *) -1);
|
||||
meta->SHMInfo.shmid = -1;
|
||||
#endif
|
||||
meta->width = width;
|
||||
meta->height = height;
|
||||
meta->sink = gst_object_ref (ximagesink);
|
||||
|
||||
GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
|
||||
meta->width, meta->height);
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
/* Setting an error handler to catch failure */
|
||||
error_caught = FALSE;
|
||||
handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
if (xcontext->use_xshm) {
|
||||
meta->ximage = XShmCreateImage (xcontext->disp,
|
||||
xcontext->visual,
|
||||
xcontext->depth,
|
||||
ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
|
||||
if (!meta->ximage || error_caught)
|
||||
goto create_failed;
|
||||
|
||||
/* we have to use the returned bytes_per_line for our shm size */
|
||||
meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
|
||||
GST_LOG_OBJECT (ximagesink,
|
||||
"XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
|
||||
meta->size, meta->width, meta->ximage->bytes_per_line);
|
||||
|
||||
/* get shared memory */
|
||||
meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
|
||||
if (meta->SHMInfo.shmid == -1)
|
||||
goto shmget_failed;
|
||||
|
||||
/* attach */
|
||||
meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
|
||||
if (meta->SHMInfo.shmaddr == ((void *) -1))
|
||||
goto shmat_failed;
|
||||
|
||||
/* now we can set up the image data */
|
||||
meta->ximage->data = meta->SHMInfo.shmaddr;
|
||||
meta->SHMInfo.readOnly = FALSE;
|
||||
|
||||
if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
|
||||
goto xattach_failed;
|
||||
|
||||
XSync (xcontext->disp, FALSE);
|
||||
|
||||
/* Now that everyone has attached, we can delete the shared memory segment.
|
||||
* This way, it will be deleted as soon as we detach later, and not
|
||||
* leaked if we crash. */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
} else
|
||||
#endif /* HAVE_XSHM */
|
||||
{
|
||||
guint allocsize;
|
||||
|
||||
meta->ximage = XCreateImage (xcontext->disp,
|
||||
xcontext->visual,
|
||||
xcontext->depth,
|
||||
ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
|
||||
if (!meta->ximage || error_caught)
|
||||
goto create_failed;
|
||||
|
||||
/* upstream will assume that rowstrides are multiples of 4, but this
|
||||
* doesn't always seem to be the case with XCreateImage() */
|
||||
if ((meta->ximage->bytes_per_line % 4) != 0) {
|
||||
GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
|
||||
"usually assumed");
|
||||
}
|
||||
|
||||
/* we have to use the returned bytes_per_line for our image size */
|
||||
meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
|
||||
|
||||
/* alloc a bit more for unexpected strides to avoid crashes upstream.
|
||||
* FIXME: if we get an unrounded stride, the image will be displayed
|
||||
* distorted, since all upstream elements assume a rounded stride */
|
||||
allocsize =
|
||||
GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
|
||||
|
||||
meta->ximage->data = g_malloc (allocsize);
|
||||
GST_LOG_OBJECT (ximagesink,
|
||||
"non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
|
||||
"stride %d", meta->size, allocsize, meta->width,
|
||||
meta->ximage->bytes_per_line);
|
||||
|
||||
XSync (xcontext->disp, FALSE);
|
||||
}
|
||||
|
||||
/* Reset error handler */
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
|
||||
GST_BUFFER_DATA (ximage) = (guchar *) meta->ximage->data;
|
||||
GST_BUFFER_SIZE (ximage) = meta->size;
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
success = TRUE;
|
||||
|
||||
beach:
|
||||
if (!success) {
|
||||
gst_buffer_unref (GST_BUFFER_CAST (ximage));
|
||||
ximage = NULL;
|
||||
}
|
||||
return ximage;
|
||||
|
||||
/* ERRORS */
|
||||
create_failed:
|
||||
{
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
/* Reset error handler */
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
/* Push an error */
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("could not XShmCreateImage a %dx%d image", meta->width, meta->height));
|
||||
goto beach;
|
||||
}
|
||||
shmget_failed:
|
||||
{
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
|
||||
meta->size));
|
||||
goto beach;
|
||||
}
|
||||
shmat_failed:
|
||||
{
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("Failed to shmat: %s", g_strerror (errno)));
|
||||
/* Clean up the shared memory segment */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
xattach_failed:
|
||||
{
|
||||
/* Clean up shm seg */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height), ("Failed to XShmAttach"));
|
||||
goto beach;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = meta->sink;
|
||||
|
||||
GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
|
||||
|
||||
/* Hold the object lock to ensure the XContext doesn't disappear */
|
||||
GST_OBJECT_LOCK (ximagesink);
|
||||
/* We might have some buffers destroyed after changing state to NULL */
|
||||
if (!ximagesink->xcontext) {
|
||||
GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
|
||||
#ifdef HAVE_XSHM
|
||||
if (meta->SHMInfo.shmaddr != ((void *) -1)) {
|
||||
shmdt (meta->SHMInfo.shmaddr);
|
||||
}
|
||||
#endif
|
||||
goto beach;
|
||||
}
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
if (ximagesink->xcontext->use_xshm) {
|
||||
if (meta->SHMInfo.shmaddr != ((void *) -1)) {
|
||||
XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
|
||||
XSync (ximagesink->xcontext->disp, 0);
|
||||
shmdt (meta->SHMInfo.shmaddr);
|
||||
meta->SHMInfo.shmaddr = (void *) -1;
|
||||
}
|
||||
if (meta->ximage)
|
||||
XDestroyImage (meta->ximage);
|
||||
|
||||
} else
|
||||
#endif /* HAVE_XSHM */
|
||||
{
|
||||
if (meta->ximage) {
|
||||
XDestroyImage (meta->ximage);
|
||||
}
|
||||
}
|
||||
|
||||
XSync (ximagesink->xcontext->disp, FALSE);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
beach:
|
||||
GST_OBJECT_UNLOCK (ximagesink);
|
||||
}
|
||||
|
||||
#ifdef HAVE_XSHM /* Check that XShm calls actually work */
|
||||
gboolean
|
||||
gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
|
||||
GstXContext * xcontext)
|
||||
{
|
||||
XImage *ximage;
|
||||
XShmSegmentInfo SHMInfo;
|
||||
size_t size;
|
||||
int (*handler) (Display *, XErrorEvent *);
|
||||
gboolean result = FALSE;
|
||||
gboolean did_attach = FALSE;
|
||||
|
||||
g_return_val_if_fail (xcontext != NULL, FALSE);
|
||||
|
||||
/* Sync to ensure any older errors are already processed */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
|
||||
/* Set defaults so we don't free these later unnecessarily */
|
||||
SHMInfo.shmaddr = ((void *) -1);
|
||||
SHMInfo.shmid = -1;
|
||||
|
||||
/* Setting an error handler to catch failure */
|
||||
error_caught = FALSE;
|
||||
handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
|
||||
|
||||
/* Trying to create a 1x1 ximage */
|
||||
GST_DEBUG ("XShmCreateImage of 1x1");
|
||||
|
||||
ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
|
||||
xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
|
||||
|
||||
/* Might cause an error, sync to ensure it is noticed */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
if (!ximage || error_caught) {
|
||||
GST_WARNING ("could not XShmCreateImage a 1x1 image");
|
||||
goto beach;
|
||||
}
|
||||
size = ximage->height * ximage->bytes_per_line;
|
||||
|
||||
SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
|
||||
if (SHMInfo.shmid == -1) {
|
||||
GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
|
||||
size);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
|
||||
if (SHMInfo.shmaddr == ((void *) -1)) {
|
||||
GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
|
||||
/* Clean up shm seg */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
ximage->data = SHMInfo.shmaddr;
|
||||
SHMInfo.readOnly = FALSE;
|
||||
|
||||
if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
|
||||
GST_WARNING ("Failed to XShmAttach");
|
||||
/* Clean up shm seg */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
/* Sync to ensure we see any errors we caused */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
|
||||
/* Delete the shared memory segment as soon as everyone is attached.
|
||||
* This way, it will be deleted as soon as we detach later, and not
|
||||
* leaked if we crash. */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
|
||||
if (!error_caught) {
|
||||
did_attach = TRUE;
|
||||
/* store whether we succeeded in result */
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
beach:
|
||||
/* Sync to ensure we swallow any errors we caused and reset error_caught */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
|
||||
if (did_attach) {
|
||||
XShmDetach (xcontext->disp, &SHMInfo);
|
||||
XSync (xcontext->disp, FALSE);
|
||||
}
|
||||
if (SHMInfo.shmaddr != ((void *) -1))
|
||||
shmdt (SHMInfo.shmaddr);
|
||||
if (ximage)
|
||||
XDestroyImage (ximage);
|
||||
return result;
|
||||
}
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
/* bufferpool */
|
||||
static void gst_ximage_buffer_pool_finalize (GObject * object);
|
||||
|
||||
#define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
|
||||
|
||||
struct _GstXImageBufferPoolPrivate
|
||||
{
|
||||
GstCaps *caps;
|
||||
gint width, height;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
|
||||
GST_TYPE_BUFFER_POOL);
|
||||
|
||||
static gboolean
|
||||
ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
|
||||
{
|
||||
GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
|
||||
GstXImageBufferPoolPrivate *priv = xpool->priv;
|
||||
GstStructure *structure;
|
||||
gint width, height;
|
||||
const GstCaps *caps;
|
||||
|
||||
if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL))
|
||||
goto wrong_config;
|
||||
|
||||
if (caps == NULL)
|
||||
goto no_caps;
|
||||
|
||||
/* now parse the caps from the config */
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (!gst_structure_get_int (structure, "width", &width) ||
|
||||
!gst_structure_get_int (structure, "height", &height))
|
||||
goto wrong_caps;
|
||||
|
||||
/* keep track of the width and height and caps */
|
||||
if (priv->caps)
|
||||
gst_caps_unref (priv->caps);
|
||||
priv->caps = gst_caps_copy (caps);
|
||||
priv->width = width;
|
||||
priv->height = height;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
wrong_config:
|
||||
{
|
||||
GST_WARNING_OBJECT (pool, "invalid config");
|
||||
return FALSE;
|
||||
}
|
||||
no_caps:
|
||||
{
|
||||
GST_WARNING_OBJECT (pool, "no caps in config");
|
||||
return FALSE;
|
||||
}
|
||||
wrong_caps:
|
||||
{
|
||||
GST_WARNING_OBJECT (pool,
|
||||
"failed getting geometry from caps %" GST_PTR_FORMAT, caps);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function handles GstXImageBuffer creation depending on XShm availability */
|
||||
static GstFlowReturn
|
||||
ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
|
||||
GstBufferPoolParams * params)
|
||||
{
|
||||
GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
|
||||
GstXImageBufferPoolPrivate *priv = xpool->priv;
|
||||
GstBuffer *ximage;
|
||||
|
||||
ximage = gst_ximage_buffer_new (xpool->sink, priv->width, priv->height);
|
||||
if (ximage == NULL)
|
||||
goto no_buffer;
|
||||
|
||||
*buffer = ximage;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERROR */
|
||||
no_buffer:
|
||||
{
|
||||
GST_WARNING_OBJECT (pool, "can't create image");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ximage_buffer_pool_free (GstBufferPool * pool, GstBuffer * buffer)
|
||||
{
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
|
||||
GstBufferPool *
|
||||
gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
|
||||
{
|
||||
GstXImageBufferPool *pool;
|
||||
|
||||
g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
|
||||
|
||||
pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
|
||||
pool->sink = gst_object_ref (ximagesink);
|
||||
|
||||
return GST_BUFFER_POOL_CAST (pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
|
||||
|
||||
gobject_class->finalize = gst_ximage_buffer_pool_finalize;
|
||||
|
||||
gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
|
||||
gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
|
||||
gstbufferpool_class->free_buffer = ximage_buffer_pool_free;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
|
||||
{
|
||||
pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximage_buffer_pool_finalize (GObject * object)
|
||||
{
|
||||
GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
|
||||
GstXImageBufferPoolPrivate *priv = pool->priv;
|
||||
|
||||
GST_LOG_OBJECT (pool->sink, "finalize XImage buffer pool");
|
||||
|
||||
if (priv->caps)
|
||||
gst_caps_unref (priv->caps);
|
||||
gst_object_unref (pool->sink);
|
||||
|
||||
G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);
|
||||
}
|
116
sys/ximage/ximagepool.h
Normal file
116
sys/ximage/ximagepool.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2005> Julien Moutte <julien@moutte.net>
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_XIMAGEPOOL_H__
|
||||
#define __GST_XIMAGEPOOL_H__
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
#include <X11/extensions/XShm.h>
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstMetaXImage GstMetaXImage;
|
||||
|
||||
typedef struct _GstXImageBufferPool GstXImageBufferPool;
|
||||
typedef struct _GstXImageBufferPoolClass GstXImageBufferPoolClass;
|
||||
typedef struct _GstXImageBufferPoolPrivate GstXImageBufferPoolPrivate;
|
||||
|
||||
#include "ximagesink.h"
|
||||
|
||||
const GstMetaInfo * gst_meta_ximage_get_info (void);
|
||||
#define GST_META_INFO_XIMAGE (gst_meta_ximage_get_info())
|
||||
|
||||
#define GST_META_GET(b,t,i) ((t *)gst_buffer_get_meta((b),(i)))
|
||||
#define GST_META_ADD(b,t,i,p) ((t *)gst_buffer_add_meta((b),(i),(p)))
|
||||
|
||||
#define GST_META_XIMAGE_GET(b) GST_META_GET(b,GstMetaXImage,GST_META_INFO_XIMAGE)
|
||||
#define GST_META_XIMAGE_ADD(b) GST_META_ADD(b,GstMetaXImage,GST_META_INFO_XIMAGE,NULL)
|
||||
|
||||
/**
|
||||
* GstMetaXImage:
|
||||
* @simagesink: a reference to the our #GstXImageSink
|
||||
* @ximage: the XImage of this buffer
|
||||
* @width: the width in pixels of XImage @ximage
|
||||
* @height: the height in pixels of XImage @ximage
|
||||
* @size: the size in bytes of XImage @ximage
|
||||
*
|
||||
* Subclass of #GstBuffer containing additional information about an XImage.
|
||||
*/
|
||||
struct _GstMetaXImage
|
||||
{
|
||||
GstMeta meta;
|
||||
|
||||
GstXImageSink *sink;
|
||||
|
||||
XImage *ximage;
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
XShmSegmentInfo SHMInfo;
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
gint width, height;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
GstBuffer * gst_ximage_buffer_new (GstXImageSink *ximagesink, gint width, gint height);
|
||||
|
||||
/* buffer pool functions */
|
||||
#define GST_TYPE_XIMAGE_BUFFER_POOL (gst_ximage_buffer_pool_get_type())
|
||||
#define GST_IS_XIMAGE_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL))
|
||||
#define GST_XIMAGE_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPool))
|
||||
#define GST_XIMAGE_BUFFER_POOL_CAST(obj) ((GstXImageBufferPool*)(obj))
|
||||
|
||||
struct _GstXImageBufferPool
|
||||
{
|
||||
GstBufferPool bufferpool;
|
||||
|
||||
GstXImageSink *sink;
|
||||
|
||||
GstXImageBufferPoolPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GstXImageBufferPoolClass
|
||||
{
|
||||
GstBufferPoolClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_ximage_buffer_pool_get_type (void);
|
||||
|
||||
GstBufferPool * gst_ximage_buffer_pool_new (GstXImageSink * ximagesink);
|
||||
|
||||
gboolean gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
|
||||
GstXContext * xcontext);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_XIMAGEPOOL_H__ */
|
|
@ -129,8 +129,6 @@ MotifWmHints, MwmHints;
|
|||
#define MWM_HINTS_DECORATIONS (1L << 1)
|
||||
|
||||
static void gst_ximagesink_reset (GstXImageSink * ximagesink);
|
||||
static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
|
||||
GstBuffer * ximage);
|
||||
static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
|
||||
static void gst_ximagesink_expose (GstXOverlay * overlay);
|
||||
|
||||
|
@ -164,446 +162,8 @@ static GstVideoSinkClass *parent_class = NULL;
|
|||
/* */
|
||||
/* ============================================================= */
|
||||
|
||||
/* ximage buffers */
|
||||
const GstMetaInfo *
|
||||
gst_meta_ximage_get_info (void)
|
||||
{
|
||||
static const GstMetaInfo *meta_ximage_info = NULL;
|
||||
|
||||
if (meta_ximage_info == NULL) {
|
||||
meta_ximage_info = gst_meta_register ("GstMetaXImage", "GstMetaXImage",
|
||||
sizeof (GstMetaXImage),
|
||||
(GstMetaInitFunction) NULL,
|
||||
(GstMetaFreeFunction) NULL,
|
||||
(GstMetaCopyFunction) NULL,
|
||||
(GstMetaSubFunction) NULL,
|
||||
(GstMetaSerializeFunction) NULL, (GstMetaDeserializeFunction) NULL);
|
||||
}
|
||||
return meta_ximage_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximage_buffer_dispose (GstBuffer * ximage)
|
||||
{
|
||||
GstMetaXImage *meta = NULL;
|
||||
GstXImageSink *ximagesink = NULL;
|
||||
gboolean recycled = FALSE;
|
||||
gboolean running;
|
||||
|
||||
meta = GST_META_XIMAGE_GET (ximage);
|
||||
g_return_if_fail (meta != NULL);
|
||||
|
||||
ximagesink = meta->ximagesink;
|
||||
if (G_UNLIKELY (ximagesink == NULL))
|
||||
goto no_sink;
|
||||
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
GST_OBJECT_LOCK (ximagesink);
|
||||
running = ximagesink->running;
|
||||
GST_OBJECT_UNLOCK (ximagesink);
|
||||
|
||||
if (running == FALSE) {
|
||||
/* If the sink is shutting down, need to clear the image */
|
||||
GST_DEBUG_OBJECT (ximagesink,
|
||||
"destroy image %p because the sink is shutting down", ximage);
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximage);
|
||||
} else if ((meta->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
|
||||
(meta->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
|
||||
/* If our geometry changed we can't reuse that image. */
|
||||
GST_DEBUG_OBJECT (ximagesink,
|
||||
"destroy image %p as its size changed %dx%d vs current %dx%d",
|
||||
ximage, meta->width, meta->height,
|
||||
GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximage);
|
||||
} else {
|
||||
/* In that case we can reuse the image and add it to our image pool. */
|
||||
GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
|
||||
/* need to increment the refcount again to recycle */
|
||||
gst_buffer_ref (ximage);
|
||||
g_mutex_lock (ximagesink->pool_lock);
|
||||
ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
|
||||
g_mutex_unlock (ximagesink->pool_lock);
|
||||
recycled = TRUE;
|
||||
}
|
||||
return;
|
||||
|
||||
no_sink:
|
||||
{
|
||||
GST_WARNING ("no sink found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximage_buffer_free (GstBuffer * ximage)
|
||||
{
|
||||
GstMetaXImage *meta = GST_META_XIMAGE_GET (ximage);
|
||||
g_return_if_fail (meta != NULL);
|
||||
|
||||
/* make sure it is not recycled */
|
||||
meta->width = -1;
|
||||
meta->height = -1;
|
||||
gst_buffer_unref (ximage);
|
||||
}
|
||||
|
||||
/* X11 stuff */
|
||||
|
||||
static gboolean error_caught = FALSE;
|
||||
|
||||
static int
|
||||
gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
|
||||
{
|
||||
char error_msg[1024];
|
||||
|
||||
XGetErrorText (display, xevent->error_code, error_msg, 1024);
|
||||
GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
|
||||
error_caught = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_XSHM /* Check that XShm calls actually work */
|
||||
|
||||
static gboolean
|
||||
gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
|
||||
GstXContext * xcontext)
|
||||
{
|
||||
XImage *ximage;
|
||||
XShmSegmentInfo SHMInfo;
|
||||
size_t size;
|
||||
int (*handler) (Display *, XErrorEvent *);
|
||||
gboolean result = FALSE;
|
||||
gboolean did_attach = FALSE;
|
||||
|
||||
g_return_val_if_fail (xcontext != NULL, FALSE);
|
||||
|
||||
/* Sync to ensure any older errors are already processed */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
|
||||
/* Set defaults so we don't free these later unnecessarily */
|
||||
SHMInfo.shmaddr = ((void *) -1);
|
||||
SHMInfo.shmid = -1;
|
||||
|
||||
/* Setting an error handler to catch failure */
|
||||
error_caught = FALSE;
|
||||
handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
|
||||
|
||||
/* Trying to create a 1x1 ximage */
|
||||
GST_DEBUG ("XShmCreateImage of 1x1");
|
||||
|
||||
ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
|
||||
xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
|
||||
|
||||
/* Might cause an error, sync to ensure it is noticed */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
if (!ximage || error_caught) {
|
||||
GST_WARNING ("could not XShmCreateImage a 1x1 image");
|
||||
goto beach;
|
||||
}
|
||||
size = ximage->height * ximage->bytes_per_line;
|
||||
|
||||
SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
|
||||
if (SHMInfo.shmid == -1) {
|
||||
GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
|
||||
size);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
|
||||
if (SHMInfo.shmaddr == ((void *) -1)) {
|
||||
GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
|
||||
/* Clean up shm seg */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
ximage->data = SHMInfo.shmaddr;
|
||||
SHMInfo.readOnly = FALSE;
|
||||
|
||||
if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
|
||||
GST_WARNING ("Failed to XShmAttach");
|
||||
/* Clean up shm seg */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
/* Sync to ensure we see any errors we caused */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
|
||||
/* Delete the shared memory segment as soon as everyone is attached.
|
||||
* This way, it will be deleted as soon as we detach later, and not
|
||||
* leaked if we crash. */
|
||||
shmctl (SHMInfo.shmid, IPC_RMID, NULL);
|
||||
|
||||
if (!error_caught) {
|
||||
did_attach = TRUE;
|
||||
/* store whether we succeeded in result */
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
beach:
|
||||
/* Sync to ensure we swallow any errors we caused and reset error_caught */
|
||||
XSync (xcontext->disp, FALSE);
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
|
||||
if (did_attach) {
|
||||
XShmDetach (xcontext->disp, &SHMInfo);
|
||||
XSync (xcontext->disp, FALSE);
|
||||
}
|
||||
if (SHMInfo.shmaddr != ((void *) -1))
|
||||
shmdt (SHMInfo.shmaddr);
|
||||
if (ximage)
|
||||
XDestroyImage (ximage);
|
||||
return result;
|
||||
}
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
/* This function handles GstXImageBuffer creation depending on XShm availability */
|
||||
static GstBuffer *
|
||||
gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
|
||||
{
|
||||
GstBuffer *buffer = NULL;
|
||||
GstMetaXImage *meta = NULL;
|
||||
GstStructure *structure = NULL;
|
||||
gboolean succeeded = FALSE;
|
||||
int (*handler) (Display *, XErrorEvent *);
|
||||
|
||||
g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
|
||||
|
||||
|
||||
buffer = gst_buffer_new ();
|
||||
GST_MINI_OBJECT_CAST (buffer)->dispose =
|
||||
(GstMiniObjectDisposeFunction) gst_ximage_buffer_dispose;
|
||||
meta = GST_META_XIMAGE_ADD (buffer);
|
||||
#ifdef HAVE_XSHM
|
||||
meta->SHMInfo.shmaddr = ((void *) -1);
|
||||
meta->SHMInfo.shmid = -1;
|
||||
#endif
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (!gst_structure_get_int (structure, "width", &meta->width) ||
|
||||
!gst_structure_get_int (structure, "height", &meta->height)) {
|
||||
GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
|
||||
meta->width, meta->height);
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
/* Setting an error handler to catch failure */
|
||||
error_caught = FALSE;
|
||||
handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
if (ximagesink->xcontext->use_xshm) {
|
||||
meta->ximage = XShmCreateImage (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->visual,
|
||||
ximagesink->xcontext->depth,
|
||||
ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
|
||||
if (!meta->ximage || error_caught) {
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
/* Reset error handler */
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
/* Push an error */
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("could not XShmCreateImage a %dx%d image",
|
||||
meta->width, meta->height));
|
||||
goto beach;
|
||||
}
|
||||
|
||||
/* we have to use the returned bytes_per_line for our shm size */
|
||||
meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
|
||||
GST_LOG_OBJECT (ximagesink,
|
||||
"XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
|
||||
meta->size, meta->width, meta->ximage->bytes_per_line);
|
||||
|
||||
meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
|
||||
if (meta->SHMInfo.shmid == -1) {
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
|
||||
meta->size));
|
||||
goto beach;
|
||||
}
|
||||
|
||||
meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
|
||||
if (meta->SHMInfo.shmaddr == ((void *) -1)) {
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("Failed to shmat: %s", g_strerror (errno)));
|
||||
/* Clean up the shared memory segment */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
goto beach;
|
||||
}
|
||||
|
||||
meta->ximage->data = meta->SHMInfo.shmaddr;
|
||||
meta->SHMInfo.readOnly = FALSE;
|
||||
|
||||
if (XShmAttach (ximagesink->xcontext->disp, &meta->SHMInfo) == 0) {
|
||||
/* Clean up shm seg */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height), ("Failed to XShmAttach"));
|
||||
goto beach;
|
||||
}
|
||||
|
||||
XSync (ximagesink->xcontext->disp, FALSE);
|
||||
|
||||
/* Now that everyone has attached, we can delete the shared memory segment.
|
||||
* This way, it will be deleted as soon as we detach later, and not
|
||||
* leaked if we crash. */
|
||||
shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
|
||||
|
||||
} else
|
||||
#endif /* HAVE_XSHM */
|
||||
{
|
||||
guint allocsize;
|
||||
|
||||
meta->ximage = XCreateImage (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->visual,
|
||||
ximagesink->xcontext->depth,
|
||||
ZPixmap, 0, NULL,
|
||||
meta->width, meta->height, ximagesink->xcontext->bpp, 0);
|
||||
if (!meta->ximage || error_caught) {
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
/* Reset error handler */
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
/* Push an error */
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("could not XCreateImage a %dx%d image", meta->width, meta->height));
|
||||
goto beach;
|
||||
}
|
||||
|
||||
/* upstream will assume that rowstrides are multiples of 4, but this
|
||||
* doesn't always seem to be the case with XCreateImage() */
|
||||
if ((meta->ximage->bytes_per_line % 4) != 0) {
|
||||
GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
|
||||
"usually assumed");
|
||||
}
|
||||
|
||||
/* we have to use the returned bytes_per_line for our image size */
|
||||
meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
|
||||
|
||||
/* alloc a bit more for unexpected strides to avoid crashes upstream.
|
||||
* FIXME: if we get an unrounded stride, the image will be displayed
|
||||
* distorted, since all upstream elements assume a rounded stride */
|
||||
allocsize =
|
||||
GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
|
||||
meta->ximage->data = g_malloc (allocsize);
|
||||
GST_LOG_OBJECT (ximagesink,
|
||||
"non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
|
||||
"stride %d", meta->size, allocsize, meta->width,
|
||||
meta->ximage->bytes_per_line);
|
||||
|
||||
XSync (ximagesink->xcontext->disp, FALSE);
|
||||
}
|
||||
|
||||
/* Reset error handler */
|
||||
error_caught = FALSE;
|
||||
XSetErrorHandler (handler);
|
||||
|
||||
succeeded = TRUE;
|
||||
|
||||
/* Keep a ref to our sink */
|
||||
meta->ximagesink = gst_object_ref (ximagesink);
|
||||
|
||||
GST_BUFFER_DATA (buffer) = (guchar *) meta->ximage->data;
|
||||
GST_BUFFER_SIZE (buffer) = meta->size;
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
beach:
|
||||
if (!succeeded) {
|
||||
gst_ximage_buffer_free (buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* This function destroys a GstBuffer with GstXImageData handling XShm availability */
|
||||
static void
|
||||
gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink, GstBuffer * ximage)
|
||||
{
|
||||
GstMetaXImage *meta;
|
||||
|
||||
g_return_if_fail (ximage != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
meta = GST_META_XIMAGE_GET (ximage);
|
||||
g_return_if_fail (meta != NULL);
|
||||
|
||||
/* Hold the object lock to ensure the XContext doesn't disappear */
|
||||
GST_OBJECT_LOCK (ximagesink);
|
||||
|
||||
/* If the destroyed image is the current one we destroy our reference too */
|
||||
if (ximagesink->cur_image == ximage) {
|
||||
ximagesink->cur_image = NULL;
|
||||
}
|
||||
|
||||
/* We might have some buffers destroyed after changing state to NULL */
|
||||
if (!ximagesink->xcontext) {
|
||||
GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
|
||||
#ifdef HAVE_XSHM
|
||||
if (meta->SHMInfo.shmaddr != ((void *) -1)) {
|
||||
shmdt (meta->SHMInfo.shmaddr);
|
||||
}
|
||||
#endif
|
||||
goto beach;
|
||||
}
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
if (ximagesink->xcontext->use_xshm) {
|
||||
if (meta->SHMInfo.shmaddr != ((void *) -1)) {
|
||||
XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
|
||||
XSync (ximagesink->xcontext->disp, 0);
|
||||
shmdt (meta->SHMInfo.shmaddr);
|
||||
}
|
||||
if (meta->ximage)
|
||||
XDestroyImage (meta->ximage);
|
||||
|
||||
} else
|
||||
#endif /* HAVE_XSHM */
|
||||
{
|
||||
if (meta->ximage) {
|
||||
XDestroyImage (meta->ximage);
|
||||
}
|
||||
}
|
||||
|
||||
XSync (ximagesink->xcontext->disp, FALSE);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
beach:
|
||||
GST_OBJECT_UNLOCK (ximagesink);
|
||||
|
||||
if (meta->ximagesink) {
|
||||
/* Release the ref to our sink */
|
||||
meta->ximagesink = NULL;
|
||||
gst_object_unref (ximagesink);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* We are called with the x_lock taken */
|
||||
static void
|
||||
gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
|
||||
|
@ -1384,22 +944,6 @@ gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
|
|||
g_free (xcontext);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
|
||||
{
|
||||
g_mutex_lock (ximagesink->pool_lock);
|
||||
|
||||
while (ximagesink->buffer_pool) {
|
||||
GstBuffer *ximage = ximagesink->buffer_pool->data;
|
||||
|
||||
ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
|
||||
ximagesink->buffer_pool);
|
||||
gst_ximage_buffer_free (ximage);
|
||||
}
|
||||
|
||||
g_mutex_unlock (ximagesink->pool_lock);
|
||||
}
|
||||
|
||||
/* Element stuff */
|
||||
|
||||
static GstCaps *
|
||||
|
@ -1440,6 +984,7 @@ gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|||
GstXImageSink *ximagesink;
|
||||
gboolean ret = TRUE;
|
||||
GstStructure *structure;
|
||||
GstBufferPool *newpool, *oldpool;
|
||||
const GValue *par;
|
||||
gint new_width, new_height;
|
||||
const GValue *fps;
|
||||
|
@ -1497,11 +1042,8 @@ gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|||
|
||||
/* Creating our window and our image */
|
||||
if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
|
||||
GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
|
||||
GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
|
||||
("Invalid image size."));
|
||||
return FALSE;
|
||||
}
|
||||
GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
|
||||
goto invalid_size;
|
||||
|
||||
g_mutex_lock (ximagesink->flow_lock);
|
||||
if (!ximagesink->xwindow) {
|
||||
|
@ -1512,20 +1054,26 @@ gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|||
ximagesink->draw_border = TRUE;
|
||||
g_mutex_unlock (ximagesink->flow_lock);
|
||||
|
||||
/* If our ximage has changed we destroy it, next chain iteration will create
|
||||
a new one */
|
||||
if ((ximagesink->ximage)) {
|
||||
GstMetaXImage *meta = GST_META_XIMAGE_GET (ximagesink->ximage);
|
||||
/* create a new pool for the new configuration */
|
||||
newpool = gst_ximage_buffer_pool_new (ximagesink);
|
||||
|
||||
if (((GST_VIDEO_SINK_WIDTH (ximagesink) != meta->width) ||
|
||||
(GST_VIDEO_SINK_HEIGHT (ximagesink) != meta->height))) {
|
||||
GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
|
||||
ximagesink->ximage);
|
||||
gst_buffer_unref (ximagesink->ximage);
|
||||
ximagesink->ximage = NULL;
|
||||
}
|
||||
structure = gst_buffer_pool_get_config (newpool);
|
||||
gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
|
||||
if (!gst_buffer_pool_set_config (newpool, structure))
|
||||
goto config_failed;
|
||||
|
||||
if (!gst_buffer_pool_set_active (newpool, TRUE))
|
||||
goto activate_failed;
|
||||
|
||||
oldpool = ximagesink->pool;
|
||||
ximagesink->pool = newpool;
|
||||
|
||||
/* unref the old sink */
|
||||
if (oldpool) {
|
||||
/* deactivate */
|
||||
gst_buffer_pool_set_active (oldpool, FALSE);
|
||||
gst_object_unref (oldpool);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -1539,6 +1087,22 @@ wrong_aspect:
|
|||
GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
|
||||
return FALSE;
|
||||
}
|
||||
invalid_size:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
|
||||
("Invalid image size."));
|
||||
return FALSE;
|
||||
}
|
||||
config_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (ximagesink, "failed to set config.");
|
||||
return FALSE;
|
||||
}
|
||||
activate_failed:
|
||||
{
|
||||
GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
|
@ -1633,6 +1197,7 @@ gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
|||
static GstFlowReturn
|
||||
gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn res;
|
||||
GstXImageSink *ximagesink;
|
||||
GstMetaXImage *meta;
|
||||
|
||||
|
@ -1651,46 +1216,56 @@ gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
|
|||
/* If this buffer has been allocated using our buffer management we simply
|
||||
put the ximage which is in the PRIVATE pointer */
|
||||
GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
|
||||
if (!gst_ximagesink_ximage_put (ximagesink, buf))
|
||||
goto no_window;
|
||||
res = GST_FLOW_OK;
|
||||
} else {
|
||||
GstBuffer *temp;
|
||||
|
||||
/* Else we have to copy the data into our private image, */
|
||||
/* if we have one... */
|
||||
GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
|
||||
if (!ximagesink->ximage) {
|
||||
GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
|
||||
ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
|
||||
GST_BUFFER_CAPS (buf));
|
||||
if (!ximagesink->ximage)
|
||||
/* The create method should have posted an informative error */
|
||||
goto no_ximage;
|
||||
GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
|
||||
|
||||
if (GST_BUFFER_SIZE (ximagesink->ximage) < GST_BUFFER_SIZE (buf)) {
|
||||
meta = GST_META_XIMAGE_GET (ximagesink->ximage);
|
||||
/* we should have a pool, configured in setcaps */
|
||||
if (ximagesink->pool == NULL)
|
||||
goto no_pool;
|
||||
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer of %dx%d pixels",
|
||||
meta->width, meta->height),
|
||||
("XServer allocated buffer size did not match input buffer"));
|
||||
/* take a buffer form our pool */
|
||||
res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
|
||||
if (res != GST_FLOW_OK)
|
||||
goto no_buffer;
|
||||
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
|
||||
ximagesink->ximage = NULL;
|
||||
goto no_ximage;
|
||||
}
|
||||
}
|
||||
memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
|
||||
MIN (GST_BUFFER_SIZE (buf), GST_BUFFER_SIZE (ximagesink->ximage)));
|
||||
if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
|
||||
goto no_window;
|
||||
if (GST_BUFFER_SIZE (temp) < GST_BUFFER_SIZE (buf))
|
||||
goto wrong_size;
|
||||
|
||||
memcpy (GST_BUFFER_DATA (temp), GST_BUFFER_DATA (buf),
|
||||
MIN (GST_BUFFER_SIZE (temp), GST_BUFFER_SIZE (buf)));
|
||||
|
||||
buf = temp;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
if (!gst_ximagesink_ximage_put (ximagesink, buf))
|
||||
goto no_window;
|
||||
|
||||
return res;
|
||||
|
||||
/* ERRORS */
|
||||
no_ximage:
|
||||
no_pool:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Internal error: can't allocate images"),
|
||||
("We don't have a bufferpool negotiated"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
no_buffer:
|
||||
{
|
||||
/* No image available. That's very bad ! */
|
||||
GST_WARNING_OBJECT (ximagesink, "could not create image");
|
||||
return res;
|
||||
}
|
||||
wrong_size:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
|
||||
("Failed to create output image buffer"),
|
||||
("XServer allocated buffer size did not match input buffer"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
no_window:
|
||||
|
@ -1701,7 +1276,6 @@ no_window:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
|
||||
{
|
||||
|
@ -1748,7 +1322,6 @@ gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
|||
GstCaps * caps, GstBuffer ** buf)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
GstMetaXImage *meta = NULL;
|
||||
GstBuffer *ximage = NULL;
|
||||
GstStructure *structure = NULL;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
@ -1877,34 +1450,21 @@ gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
|||
}
|
||||
|
||||
alloc:
|
||||
/* Inspect our buffer pool */
|
||||
g_mutex_lock (ximagesink->pool_lock);
|
||||
while (ximagesink->buffer_pool) {
|
||||
ximage = ximagesink->buffer_pool->data;
|
||||
|
||||
if (ximage) {
|
||||
meta = GST_META_XIMAGE_GET (ximage);
|
||||
|
||||
/* Removing from the pool */
|
||||
ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
|
||||
ximagesink->buffer_pool);
|
||||
|
||||
/* If the ximage is invalid for our need, destroy */
|
||||
if ((meta->width != width) || (meta->height != height)) {
|
||||
gst_ximage_buffer_free (ximage);
|
||||
ximage = NULL;
|
||||
} else {
|
||||
/* We found a suitable ximage */
|
||||
break;
|
||||
}
|
||||
if (gst_caps_is_equal (GST_PAD_CAPS (bsink->sinkpad), alloc_caps)) {
|
||||
/* we negotiated this format before, use the pool */
|
||||
if (ximagesink->pool) {
|
||||
GST_LOG_OBJECT (ximagesink, "retrieving buffer from pool");
|
||||
ret = gst_buffer_pool_acquire_buffer (ximagesink->pool, &ximage, NULL);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (ximagesink->pool_lock);
|
||||
|
||||
/* We haven't found anything, creating a new one */
|
||||
if (!ximage) {
|
||||
ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
|
||||
if (ximage == NULL) {
|
||||
/* Something new make a new image a new one */
|
||||
GST_LOG_OBJECT (ximagesink, "allocating new image");
|
||||
ximage = gst_ximage_buffer_new (ximagesink, width, height);
|
||||
}
|
||||
|
||||
/* Now we should have a ximage, set appropriate caps on it */
|
||||
if (ximage) {
|
||||
/* Make sure the buffer is cleared of any previously used flags */
|
||||
|
@ -2247,16 +1807,13 @@ gst_ximagesink_reset (GstXImageSink * ximagesink)
|
|||
if (thread)
|
||||
g_thread_join (thread);
|
||||
|
||||
if (ximagesink->ximage) {
|
||||
gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
|
||||
ximagesink->ximage = NULL;
|
||||
}
|
||||
if (ximagesink->cur_image) {
|
||||
gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
|
||||
ximagesink->cur_image = NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
gst_ximagesink_bufferpool_clear (ximagesink);
|
||||
#endif
|
||||
|
||||
g_mutex_lock (ximagesink->flow_lock);
|
||||
if (ximagesink->xwindow) {
|
||||
|
@ -2294,10 +1851,6 @@ gst_ximagesink_finalize (GObject * object)
|
|||
g_mutex_free (ximagesink->flow_lock);
|
||||
ximagesink->flow_lock = NULL;
|
||||
}
|
||||
if (ximagesink->pool_lock) {
|
||||
g_mutex_free (ximagesink->pool_lock);
|
||||
ximagesink->pool_lock = NULL;
|
||||
}
|
||||
|
||||
g_free (ximagesink->media_title);
|
||||
|
||||
|
@ -2310,7 +1863,6 @@ gst_ximagesink_init (GstXImageSink * ximagesink)
|
|||
ximagesink->display_name = NULL;
|
||||
ximagesink->xcontext = NULL;
|
||||
ximagesink->xwindow = NULL;
|
||||
ximagesink->ximage = NULL;
|
||||
ximagesink->cur_image = NULL;
|
||||
|
||||
ximagesink->event_thread = NULL;
|
||||
|
@ -2324,8 +1876,7 @@ gst_ximagesink_init (GstXImageSink * ximagesink)
|
|||
|
||||
ximagesink->par = NULL;
|
||||
|
||||
ximagesink->pool_lock = g_mutex_new ();
|
||||
ximagesink->buffer_pool = NULL;
|
||||
ximagesink->pool = NULL;
|
||||
|
||||
ximagesink->synchronous = FALSE;
|
||||
ximagesink->keep_aspect = FALSE;
|
||||
|
@ -2473,6 +2024,11 @@ gst_ximagesink_get_type (void)
|
|||
&navigation_info);
|
||||
g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
|
||||
&overlay_info);
|
||||
|
||||
/* register type and create class in a more safe place instead of at
|
||||
* runtime since the type registration and class creation is not
|
||||
* threadsafe. */
|
||||
g_type_class_ref (gst_ximage_buffer_pool_get_type ());
|
||||
}
|
||||
|
||||
return ximagesink_type;
|
||||
|
|
|
@ -49,14 +49,15 @@ G_BEGIN_DECLS
|
|||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_XIMAGESINK))
|
||||
#define GST_IS_XIMAGESINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_XIMAGESINK))
|
||||
|
||||
typedef struct _GstXContext GstXContext;
|
||||
typedef struct _GstXWindow GstXWindow;
|
||||
|
||||
typedef struct _GstMetaXImage GstMetaXImage;
|
||||
|
||||
typedef struct _GstXImageSink GstXImageSink;
|
||||
typedef struct _GstXImageSinkClass GstXImageSinkClass;
|
||||
|
||||
#include "ximagepool.h"
|
||||
|
||||
/*
|
||||
* GstXContext:
|
||||
* @disp: the X11 Display of this context
|
||||
|
@ -129,42 +130,6 @@ struct _GstXWindow
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* GstMetaXImage:
|
||||
* @ximagesink: a reference to our #GstXImageSink
|
||||
* @ximage: the XImage of this buffer
|
||||
* @width: the width in pixels of XImage @ximage
|
||||
* @height: the height in pixels of XImage @ximage
|
||||
* @size: the size in bytes of XImage @ximage
|
||||
*
|
||||
* Structure with additional information about an XImage.
|
||||
*/
|
||||
struct _GstMetaXImage
|
||||
{
|
||||
GstMeta meta;
|
||||
|
||||
/* Reference to the ximagesink we belong to */
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
XImage *ximage;
|
||||
|
||||
#ifdef HAVE_XSHM
|
||||
XShmSegmentInfo SHMInfo;
|
||||
#endif /* HAVE_XSHM */
|
||||
|
||||
gint width, height;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
const GstMetaInfo * gst_meta_ximage_get_info (void);
|
||||
#define GST_META_INFO_XIMAGE (gst_meta_ximage_get_info())
|
||||
|
||||
#define GST_META_GET(b,t,i) ((t *)gst_buffer_get_meta((b),(i)))
|
||||
#define GST_META_ADD(b,t,i,p) ((t *)gst_buffer_add_meta((b),(i),(p)))
|
||||
|
||||
#define GST_META_XIMAGE_GET(b) GST_META_GET(b,GstMetaXImage,GST_META_INFO_XIMAGE)
|
||||
#define GST_META_XIMAGE_ADD(b) GST_META_ADD(b,GstMetaXImage,GST_META_INFO_XIMAGE,NULL)
|
||||
|
||||
/**
|
||||
* GstXImageSink:
|
||||
* @display_name: the name of the Display we want to render to
|
||||
|
@ -203,7 +168,6 @@ struct _GstXImageSink
|
|||
|
||||
GstXContext *xcontext;
|
||||
GstXWindow *xwindow;
|
||||
GstBuffer *ximage;
|
||||
GstBuffer *cur_image;
|
||||
|
||||
GThread *event_thread;
|
||||
|
@ -219,8 +183,8 @@ struct _GstXImageSink
|
|||
/* object-set pixel aspect ratio */
|
||||
GValue *par;
|
||||
|
||||
GMutex *pool_lock;
|
||||
GSList *buffer_pool;
|
||||
/* the buffer pool */
|
||||
GstBufferPool *pool;
|
||||
|
||||
gboolean synchronous;
|
||||
gboolean keep_aspect;
|
||||
|
|
Loading…
Reference in a new issue