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;
103 gint width, height, align = 15, offset;
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 =
169 shmget (IPC_PRIVATE, meta->size + align, IPC_CREAT | 0777);
170 if (meta->SHMInfo.shmid == -1)
174 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
175 if (meta->SHMInfo.shmaddr == ((void *) -1))
178 /* now we can set up the image data */
179 meta->ximage->data = meta->SHMInfo.shmaddr;
180 meta->SHMInfo.readOnly = FALSE;
182 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
185 XSync (xcontext->disp, FALSE);
187 /* Now that everyone has attached, we can delete the shared memory segment.
188 * This way, it will be deleted as soon as we detach later, and not
189 * leaked if we crash. */
190 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
192 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
193 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
196 #endif /* HAVE_XSHM */
200 meta->ximage = XCreateImage (xcontext->disp,
202 xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
203 if (!meta->ximage || error_caught)
206 /* upstream will assume that rowstrides are multiples of 4, but this
207 * doesn't always seem to be the case with XCreateImage() */
208 if ((meta->ximage->bytes_per_line % 4) != 0) {
209 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
213 /* we have to use the returned bytes_per_line for our image size */
214 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
216 /* alloc a bit more for unexpected strides to avoid crashes upstream.
217 * FIXME: if we get an unrounded stride, the image will be displayed
218 * distorted, since all upstream elements assume a rounded stride */
220 GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
222 meta->ximage->data = g_malloc (allocsize + align);
223 GST_LOG_OBJECT (ximagesink,
224 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
225 "stride %d", meta->size, allocsize, width,
226 meta->ximage->bytes_per_line);
228 XSync (xcontext->disp, FALSE);
231 if ((offset = ((guintptr) meta->ximage->data & align)))
232 offset = (align + 1) - offset;
234 GST_DEBUG_OBJECT (ximagesink, "memory %p, align %d, offset %d",
235 meta->ximage->data, align, offset);
237 /* Reset error handler */
238 error_caught = FALSE;
239 XSetErrorHandler (handler);
241 gst_buffer_append_memory (buffer,
242 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
243 meta->size + align, offset, meta->size, NULL, NULL));
245 g_mutex_unlock (ximagesink->x_lock);
258 g_mutex_unlock (ximagesink->x_lock);
259 /* Reset error handler */
260 error_caught = FALSE;
261 XSetErrorHandler (handler);
263 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
264 ("Failed to create output image buffer of %dx%d pixels",
266 ("could not XShmCreateImage a %dx%d image", width, height));
272 g_mutex_unlock (ximagesink->x_lock);
273 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
274 ("Failed to create output image buffer of %dx%d pixels",
276 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
282 g_mutex_unlock (ximagesink->x_lock);
283 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
284 ("Failed to create output image buffer of %dx%d pixels",
285 width, height), ("Failed to shmat: %s", g_strerror (errno)));
286 /* Clean up the shared memory segment */
287 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
292 /* Clean up the shared memory segment */
293 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
294 g_mutex_unlock (ximagesink->x_lock);
296 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
297 ("Failed to create output image buffer of %dx%d pixels",
298 width, height), ("Failed to XShmAttach"));
305 gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer)
307 GstXImageSink *ximagesink;
309 ximagesink = meta->sink;
311 GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
313 /* Hold the object lock to ensure the XContext doesn't disappear */
314 GST_OBJECT_LOCK (ximagesink);
315 /* We might have some buffers destroyed after changing state to NULL */
316 if (ximagesink->xcontext == NULL) {
317 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
319 /* Need to free the shared memory segment even if the x context
320 * was already cleaned up */
321 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
322 shmdt (meta->SHMInfo.shmaddr);
328 g_mutex_lock (ximagesink->x_lock);
331 if (ximagesink->xcontext->use_xshm) {
332 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
333 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
334 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
335 XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
336 XSync (ximagesink->xcontext->disp, FALSE);
337 shmdt (meta->SHMInfo.shmaddr);
338 meta->SHMInfo.shmaddr = (void *) -1;
341 XDestroyImage (meta->ximage);
343 #endif /* HAVE_XSHM */
346 XDestroyImage (meta->ximage);
350 XSync (ximagesink->xcontext->disp, FALSE);
352 g_mutex_unlock (ximagesink->x_lock);
355 GST_OBJECT_UNLOCK (ximagesink);
357 gst_object_unref (meta->sink);
361 /* This function checks that it is actually really possible to create an image
364 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
365 GstXContext * xcontext)
368 XShmSegmentInfo SHMInfo;
370 int (*handler) (Display *, XErrorEvent *);
371 gboolean result = FALSE;
372 gboolean did_attach = FALSE;
374 g_return_val_if_fail (xcontext != NULL, FALSE);
376 /* Sync to ensure any older errors are already processed */
377 XSync (xcontext->disp, FALSE);
379 /* Set defaults so we don't free these later unnecessarily */
380 SHMInfo.shmaddr = ((void *) -1);
383 /* Setting an error handler to catch failure */
384 error_caught = FALSE;
385 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
387 /* Trying to create a 1x1 ximage */
388 GST_DEBUG ("XShmCreateImage of 1x1");
390 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
391 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
393 /* Might cause an error, sync to ensure it is noticed */
394 XSync (xcontext->disp, FALSE);
395 if (!ximage || error_caught) {
396 GST_WARNING ("could not XShmCreateImage a 1x1 image");
399 size = ximage->height * ximage->bytes_per_line;
401 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
402 if (SHMInfo.shmid == -1) {
403 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
408 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
409 if (SHMInfo.shmaddr == ((void *) -1)) {
410 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
411 /* Clean up the shared memory segment */
412 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
416 ximage->data = SHMInfo.shmaddr;
417 SHMInfo.readOnly = FALSE;
419 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
420 GST_WARNING ("Failed to XShmAttach");
421 /* Clean up the shared memory segment */
422 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
426 /* Sync to ensure we see any errors we caused */
427 XSync (xcontext->disp, FALSE);
429 /* Delete the shared memory segment as soon as everyone is attached.
430 * This way, it will be deleted as soon as we detach later, and not
431 * leaked if we crash. */
432 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
435 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
439 /* store whether we succeeded in result */
442 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
443 "Not using shared memory.");
447 /* Sync to ensure we swallow any errors we caused and reset error_caught */
448 XSync (xcontext->disp, FALSE);
450 error_caught = FALSE;
451 XSetErrorHandler (handler);
454 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
455 SHMInfo.shmid, SHMInfo.shmseg);
456 XShmDetach (xcontext->disp, &SHMInfo);
457 XSync (xcontext->disp, FALSE);
459 if (SHMInfo.shmaddr != ((void *) -1))
460 shmdt (SHMInfo.shmaddr);
462 XDestroyImage (ximage);
465 #endif /* HAVE_XSHM */
468 static void gst_ximage_buffer_pool_finalize (GObject * object);
470 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
471 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
473 #define gst_ximage_buffer_pool_parent_class parent_class
474 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
475 GST_TYPE_BUFFER_POOL);
477 static const gchar **
478 ximage_buffer_pool_get_options (GstBufferPool * pool)
480 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
481 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
488 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
490 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
491 GstXImageBufferPoolPrivate *priv = xpool->priv;
495 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
501 /* now parse the caps from the config */
502 if (!gst_video_info_from_caps (&info, caps))
505 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
508 /* keep track of the width and height and caps */
510 gst_caps_unref (priv->caps);
511 priv->caps = gst_caps_ref (caps);
513 /* check for the configured metadata */
514 priv->add_metavideo =
515 gst_buffer_pool_config_has_option (config,
516 GST_BUFFER_POOL_OPTION_VIDEO_META);
518 /* parse extra alignment info */
519 priv->need_alignment = gst_buffer_pool_config_has_option (config,
520 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
522 if (priv->need_alignment) {
523 gst_buffer_pool_config_get_video_alignment (config, &priv->align);
525 GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
526 priv->align.padding_left, priv->align.padding_left,
527 priv->align.padding_bottom);
529 /* do padding and alignment */
530 gst_video_info_align (&info, &priv->align);
532 /* we need the video metadata too now */
533 priv->add_metavideo = TRUE;
535 gst_video_alignment_reset (&priv->align);
538 /* add the padding */
540 GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
541 priv->align.padding_right;
542 priv->padded_height =
543 GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
544 priv->align.padding_bottom;
548 return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
553 GST_WARNING_OBJECT (pool, "invalid config");
558 GST_WARNING_OBJECT (pool, "no caps in config");
563 GST_WARNING_OBJECT (pool,
564 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
569 /* This function handles GstXImageBuffer creation depending on XShm availability */
571 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
572 GstBufferPoolAcquireParams * params)
574 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
575 GstXImageBufferPoolPrivate *priv = xpool->priv;
582 ximage = gst_buffer_new ();
583 meta = gst_buffer_add_ximage_meta (ximage, xpool);
585 gst_buffer_unref (ximage);
588 if (priv->add_metavideo) {
589 GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
590 /* these are just the defaults for now */
591 gst_buffer_add_video_meta_full (ximage, GST_VIDEO_FRAME_FLAG_NONE,
592 GST_VIDEO_INFO_FORMAT (info), priv->padded_width, priv->padded_height,
593 GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
602 GST_WARNING_OBJECT (pool, "can't create image");
603 return GST_FLOW_ERROR;
608 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
610 GstXImageBufferPool *pool;
612 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
614 pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
615 pool->sink = gst_object_ref (ximagesink);
617 GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
619 return GST_BUFFER_POOL_CAST (pool);
623 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
625 GObjectClass *gobject_class = (GObjectClass *) klass;
626 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
628 g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
630 gobject_class->finalize = gst_ximage_buffer_pool_finalize;
632 gstbufferpool_class->get_options = ximage_buffer_pool_get_options;
633 gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
634 gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
638 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
640 pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
644 gst_ximage_buffer_pool_finalize (GObject * object)
646 GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
647 GstXImageBufferPoolPrivate *priv = pool->priv;
649 GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
652 gst_caps_unref (priv->caps);
653 gst_object_unref (pool->sink);
655 G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);