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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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 (g_once_init_enter (&ximage_meta_info)) {
72 const GstMetaInfo *meta =
73 gst_meta_register (GST_XIMAGE_META_API_TYPE, "GstXImageMeta",
74 sizeof (GstXImageMeta), (GstMetaInitFunction) NULL,
75 (GstMetaFreeFunction) gst_ximage_meta_free,
76 (GstMetaTransformFunction) NULL);
77 g_once_init_leave (&ximage_meta_info, meta);
79 return ximage_meta_info;
83 static gboolean error_caught = FALSE;
86 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
90 XGetErrorText (display, xevent->error_code, error_msg, 1024);
91 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
96 static GstXImageMeta *
97 gst_buffer_add_ximage_meta (GstBuffer * buffer, GstXImageBufferPool * xpool)
99 GstXImageSink *ximagesink;
100 int (*handler) (Display *, XErrorEvent *);
101 gboolean success = FALSE;
102 GstXContext *xcontext;
104 gint width, height, align = 15, offset;
105 GstXImageBufferPoolPrivate *priv;
108 ximagesink = xpool->sink;
109 xcontext = ximagesink->xcontext;
111 width = priv->padded_width;
112 height = priv->padded_height;
115 (GstXImageMeta *) gst_buffer_add_meta (buffer, GST_XIMAGE_META_INFO,
118 meta->SHMInfo.shmaddr = ((void *) -1);
119 meta->SHMInfo.shmid = -1;
121 meta->x = priv->align.padding_left;
122 meta->y = priv->align.padding_top;
123 meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
124 meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
125 meta->sink = gst_object_ref (ximagesink);
127 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
130 g_mutex_lock (&ximagesink->x_lock);
132 /* Setting an error handler to catch failure */
133 error_caught = FALSE;
134 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
137 if (xcontext->use_xshm) {
138 meta->ximage = XShmCreateImage (xcontext->disp,
140 xcontext->depth, ZPixmap, NULL, &meta->SHMInfo, width, height);
141 if (!meta->ximage || error_caught) {
142 g_mutex_unlock (&ximagesink->x_lock);
144 /* Reset error flag */
145 error_caught = FALSE;
148 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
149 ("Failed to create output image buffer of %dx%d pixels",
151 ("could not XShmCreateImage a %dx%d image", width, height));
153 /* Retry without XShm */
154 ximagesink->xcontext->use_xshm = FALSE;
156 /* Hold X mutex again to try without XShm */
157 g_mutex_lock (&ximagesink->x_lock);
162 /* we have to use the returned bytes_per_line for our shm size */
163 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
164 GST_LOG_OBJECT (ximagesink,
165 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
166 meta->size, width, meta->ximage->bytes_per_line);
168 /* get shared memory */
169 meta->SHMInfo.shmid =
170 shmget (IPC_PRIVATE, meta->size + align, IPC_CREAT | 0777);
171 if (meta->SHMInfo.shmid == -1)
175 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
176 if (meta->SHMInfo.shmaddr == ((void *) -1))
179 /* now we can set up the image data */
180 meta->ximage->data = meta->SHMInfo.shmaddr;
181 meta->SHMInfo.readOnly = FALSE;
183 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
186 XSync (xcontext->disp, FALSE);
188 /* Now that everyone has attached, we can delete the shared memory segment.
189 * This way, it will be deleted as soon as we detach later, and not
190 * leaked if we crash. */
191 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
193 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
194 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
197 #endif /* HAVE_XSHM */
201 meta->ximage = XCreateImage (xcontext->disp,
203 xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
204 if (!meta->ximage || error_caught)
207 /* upstream will assume that rowstrides are multiples of 4, but this
208 * doesn't always seem to be the case with XCreateImage() */
209 if ((meta->ximage->bytes_per_line % 4) != 0) {
210 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
214 /* we have to use the returned bytes_per_line for our image size */
215 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
217 /* alloc a bit more for unexpected strides to avoid crashes upstream.
218 * FIXME: if we get an unrounded stride, the image will be displayed
219 * distorted, since all upstream elements assume a rounded stride */
221 GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
223 meta->ximage->data = g_malloc (allocsize + align);
224 GST_LOG_OBJECT (ximagesink,
225 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
226 "stride %d", meta->size, allocsize, width,
227 meta->ximage->bytes_per_line);
229 XSync (xcontext->disp, FALSE);
232 if ((offset = ((guintptr) meta->ximage->data & align)))
233 offset = (align + 1) - offset;
235 GST_DEBUG_OBJECT (ximagesink, "memory %p, align %d, offset %d",
236 meta->ximage->data, align, offset);
238 /* Reset error handler */
239 error_caught = FALSE;
240 XSetErrorHandler (handler);
242 gst_buffer_append_memory (buffer,
243 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
244 meta->size + align, offset, meta->size, NULL, NULL));
246 g_mutex_unlock (&ximagesink->x_lock);
259 g_mutex_unlock (&ximagesink->x_lock);
260 /* Reset error handler */
261 error_caught = FALSE;
262 XSetErrorHandler (handler);
264 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
265 ("Failed to create output image buffer of %dx%d pixels",
267 ("could not XShmCreateImage a %dx%d image", width, height));
273 g_mutex_unlock (&ximagesink->x_lock);
274 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
275 ("Failed to create output image buffer of %dx%d pixels",
277 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
283 g_mutex_unlock (&ximagesink->x_lock);
284 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
285 ("Failed to create output image buffer of %dx%d pixels",
286 width, height), ("Failed to shmat: %s", g_strerror (errno)));
287 /* Clean up the shared memory segment */
288 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
293 /* Clean up the shared memory segment */
294 shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
295 g_mutex_unlock (&ximagesink->x_lock);
297 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
298 ("Failed to create output image buffer of %dx%d pixels",
299 width, height), ("Failed to XShmAttach"));
306 gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer)
308 GstXImageSink *ximagesink;
310 ximagesink = meta->sink;
312 GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
314 /* Hold the object lock to ensure the XContext doesn't disappear */
315 GST_OBJECT_LOCK (ximagesink);
316 /* We might have some buffers destroyed after changing state to NULL */
317 if (ximagesink->xcontext == NULL) {
318 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
320 /* Need to free the shared memory segment even if the x context
321 * was already cleaned up */
322 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
323 shmdt (meta->SHMInfo.shmaddr);
329 g_mutex_lock (&ximagesink->x_lock);
332 if (ximagesink->xcontext->use_xshm) {
333 if (meta->SHMInfo.shmaddr != ((void *) -1)) {
334 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
335 meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
336 XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
337 XSync (ximagesink->xcontext->disp, FALSE);
338 shmdt (meta->SHMInfo.shmaddr);
339 meta->SHMInfo.shmaddr = (void *) -1;
342 XDestroyImage (meta->ximage);
344 #endif /* HAVE_XSHM */
347 XDestroyImage (meta->ximage);
351 XSync (ximagesink->xcontext->disp, FALSE);
353 g_mutex_unlock (&ximagesink->x_lock);
356 GST_OBJECT_UNLOCK (ximagesink);
358 gst_object_unref (meta->sink);
362 /* This function checks that it is actually really possible to create an image
365 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
366 GstXContext * xcontext)
369 XShmSegmentInfo SHMInfo;
371 int (*handler) (Display *, XErrorEvent *);
372 gboolean result = FALSE;
373 gboolean did_attach = FALSE;
375 g_return_val_if_fail (xcontext != NULL, FALSE);
377 /* Sync to ensure any older errors are already processed */
378 XSync (xcontext->disp, FALSE);
380 /* Set defaults so we don't free these later unnecessarily */
381 SHMInfo.shmaddr = ((void *) -1);
384 /* Setting an error handler to catch failure */
385 error_caught = FALSE;
386 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
388 /* Trying to create a 1x1 ximage */
389 GST_DEBUG ("XShmCreateImage of 1x1");
391 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
392 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
394 /* Might cause an error, sync to ensure it is noticed */
395 XSync (xcontext->disp, FALSE);
396 if (!ximage || error_caught) {
397 GST_WARNING ("could not XShmCreateImage a 1x1 image");
400 size = ximage->height * ximage->bytes_per_line;
402 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
403 if (SHMInfo.shmid == -1) {
404 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
409 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
410 if (SHMInfo.shmaddr == ((void *) -1)) {
411 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
412 /* Clean up the shared memory segment */
413 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
417 ximage->data = SHMInfo.shmaddr;
418 SHMInfo.readOnly = FALSE;
420 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
421 GST_WARNING ("Failed to XShmAttach");
422 /* Clean up the shared memory segment */
423 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
427 /* Sync to ensure we see any errors we caused */
428 XSync (xcontext->disp, FALSE);
430 /* Delete the shared memory segment as soon as everyone is attached.
431 * This way, it will be deleted as soon as we detach later, and not
432 * leaked if we crash. */
433 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
436 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
440 /* store whether we succeeded in result */
443 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
444 "Not using shared memory.");
448 /* Sync to ensure we swallow any errors we caused and reset error_caught */
449 XSync (xcontext->disp, FALSE);
451 error_caught = FALSE;
452 XSetErrorHandler (handler);
455 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
456 SHMInfo.shmid, SHMInfo.shmseg);
457 XShmDetach (xcontext->disp, &SHMInfo);
458 XSync (xcontext->disp, FALSE);
460 if (SHMInfo.shmaddr != ((void *) -1))
461 shmdt (SHMInfo.shmaddr);
463 XDestroyImage (ximage);
466 #endif /* HAVE_XSHM */
469 static void gst_ximage_buffer_pool_finalize (GObject * object);
471 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
472 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
474 #define gst_ximage_buffer_pool_parent_class parent_class
475 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
476 GST_TYPE_BUFFER_POOL);
478 static const gchar **
479 ximage_buffer_pool_get_options (GstBufferPool * pool)
481 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
482 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
489 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
491 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
492 GstXImageBufferPoolPrivate *priv = xpool->priv;
496 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
502 /* now parse the caps from the config */
503 if (!gst_video_info_from_caps (&info, caps))
506 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
509 /* keep track of the width and height and caps */
511 gst_caps_unref (priv->caps);
512 priv->caps = gst_caps_ref (caps);
514 /* check for the configured metadata */
515 priv->add_metavideo =
516 gst_buffer_pool_config_has_option (config,
517 GST_BUFFER_POOL_OPTION_VIDEO_META);
519 /* parse extra alignment info */
520 priv->need_alignment = gst_buffer_pool_config_has_option (config,
521 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
523 if (priv->need_alignment) {
524 gst_buffer_pool_config_get_video_alignment (config, &priv->align);
526 GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
527 priv->align.padding_left, priv->align.padding_left,
528 priv->align.padding_bottom);
530 /* do padding and alignment */
531 gst_video_info_align (&info, &priv->align);
533 /* we need the video metadata too now */
534 priv->add_metavideo = TRUE;
536 gst_video_alignment_reset (&priv->align);
539 /* add the padding */
541 GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
542 priv->align.padding_right;
543 priv->padded_height =
544 GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
545 priv->align.padding_bottom;
549 return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
554 GST_WARNING_OBJECT (pool, "invalid config");
559 GST_WARNING_OBJECT (pool, "no caps in config");
564 GST_WARNING_OBJECT (pool,
565 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
570 /* This function handles GstXImageBuffer creation depending on XShm availability */
572 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
573 GstBufferPoolAcquireParams * params)
575 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
576 GstXImageBufferPoolPrivate *priv = xpool->priv;
583 ximage = gst_buffer_new ();
584 meta = gst_buffer_add_ximage_meta (ximage, xpool);
586 gst_buffer_unref (ximage);
589 if (priv->add_metavideo) {
590 GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
591 /* these are just the defaults for now */
592 gst_buffer_add_video_meta_full (ximage, GST_VIDEO_FRAME_FLAG_NONE,
593 GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
594 GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
595 info->offset, info->stride);
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);