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/gstvideometa.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_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer);
53 gst_ximage_meta_api_get_type (void)
55 static volatile GType type;
56 static const gchar *tags[] =
57 { "memory", "size", "colorspace", "orientation", NULL };
59 if (g_once_init_enter (&type)) {
60 GType _type = gst_meta_api_type_register ("GstXImageMetaAPI", tags);
61 g_once_init_leave (&type, _type);
67 gst_ximage_meta_get_info (void)
69 static const GstMetaInfo *ximage_meta_info = NULL;
71 if (ximage_meta_info == NULL) {
73 gst_meta_register (GST_XIMAGE_META_API_TYPE, "GstXImageMeta",
74 sizeof (GstXImageMeta), (GstMetaInitFunction) NULL,
75 (GstMetaFreeFunction) gst_ximage_meta_free,
76 (GstMetaTransformFunction) NULL);
78 return ximage_meta_info;
82 static gboolean error_caught = FALSE;
85 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
89 XGetErrorText (display, xevent->error_code, error_msg, 1024);
90 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
95 static GstXImageMeta *
96 gst_buffer_add_ximage_meta (GstBuffer * buffer, GstXImageBufferPool * xpool)
98 GstXImageSink *ximagesink;
99 int (*handler) (Display *, XErrorEvent *);
100 gboolean success = FALSE;
101 GstXContext *xcontext;
104 GstXImageBufferPoolPrivate *priv;
107 ximagesink = xpool->sink;
108 xcontext = ximagesink->xcontext;
110 width = priv->padded_width;
111 height = priv->padded_height;
114 (GstXImageMeta *) gst_buffer_add_meta (buffer, GST_XIMAGE_META_INFO,
117 meta->SHMInfo.shmaddr = ((void *) -1);
118 meta->SHMInfo.shmid = -1;
120 meta->x = priv->align.padding_left;
121 meta->y = priv->align.padding_top;
122 meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
123 meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
124 meta->sink = gst_object_ref (ximagesink);
126 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
129 g_mutex_lock (ximagesink->x_lock);
131 /* Setting an error handler to catch failure */
132 error_caught = FALSE;
133 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
136 if (xcontext->use_xshm) {
137 meta->ximage = XShmCreateImage (xcontext->disp,
139 xcontext->depth, ZPixmap, NULL, &meta->SHMInfo, width, height);
140 if (!meta->ximage || error_caught) {
141 g_mutex_unlock (ximagesink->x_lock);
143 /* Reset error flag */
144 error_caught = FALSE;
147 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
148 ("Failed to create output image buffer of %dx%d pixels",
150 ("could not XShmCreateImage a %dx%d image", width, height));
152 /* Retry without XShm */
153 ximagesink->xcontext->use_xshm = FALSE;
155 /* Hold X mutex again to try without XShm */
156 g_mutex_lock (ximagesink->x_lock);
161 /* we have to use the returned bytes_per_line for our shm size */
162 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
163 GST_LOG_OBJECT (ximagesink,
164 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
165 meta->size, width, meta->ximage->bytes_per_line);
167 /* get shared memory */
168 meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
169 if (meta->SHMInfo.shmid == -1)
173 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
174 if (meta->SHMInfo.shmaddr == ((void *) -1))
177 /* now we can set up the image data */
178 meta->ximage->data = meta->SHMInfo.shmaddr;
179 meta->SHMInfo.readOnly = FALSE;
181 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
184 XSync (xcontext->disp, FALSE);
186 /* Now that everyone has attached, we can delete the shared memory segment.
187 * This way, it will be deleted as soon as we detach later, and not
188 * leaked if we crash. */
189 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
191 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
192 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
195 #endif /* HAVE_XSHM */
199 meta->ximage = XCreateImage (xcontext->disp,
201 xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
202 if (!meta->ximage || error_caught)
205 /* upstream will assume that rowstrides are multiples of 4, but this
206 * doesn't always seem to be the case with XCreateImage() */
207 if ((meta->ximage->bytes_per_line % 4) != 0) {
208 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
212 /* we have to use the returned bytes_per_line for our image size */
213 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
215 /* alloc a bit more for unexpected strides to avoid crashes upstream.
216 * FIXME: if we get an unrounded stride, the image will be displayed
217 * distorted, since all upstream elements assume a rounded stride */
219 GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
221 meta->ximage->data = g_malloc (allocsize);
222 GST_LOG_OBJECT (ximagesink,
223 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
224 "stride %d", meta->size, allocsize, width,
225 meta->ximage->bytes_per_line);
227 XSync (xcontext->disp, FALSE);
230 /* Reset error handler */
231 error_caught = FALSE;
232 XSetErrorHandler (handler);
234 gst_buffer_take_memory (buffer, -1,
235 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
236 meta->size, 0, meta->size, NULL, NULL));
238 g_mutex_unlock (ximagesink->x_lock);
251 g_mutex_unlock (ximagesink->x_lock);
252 /* Reset error handler */
253 error_caught = FALSE;
254 XSetErrorHandler (handler);
256 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
257 ("Failed to create output image buffer of %dx%d pixels",
259 ("could not XShmCreateImage a %dx%d image", width, height));
264 g_mutex_unlock (ximagesink->x_lock);
265 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
266 ("Failed to create output image buffer of %dx%d pixels",
268 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
274 g_mutex_unlock (ximagesink->x_lock);
275 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
276 ("Failed to create output image buffer of %dx%d pixels",
277 width, height), ("Failed to shmat: %s", g_strerror (errno)));
278 /* Clean up the shared memory segment */
279 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
284 /* Clean up the shared memory segment */
285 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
286 g_mutex_unlock (ximagesink->x_lock);
288 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
289 ("Failed to create output image buffer of %dx%d pixels",
290 width, height), ("Failed to XShmAttach"));
296 gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer)
298 GstXImageSink *ximagesink;
300 ximagesink = meta->sink;
302 GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
304 /* Hold the object lock to ensure the XContext doesn't disappear */
305 GST_OBJECT_LOCK (ximagesink);
306 /* We might have some buffers destroyed after changing state to NULL */
307 if (ximagesink->xcontext == NULL) {
308 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
310 /* Need to free the shared memory segment even if the x context
311 * was already cleaned up */
312 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
313 shmdt (meta->SHMInfo.shmaddr);
319 g_mutex_lock (ximagesink->x_lock);
322 if (ximagesink->xcontext->use_xshm) {
323 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
324 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
325 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
326 XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
327 XSync (ximagesink->xcontext->disp, FALSE);
328 shmdt (meta->SHMInfo.shmaddr);
329 meta->SHMInfo.shmaddr = (void *) -1;
332 XDestroyImage (meta->ximage);
334 #endif /* HAVE_XSHM */
337 XDestroyImage (meta->ximage);
341 XSync (ximagesink->xcontext->disp, FALSE);
343 g_mutex_unlock (ximagesink->x_lock);
346 GST_OBJECT_UNLOCK (ximagesink);
348 gst_object_unref (meta->sink);
352 /* This function checks that it is actually really possible to create an image
355 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
356 GstXContext * xcontext)
359 XShmSegmentInfo SHMInfo;
361 int (*handler) (Display *, XErrorEvent *);
362 gboolean result = FALSE;
363 gboolean did_attach = FALSE;
365 g_return_val_if_fail (xcontext != NULL, FALSE);
367 /* Sync to ensure any older errors are already processed */
368 XSync (xcontext->disp, FALSE);
370 /* Set defaults so we don't free these later unnecessarily */
371 SHMInfo.shmaddr = ((void *) -1);
374 /* Setting an error handler to catch failure */
375 error_caught = FALSE;
376 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
378 /* Trying to create a 1x1 ximage */
379 GST_DEBUG ("XShmCreateImage of 1x1");
381 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
382 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
384 /* Might cause an error, sync to ensure it is noticed */
385 XSync (xcontext->disp, FALSE);
386 if (!ximage || error_caught) {
387 GST_WARNING ("could not XShmCreateImage a 1x1 image");
390 size = ximage->height * ximage->bytes_per_line;
392 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
393 if (SHMInfo.shmid == -1) {
394 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
399 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
400 if (SHMInfo.shmaddr == ((void *) -1)) {
401 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
402 /* Clean up the shared memory segment */
403 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
407 ximage->data = SHMInfo.shmaddr;
408 SHMInfo.readOnly = FALSE;
410 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
411 GST_WARNING ("Failed to XShmAttach");
412 /* Clean up the shared memory segment */
413 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
417 /* Sync to ensure we see any errors we caused */
418 XSync (xcontext->disp, FALSE);
420 /* Delete the shared memory segment as soon as everyone is attached.
421 * This way, it will be deleted as soon as we detach later, and not
422 * leaked if we crash. */
423 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
426 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
430 /* store whether we succeeded in result */
433 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
434 "Not using shared memory.");
438 /* Sync to ensure we swallow any errors we caused and reset error_caught */
439 XSync (xcontext->disp, FALSE);
441 error_caught = FALSE;
442 XSetErrorHandler (handler);
445 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
446 SHMInfo.shmid, SHMInfo.shmseg);
447 XShmDetach (xcontext->disp, &SHMInfo);
448 XSync (xcontext->disp, FALSE);
450 if (SHMInfo.shmaddr != ((void *) -1))
451 shmdt (SHMInfo.shmaddr);
453 XDestroyImage (ximage);
456 #endif /* HAVE_XSHM */
459 static void gst_ximage_buffer_pool_finalize (GObject * object);
461 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
462 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
464 #define gst_ximage_buffer_pool_parent_class parent_class
465 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
466 GST_TYPE_BUFFER_POOL);
468 static const gchar **
469 ximage_buffer_pool_get_options (GstBufferPool * pool)
471 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
472 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
479 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
481 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
482 GstXImageBufferPoolPrivate *priv = xpool->priv;
486 if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL, NULL))
492 /* now parse the caps from the config */
493 if (!gst_video_info_from_caps (&info, caps))
498 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
501 /* keep track of the width and height and caps */
503 gst_caps_unref (priv->caps);
504 priv->caps = gst_caps_copy (caps);
506 /* check for the configured metadata */
507 priv->add_metavideo =
508 gst_buffer_pool_config_has_option (config,
509 GST_BUFFER_POOL_OPTION_VIDEO_META);
511 /* parse extra alignment info */
512 priv->need_alignment = gst_buffer_pool_config_has_option (config,
513 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
515 if (priv->need_alignment) {
516 gst_buffer_pool_config_get_video_alignment (config, &priv->align);
518 GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
519 priv->align.padding_left, priv->align.padding_left,
520 priv->align.padding_bottom);
522 /* we need the video metadata too now */
523 priv->add_metavideo = TRUE;
525 gst_video_alignment_reset (&priv->align);
528 /* add the padding */
530 GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
531 priv->align.padding_right;
532 priv->padded_height =
533 GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
534 priv->align.padding_bottom;
536 return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
541 GST_WARNING_OBJECT (pool, "invalid config");
546 GST_WARNING_OBJECT (pool, "no caps in config");
551 GST_WARNING_OBJECT (pool,
552 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
557 /* This function handles GstXImageBuffer creation depending on XShm availability */
559 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
560 GstBufferPoolParams * params)
562 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
563 GstXImageBufferPoolPrivate *priv = xpool->priv;
570 ximage = gst_buffer_new ();
571 meta = gst_buffer_add_ximage_meta (ximage, xpool);
573 gst_buffer_unref (ximage);
576 if (priv->add_metavideo) {
579 GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
580 /* these are just the defaults for now */
581 meta = gst_buffer_add_video_meta (ximage, 0, GST_VIDEO_INFO_FORMAT (info),
582 priv->padded_width, priv->padded_height);
584 if (priv->need_alignment) {
585 gint vpad, hpad, pstride;
587 vpad = priv->align.padding_left;
588 hpad = priv->align.padding_top;
590 meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
591 meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
592 pstride = GST_VIDEO_INFO_COMP_PSTRIDE (&priv->info, 0);
594 meta->offset[0] += (vpad * meta->stride[0]) + (hpad * pstride);
604 GST_WARNING_OBJECT (pool, "can't create image");
605 return GST_FLOW_ERROR;
610 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
612 GstXImageBufferPool *pool;
614 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
616 pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
617 pool->sink = gst_object_ref (ximagesink);
619 GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
621 return GST_BUFFER_POOL_CAST (pool);
625 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
627 GObjectClass *gobject_class = (GObjectClass *) klass;
628 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
630 g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
632 gobject_class->finalize = gst_ximage_buffer_pool_finalize;
634 gstbufferpool_class->get_options = ximage_buffer_pool_get_options;
635 gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
636 gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
640 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
642 pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
646 gst_ximage_buffer_pool_finalize (GObject * object)
648 GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
649 GstXImageBufferPoolPrivate *priv = pool->priv;
651 GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
654 gst_caps_unref (priv->caps);
655 gst_object_unref (pool->sink);
657 G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);