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>
33 #include <gst/video/gstvideopool.h>
35 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagepool);
36 #define GST_CAT_DEFAULT gst_debug_ximagepool
38 struct _GstXImageBufferPoolPrivate
42 GstVideoAlignment align;
45 gboolean add_metavideo;
46 gboolean need_alignment;
49 static void gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer);
53 gst_meta_ximage_get_info (void)
55 static const GstMetaInfo *meta_ximage_info = NULL;
57 if (meta_ximage_info == NULL) {
58 meta_ximage_info = gst_meta_register ("GstMetaXImage", "GstMetaXImage",
59 sizeof (GstMetaXImage),
60 (GstMetaInitFunction) NULL,
61 (GstMetaFreeFunction) gst_meta_ximage_free,
62 (GstMetaCopyFunction) NULL, (GstMetaTransformFunction) NULL);
64 return meta_ximage_info;
68 static gboolean error_caught = FALSE;
71 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
75 XGetErrorText (display, xevent->error_code, error_msg, 1024);
76 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
81 static GstMetaXImage *
82 gst_buffer_add_meta_ximage (GstBuffer * buffer, GstXImageBufferPool * xpool)
84 GstXImageSink *ximagesink;
85 int (*handler) (Display *, XErrorEvent *);
86 gboolean success = FALSE;
87 GstXContext *xcontext;
90 GstXImageBufferPoolPrivate *priv;
93 ximagesink = xpool->sink;
94 xcontext = ximagesink->xcontext;
96 width = priv->padded_width;
97 height = priv->padded_height;
100 (GstMetaXImage *) gst_buffer_add_meta (buffer, GST_META_INFO_XIMAGE,
103 meta->SHMInfo.shmaddr = ((void *) -1);
104 meta->SHMInfo.shmid = -1;
106 meta->x = priv->align.padding_left;
107 meta->y = priv->align.padding_top;
108 meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
109 meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
110 meta->sink = gst_object_ref (ximagesink);
112 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
115 g_mutex_lock (ximagesink->x_lock);
117 /* Setting an error handler to catch failure */
118 error_caught = FALSE;
119 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
122 if (xcontext->use_xshm) {
123 meta->ximage = XShmCreateImage (xcontext->disp,
125 xcontext->depth, ZPixmap, NULL, &meta->SHMInfo, width, height);
126 if (!meta->ximage || error_caught) {
127 g_mutex_unlock (ximagesink->x_lock);
129 /* Reset error flag */
130 error_caught = FALSE;
133 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
134 ("Failed to create output image buffer of %dx%d pixels",
136 ("could not XShmCreateImage a %dx%d image", width, height));
138 /* Retry without XShm */
139 ximagesink->xcontext->use_xshm = FALSE;
141 /* Hold X mutex again to try without XShm */
142 g_mutex_lock (ximagesink->x_lock);
147 /* we have to use the returned bytes_per_line for our shm size */
148 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
149 GST_LOG_OBJECT (ximagesink,
150 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
151 meta->size, width, meta->ximage->bytes_per_line);
153 /* get shared memory */
154 meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
155 if (meta->SHMInfo.shmid == -1)
159 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
160 if (meta->SHMInfo.shmaddr == ((void *) -1))
163 /* now we can set up the image data */
164 meta->ximage->data = meta->SHMInfo.shmaddr;
165 meta->SHMInfo.readOnly = FALSE;
167 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
170 XSync (xcontext->disp, FALSE);
172 /* Now that everyone has attached, we can delete the shared memory segment.
173 * This way, it will be deleted as soon as we detach later, and not
174 * leaked if we crash. */
175 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
177 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
178 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
181 #endif /* HAVE_XSHM */
185 meta->ximage = XCreateImage (xcontext->disp,
187 xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
188 if (!meta->ximage || error_caught)
191 /* upstream will assume that rowstrides are multiples of 4, but this
192 * doesn't always seem to be the case with XCreateImage() */
193 if ((meta->ximage->bytes_per_line % 4) != 0) {
194 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
198 /* we have to use the returned bytes_per_line for our image size */
199 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
201 /* alloc a bit more for unexpected strides to avoid crashes upstream.
202 * FIXME: if we get an unrounded stride, the image will be displayed
203 * distorted, since all upstream elements assume a rounded stride */
205 GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
207 meta->ximage->data = g_malloc (allocsize);
208 GST_LOG_OBJECT (ximagesink,
209 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
210 "stride %d", meta->size, allocsize, width,
211 meta->ximage->bytes_per_line);
213 XSync (xcontext->disp, FALSE);
216 /* Reset error handler */
217 error_caught = FALSE;
218 XSetErrorHandler (handler);
220 gst_buffer_take_memory (buffer, -1,
221 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
222 NULL, meta->size, 0, meta->size));
224 g_mutex_unlock (ximagesink->x_lock);
237 g_mutex_unlock (ximagesink->x_lock);
238 /* Reset error handler */
239 error_caught = FALSE;
240 XSetErrorHandler (handler);
242 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
243 ("Failed to create output image buffer of %dx%d pixels",
245 ("could not XShmCreateImage a %dx%d image", width, height));
250 g_mutex_unlock (ximagesink->x_lock);
251 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
252 ("Failed to create output image buffer of %dx%d pixels",
254 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
260 g_mutex_unlock (ximagesink->x_lock);
261 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
262 ("Failed to create output image buffer of %dx%d pixels",
263 width, height), ("Failed to shmat: %s", g_strerror (errno)));
264 /* Clean up the shared memory segment */
265 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
270 /* Clean up the shared memory segment */
271 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
272 g_mutex_unlock (ximagesink->x_lock);
274 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
275 ("Failed to create output image buffer of %dx%d pixels",
276 width, height), ("Failed to XShmAttach"));
282 gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer)
284 GstXImageSink *ximagesink;
286 ximagesink = meta->sink;
288 GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
290 /* Hold the object lock to ensure the XContext doesn't disappear */
291 GST_OBJECT_LOCK (ximagesink);
292 /* We might have some buffers destroyed after changing state to NULL */
293 if (ximagesink->xcontext == NULL) {
294 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
296 /* Need to free the shared memory segment even if the x context
297 * was already cleaned up */
298 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
299 shmdt (meta->SHMInfo.shmaddr);
305 g_mutex_lock (ximagesink->x_lock);
308 if (ximagesink->xcontext->use_xshm) {
309 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
310 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
311 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
312 XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
313 XSync (ximagesink->xcontext->disp, FALSE);
314 shmdt (meta->SHMInfo.shmaddr);
315 meta->SHMInfo.shmaddr = (void *) -1;
318 XDestroyImage (meta->ximage);
320 #endif /* HAVE_XSHM */
323 XDestroyImage (meta->ximage);
327 XSync (ximagesink->xcontext->disp, FALSE);
329 g_mutex_unlock (ximagesink->x_lock);
332 GST_OBJECT_UNLOCK (ximagesink);
334 gst_object_unref (meta->sink);
338 /* This function checks that it is actually really possible to create an image
341 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
342 GstXContext * xcontext)
345 XShmSegmentInfo SHMInfo;
347 int (*handler) (Display *, XErrorEvent *);
348 gboolean result = FALSE;
349 gboolean did_attach = FALSE;
351 g_return_val_if_fail (xcontext != NULL, FALSE);
353 /* Sync to ensure any older errors are already processed */
354 XSync (xcontext->disp, FALSE);
356 /* Set defaults so we don't free these later unnecessarily */
357 SHMInfo.shmaddr = ((void *) -1);
360 /* Setting an error handler to catch failure */
361 error_caught = FALSE;
362 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
364 /* Trying to create a 1x1 ximage */
365 GST_DEBUG ("XShmCreateImage of 1x1");
367 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
368 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
370 /* Might cause an error, sync to ensure it is noticed */
371 XSync (xcontext->disp, FALSE);
372 if (!ximage || error_caught) {
373 GST_WARNING ("could not XShmCreateImage a 1x1 image");
376 size = ximage->height * ximage->bytes_per_line;
378 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
379 if (SHMInfo.shmid == -1) {
380 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
385 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
386 if (SHMInfo.shmaddr == ((void *) -1)) {
387 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
388 /* Clean up the shared memory segment */
389 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
393 ximage->data = SHMInfo.shmaddr;
394 SHMInfo.readOnly = FALSE;
396 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
397 GST_WARNING ("Failed to XShmAttach");
398 /* Clean up the shared memory segment */
399 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
403 /* Sync to ensure we see any errors we caused */
404 XSync (xcontext->disp, FALSE);
406 /* Delete the shared memory segment as soon as everyone is attached.
407 * This way, it will be deleted as soon as we detach later, and not
408 * leaked if we crash. */
409 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
412 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
416 /* store whether we succeeded in result */
419 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
420 "Not using shared memory.");
424 /* Sync to ensure we swallow any errors we caused and reset error_caught */
425 XSync (xcontext->disp, FALSE);
427 error_caught = FALSE;
428 XSetErrorHandler (handler);
431 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
432 SHMInfo.shmid, SHMInfo.shmseg);
433 XShmDetach (xcontext->disp, &SHMInfo);
434 XSync (xcontext->disp, FALSE);
436 if (SHMInfo.shmaddr != ((void *) -1))
437 shmdt (SHMInfo.shmaddr);
439 XDestroyImage (ximage);
442 #endif /* HAVE_XSHM */
445 static void gst_ximage_buffer_pool_finalize (GObject * object);
447 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
448 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
450 #define gst_ximage_buffer_pool_parent_class parent_class
451 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
452 GST_TYPE_BUFFER_POOL);
454 static const gchar **
455 ximage_buffer_pool_get_options (GstBufferPool * pool)
457 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_META_VIDEO,
458 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
465 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
467 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
468 GstXImageBufferPoolPrivate *priv = xpool->priv;
472 if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL, NULL))
478 /* now parse the caps from the config */
479 if (!gst_video_info_from_caps (&info, caps))
484 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
487 /* keep track of the width and height and caps */
489 gst_caps_unref (priv->caps);
490 priv->caps = gst_caps_copy (caps);
492 /* check for the configured metadata */
493 priv->add_metavideo =
494 gst_buffer_pool_config_has_option (config,
495 GST_BUFFER_POOL_OPTION_META_VIDEO);
497 /* parse extra alignment info */
498 priv->need_alignment = gst_buffer_pool_config_has_option (config,
499 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
501 if (priv->need_alignment) {
502 gst_buffer_pool_config_get_video_alignment (config, &priv->align);
504 GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
505 priv->align.padding_left, priv->align.padding_left,
506 priv->align.padding_bottom);
508 /* we need the video metadata too now */
509 priv->add_metavideo = TRUE;
512 /* add the padding */
514 GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
515 priv->align.padding_right;
516 priv->padded_height =
517 GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
518 priv->align.padding_bottom;
520 return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
525 GST_WARNING_OBJECT (pool, "invalid config");
530 GST_WARNING_OBJECT (pool, "no caps in config");
535 GST_WARNING_OBJECT (pool,
536 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
541 /* This function handles GstXImageBuffer creation depending on XShm availability */
543 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
544 GstBufferPoolParams * params)
546 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
547 GstXImageBufferPoolPrivate *priv = xpool->priv;
554 ximage = gst_buffer_new ();
555 meta = gst_buffer_add_meta_ximage (ximage, xpool);
557 gst_buffer_unref (ximage);
560 if (priv->add_metavideo) {
563 GST_DEBUG_OBJECT (pool, "adding GstMetaVideo");
564 /* these are just the defaults for now */
565 meta = gst_buffer_add_meta_video (ximage, 0, GST_VIDEO_INFO_FORMAT (info),
566 priv->padded_width, priv->padded_height);
568 if (priv->need_alignment) {
569 gint vpad, hpad, pstride;
571 vpad = priv->align.padding_left;
572 hpad = priv->align.padding_top;
574 meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
575 meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
576 pstride = GST_VIDEO_INFO_COMP_PSTRIDE (&priv->info, 0);
578 meta->offset[0] += (vpad * meta->stride[0]) + (hpad * pstride);
588 GST_WARNING_OBJECT (pool, "can't create image");
589 return GST_FLOW_ERROR;
594 ximage_buffer_pool_free (GstBufferPool * pool, GstBuffer * buffer)
596 gst_buffer_unref (buffer);
600 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
602 GstXImageBufferPool *pool;
604 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
606 pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
607 pool->sink = gst_object_ref (ximagesink);
609 GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
611 return GST_BUFFER_POOL_CAST (pool);
615 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
617 GObjectClass *gobject_class = (GObjectClass *) klass;
618 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
620 g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
622 gobject_class->finalize = gst_ximage_buffer_pool_finalize;
624 gstbufferpool_class->get_options = ximage_buffer_pool_get_options;
625 gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
626 gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
627 gstbufferpool_class->free_buffer = ximage_buffer_pool_free;
631 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
633 pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
637 gst_ximage_buffer_pool_finalize (GObject * object)
639 GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
640 GstXImageBufferPoolPrivate *priv = pool->priv;
642 GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
645 gst_caps_unref (priv->caps);
646 gst_object_unref (pool->sink);
648 G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);