2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
25 #include "ximagesink.h"
27 /* Debugging category */
28 #include <gst/gstinfo.h>
30 /* Helper functions */
31 #include <gst/video/video.h>
32 #include <gst/video/gstmetavideo.h>
34 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagepool);
35 #define GST_CAT_DEFAULT gst_debug_ximagepool
37 static void gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer);
41 gst_meta_ximage_get_info (void)
43 static const GstMetaInfo *meta_ximage_info = NULL;
45 if (meta_ximage_info == NULL) {
46 meta_ximage_info = gst_meta_register ("GstMetaXImage", "GstMetaXImage",
47 sizeof (GstMetaXImage),
48 (GstMetaInitFunction) NULL,
49 (GstMetaFreeFunction) gst_meta_ximage_free,
50 (GstMetaCopyFunction) NULL, (GstMetaTransformFunction) NULL);
52 return meta_ximage_info;
56 static gboolean error_caught = FALSE;
59 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
63 XGetErrorText (display, xevent->error_code, error_msg, 1024);
64 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
70 gst_buffer_add_meta_ximage (GstBuffer * buffer, GstXImageSink * ximagesink,
71 gint width, gint height)
73 int (*handler) (Display *, XErrorEvent *);
74 gboolean success = FALSE;
75 GstXContext *xcontext;
78 xcontext = ximagesink->xcontext;
81 (GstMetaXImage *) gst_buffer_add_meta (buffer, GST_META_INFO_XIMAGE,
84 meta->SHMInfo.shmaddr = ((void *) -1);
85 meta->SHMInfo.shmid = -1;
88 meta->height = height;
89 meta->sink = gst_object_ref (ximagesink);
91 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
92 meta->width, meta->height);
94 g_mutex_lock (ximagesink->x_lock);
96 /* Setting an error handler to catch failure */
98 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
101 if (xcontext->use_xshm) {
102 meta->ximage = XShmCreateImage (xcontext->disp,
105 ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
106 if (!meta->ximage || error_caught) {
107 g_mutex_unlock (ximagesink->x_lock);
109 /* Reset error flag */
110 error_caught = FALSE;
113 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
114 ("Failed to create output image buffer of %dx%d pixels",
115 meta->width, meta->height),
116 ("could not XShmCreateImage a %dx%d image",
117 meta->width, meta->height));
119 /* Retry without XShm */
120 ximagesink->xcontext->use_xshm = FALSE;
122 /* Hold X mutex again to try without XShm */
123 g_mutex_lock (ximagesink->x_lock);
128 /* we have to use the returned bytes_per_line for our shm size */
129 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
130 GST_LOG_OBJECT (ximagesink,
131 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
132 meta->size, meta->width, meta->ximage->bytes_per_line);
134 /* get shared memory */
135 meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
136 if (meta->SHMInfo.shmid == -1)
140 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
141 if (meta->SHMInfo.shmaddr == ((void *) -1))
144 /* now we can set up the image data */
145 meta->ximage->data = meta->SHMInfo.shmaddr;
146 meta->SHMInfo.readOnly = FALSE;
148 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
151 XSync (xcontext->disp, FALSE);
153 /* Now that everyone has attached, we can delete the shared memory segment.
154 * This way, it will be deleted as soon as we detach later, and not
155 * leaked if we crash. */
156 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
158 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
159 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
162 #endif /* HAVE_XSHM */
166 meta->ximage = XCreateImage (xcontext->disp,
169 ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
170 if (!meta->ximage || error_caught)
173 /* upstream will assume that rowstrides are multiples of 4, but this
174 * doesn't always seem to be the case with XCreateImage() */
175 if ((meta->ximage->bytes_per_line % 4) != 0) {
176 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
180 /* we have to use the returned bytes_per_line for our image size */
181 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
183 /* alloc a bit more for unexpected strides to avoid crashes upstream.
184 * FIXME: if we get an unrounded stride, the image will be displayed
185 * distorted, since all upstream elements assume a rounded stride */
187 GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
189 meta->ximage->data = g_malloc (allocsize);
190 GST_LOG_OBJECT (ximagesink,
191 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
192 "stride %d", meta->size, allocsize, meta->width,
193 meta->ximage->bytes_per_line);
195 XSync (xcontext->disp, FALSE);
198 /* Reset error handler */
199 error_caught = FALSE;
200 XSetErrorHandler (handler);
202 gst_buffer_take_memory (buffer, -1,
203 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
204 NULL, meta->size, 0, meta->size));
206 g_mutex_unlock (ximagesink->x_lock);
219 g_mutex_unlock (ximagesink->x_lock);
220 /* Reset error handler */
221 error_caught = FALSE;
222 XSetErrorHandler (handler);
224 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
225 ("Failed to create output image buffer of %dx%d pixels",
226 meta->width, meta->height),
227 ("could not XShmCreateImage a %dx%d image", meta->width, meta->height));
232 g_mutex_unlock (ximagesink->x_lock);
233 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
234 ("Failed to create output image buffer of %dx%d pixels",
235 meta->width, meta->height),
236 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
242 g_mutex_unlock (ximagesink->x_lock);
243 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
244 ("Failed to create output image buffer of %dx%d pixels",
245 meta->width, meta->height),
246 ("Failed to shmat: %s", g_strerror (errno)));
247 /* Clean up the shared memory segment */
248 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
253 /* Clean up the shared memory segment */
254 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
255 g_mutex_unlock (ximagesink->x_lock);
257 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
258 ("Failed to create output image buffer of %dx%d pixels",
259 meta->width, meta->height), ("Failed to XShmAttach"));
265 gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer)
267 GstXImageSink *ximagesink;
269 ximagesink = meta->sink;
271 GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
273 /* Hold the object lock to ensure the XContext doesn't disappear */
274 GST_OBJECT_LOCK (ximagesink);
275 /* We might have some buffers destroyed after changing state to NULL */
276 if (ximagesink->xcontext == NULL) {
277 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
279 /* Need to free the shared memory segment even if the x context
280 * was already cleaned up */
281 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
282 shmdt (meta->SHMInfo.shmaddr);
288 g_mutex_lock (ximagesink->x_lock);
291 if (ximagesink->xcontext->use_xshm) {
292 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
293 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
294 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
295 XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
296 XSync (ximagesink->xcontext->disp, FALSE);
297 shmdt (meta->SHMInfo.shmaddr);
298 meta->SHMInfo.shmaddr = (void *) -1;
301 XDestroyImage (meta->ximage);
303 #endif /* HAVE_XSHM */
306 XDestroyImage (meta->ximage);
310 XSync (ximagesink->xcontext->disp, FALSE);
312 g_mutex_unlock (ximagesink->x_lock);
315 GST_OBJECT_UNLOCK (ximagesink);
317 gst_object_unref (meta->sink);
321 /* This function checks that it is actually really possible to create an image
324 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
325 GstXContext * xcontext)
328 XShmSegmentInfo SHMInfo;
330 int (*handler) (Display *, XErrorEvent *);
331 gboolean result = FALSE;
332 gboolean did_attach = FALSE;
334 g_return_val_if_fail (xcontext != NULL, FALSE);
336 /* Sync to ensure any older errors are already processed */
337 XSync (xcontext->disp, FALSE);
339 /* Set defaults so we don't free these later unnecessarily */
340 SHMInfo.shmaddr = ((void *) -1);
343 /* Setting an error handler to catch failure */
344 error_caught = FALSE;
345 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
347 /* Trying to create a 1x1 ximage */
348 GST_DEBUG ("XShmCreateImage of 1x1");
350 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
351 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
353 /* Might cause an error, sync to ensure it is noticed */
354 XSync (xcontext->disp, FALSE);
355 if (!ximage || error_caught) {
356 GST_WARNING ("could not XShmCreateImage a 1x1 image");
359 size = ximage->height * ximage->bytes_per_line;
361 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
362 if (SHMInfo.shmid == -1) {
363 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
368 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
369 if (SHMInfo.shmaddr == ((void *) -1)) {
370 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
371 /* Clean up the shared memory segment */
372 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
376 ximage->data = SHMInfo.shmaddr;
377 SHMInfo.readOnly = FALSE;
379 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
380 GST_WARNING ("Failed to XShmAttach");
381 /* Clean up the shared memory segment */
382 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
386 /* Sync to ensure we see any errors we caused */
387 XSync (xcontext->disp, FALSE);
389 /* Delete the shared memory segment as soon as everyone is attached.
390 * This way, it will be deleted as soon as we detach later, and not
391 * leaked if we crash. */
392 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
395 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
399 /* store whether we succeeded in result */
402 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
403 "Not using shared memory.");
407 /* Sync to ensure we swallow any errors we caused and reset error_caught */
408 XSync (xcontext->disp, FALSE);
410 error_caught = FALSE;
411 XSetErrorHandler (handler);
414 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
415 SHMInfo.shmid, SHMInfo.shmseg);
416 XShmDetach (xcontext->disp, &SHMInfo);
417 XSync (xcontext->disp, FALSE);
419 if (SHMInfo.shmaddr != ((void *) -1))
420 shmdt (SHMInfo.shmaddr);
422 XDestroyImage (ximage);
425 #endif /* HAVE_XSHM */
428 static void gst_ximage_buffer_pool_finalize (GObject * object);
430 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
431 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
433 struct _GstXImageBufferPoolPrivate
437 gboolean add_metavideo;
440 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
441 GST_TYPE_BUFFER_POOL);
443 static const gchar **
444 ximage_buffer_pool_get_metas (GstBufferPool * pool)
446 static const gchar *metas[] = { "GstMetaVideo", NULL };
452 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
454 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
455 GstXImageBufferPoolPrivate *priv = xpool->priv;
459 if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL, NULL))
465 /* now parse the caps from the config */
466 if (!gst_video_info_from_caps (&info, caps))
471 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
474 /* keep track of the width and height and caps */
476 gst_caps_unref (priv->caps);
477 priv->caps = gst_caps_copy (caps);
479 /* check for the configured metadata */
480 priv->add_metavideo =
481 gst_buffer_pool_config_has_meta (config, GST_META_API_VIDEO);
488 GST_WARNING_OBJECT (pool, "invalid config");
493 GST_WARNING_OBJECT (pool, "no caps in config");
498 GST_WARNING_OBJECT (pool,
499 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
504 /* This function handles GstXImageBuffer creation depending on XShm availability */
506 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
507 GstBufferPoolParams * params)
509 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
510 GstXImageBufferPoolPrivate *priv = xpool->priv;
514 ximage = gst_buffer_new ();
516 gst_buffer_add_meta_ximage (ximage, xpool->sink, priv->info.width,
519 gst_buffer_unref (ximage);
522 if (priv->add_metavideo) {
523 /* these are just the defaults for now */
524 gst_buffer_add_meta_video (ximage, 0, priv->info.format, priv->info.width,
534 GST_WARNING_OBJECT (pool, "can't create image");
535 return GST_FLOW_ERROR;
540 ximage_buffer_pool_free (GstBufferPool * pool, GstBuffer * buffer)
542 gst_buffer_unref (buffer);
546 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
548 GstXImageBufferPool *pool;
550 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
552 pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
553 pool->sink = gst_object_ref (ximagesink);
555 GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
557 return GST_BUFFER_POOL_CAST (pool);
561 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
563 GObjectClass *gobject_class = (GObjectClass *) klass;
564 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
566 g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
568 gobject_class->finalize = gst_ximage_buffer_pool_finalize;
570 gstbufferpool_class->get_metas = ximage_buffer_pool_get_metas;
571 gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
572 gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
573 gstbufferpool_class->free_buffer = ximage_buffer_pool_free;
577 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
579 pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
583 gst_ximage_buffer_pool_finalize (GObject * object)
585 GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
586 GstXImageBufferPoolPrivate *priv = pool->priv;
588 GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
591 gst_caps_unref (priv->caps);
592 gst_object_unref (pool->sink);
594 G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);