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;
50 static gboolean error_caught = FALSE;
53 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
57 XGetErrorText (display, xevent->error_code, error_msg, 1024);
58 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
64 gst_ximage_memory_alloc (GstAllocator * allocator, gsize size,
65 GstAllocationParams * params)
71 gst_ximage_memory_free (GstAllocator * allocator, GstMemory * gmem)
73 GstXImageMemory *mem = (GstXImageMemory *) gmem;
74 GstXImageSink *ximagesink;
79 ximagesink = mem->sink;
81 GST_DEBUG_OBJECT (ximagesink, "free memory %p", mem);
83 /* Hold the object lock to ensure the XContext doesn't disappear */
84 GST_OBJECT_LOCK (ximagesink);
85 /* We might have some buffers destroyed after changing state to NULL */
86 if (ximagesink->xcontext == NULL) {
87 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
89 /* Need to free the shared memory segment even if the x context
90 * was already cleaned up */
91 if (mem->SHMInfo.shmaddr != ((void *) -1)) {
92 shmdt (mem->SHMInfo.shmaddr);
98 g_mutex_lock (&ximagesink->x_lock);
101 if (ximagesink->xcontext->use_xshm) {
102 if (mem->SHMInfo.shmaddr != ((void *) -1)) {
103 GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
104 mem->SHMInfo.shmid, mem->SHMInfo.shmseg);
105 XShmDetach (ximagesink->xcontext->disp, &mem->SHMInfo);
106 XSync (ximagesink->xcontext->disp, FALSE);
107 shmdt (mem->SHMInfo.shmaddr);
108 mem->SHMInfo.shmaddr = (void *) -1;
111 XDestroyImage (mem->ximage);
113 #endif /* HAVE_XSHM */
116 XDestroyImage (mem->ximage);
120 XSync (ximagesink->xcontext->disp, FALSE);
122 g_mutex_unlock (&ximagesink->x_lock);
125 GST_OBJECT_UNLOCK (ximagesink);
127 gst_object_unref (mem->sink);
130 g_slice_free (GstXImageMemory, mem);
134 ximage_memory_map (GstXImageMemory * mem, gsize maxsize, GstMapFlags flags)
136 return mem->ximage->data + mem->parent.offset;
140 ximage_memory_unmap (GstXImageMemory * mem)
145 static GstXImageMemory *
146 ximage_memory_share (GstXImageMemory * mem, gssize offset, gsize size)
148 GstXImageMemory *sub;
151 /* We can only share the complete memory */
154 if (size != -1 && size != mem->size)
157 /* find the real parent */
158 if ((parent = mem->parent.parent) == NULL)
159 parent = (GstMemory *) mem;
162 size = mem->parent.size - offset;
164 /* the shared memory is always readonly */
165 sub = g_slice_new (GstXImageMemory);
167 gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) |
168 GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->parent.allocator,
169 &mem->parent, mem->parent.maxsize, mem->parent.align,
170 mem->parent.offset + offset, size);
171 sub->sink = mem->sink;
172 sub->ximage = mem->ximage;
173 sub->SHMInfo = mem->SHMInfo;
176 sub->width = mem->width;
177 sub->height = mem->height;
182 typedef GstAllocator GstXImageMemoryAllocator;
183 typedef GstAllocatorClass GstXImageMemoryAllocatorClass;
185 GType ximage_memory_allocator_get_type (void);
186 G_DEFINE_TYPE (GstXImageMemoryAllocator, ximage_memory_allocator,
189 #define GST_XIMAGE_ALLOCATOR_NAME "ximage"
190 #define GST_TYPE_XIMAGE_MEMORY_ALLOCATOR (ximage_memory_allocator_get_type())
191 #define GST_IS_XIMAGE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_MEMORY_ALLOCATOR))
194 ximage_memory_allocator_class_init (GstXImageMemoryAllocatorClass * klass)
196 GstAllocatorClass *allocator_class;
198 allocator_class = (GstAllocatorClass *) klass;
200 allocator_class->alloc = gst_ximage_memory_alloc;
201 allocator_class->free = gst_ximage_memory_free;
205 ximage_memory_allocator_init (GstXImageMemoryAllocator * allocator)
207 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
209 alloc->mem_type = GST_XIMAGE_ALLOCATOR_NAME;
210 alloc->mem_map = (GstMemoryMapFunction) ximage_memory_map;
211 alloc->mem_unmap = (GstMemoryUnmapFunction) ximage_memory_unmap;
212 alloc->mem_share = (GstMemoryShareFunction) ximage_memory_share;
213 /* fallback copy and is_span */
215 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
219 ximage_memory_alloc (GstXImageBufferPool * xpool)
221 GstXImageSink *ximagesink;
222 int (*handler) (Display *, XErrorEvent *);
223 gboolean success = FALSE;
224 GstXContext *xcontext;
225 gint width, height, align = 15, offset;
226 GstXImageBufferPoolPrivate *priv;
227 GstXImageMemory *mem;
230 ximagesink = xpool->sink;
231 xcontext = ximagesink->xcontext;
233 width = priv->padded_width;
234 height = priv->padded_height;
236 mem = g_slice_new (GstXImageMemory);
239 mem->SHMInfo.shmaddr = ((void *) -1);
240 mem->SHMInfo.shmid = -1;
242 mem->x = priv->align.padding_left;
243 mem->y = priv->align.padding_top;
244 mem->width = GST_VIDEO_INFO_WIDTH (&priv->info);
245 mem->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
246 mem->sink = gst_object_ref (ximagesink);
248 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", mem,
251 g_mutex_lock (&ximagesink->x_lock);
253 /* Setting an error handler to catch failure */
254 error_caught = FALSE;
255 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
258 if (xcontext->use_xshm) {
259 mem->ximage = XShmCreateImage (xcontext->disp,
261 xcontext->depth, ZPixmap, NULL, &mem->SHMInfo, width, height);
262 if (!mem->ximage || error_caught) {
263 g_mutex_unlock (&ximagesink->x_lock);
265 /* Reset error flag */
266 error_caught = FALSE;
269 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
270 ("Failed to create output image buffer of %dx%d pixels",
272 ("could not XShmCreateImage a %dx%d image", width, height));
274 /* Retry without XShm */
275 ximagesink->xcontext->use_xshm = FALSE;
277 /* Hold X mutex again to try without XShm */
278 g_mutex_lock (&ximagesink->x_lock);
283 /* we have to use the returned bytes_per_line for our shm size */
284 mem->size = mem->ximage->bytes_per_line * mem->ximage->height;
285 GST_LOG_OBJECT (ximagesink,
286 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
287 mem->size, width, mem->ximage->bytes_per_line);
289 /* get shared memory */
291 shmget (IPC_PRIVATE, mem->size + align, IPC_CREAT | 0777);
292 if (mem->SHMInfo.shmid == -1)
296 mem->SHMInfo.shmaddr = shmat (mem->SHMInfo.shmid, NULL, 0);
297 if (mem->SHMInfo.shmaddr == ((void *) -1))
300 /* now we can set up the image data */
301 mem->ximage->data = mem->SHMInfo.shmaddr;
302 mem->SHMInfo.readOnly = FALSE;
304 if (XShmAttach (xcontext->disp, &mem->SHMInfo) == 0)
307 XSync (xcontext->disp, FALSE);
309 /* Now that everyone has attached, we can delete the shared memory segment.
310 * This way, it will be deleted as soon as we detach later, and not
311 * leaked if we crash. */
312 shmctl (mem->SHMInfo.shmid, IPC_RMID, NULL);
314 GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
315 mem->SHMInfo.shmid, mem->SHMInfo.shmseg);
318 #endif /* HAVE_XSHM */
322 mem->ximage = XCreateImage (xcontext->disp,
324 xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
325 if (!mem->ximage || error_caught)
328 /* upstream will assume that rowstrides are multiples of 4, but this
329 * doesn't always seem to be the case with XCreateImage() */
330 if ((mem->ximage->bytes_per_line % 4) != 0) {
331 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
335 /* we have to use the returned bytes_per_line for our image size */
336 mem->size = mem->ximage->bytes_per_line * mem->ximage->height;
338 /* alloc a bit more for unexpected strides to avoid crashes upstream.
339 * FIXME: if we get an unrounded stride, the image will be displayed
340 * distorted, since all upstream elements assume a rounded stride */
342 GST_ROUND_UP_4 (mem->ximage->bytes_per_line) * mem->ximage->height;
344 mem->ximage->data = g_malloc (allocsize + align);
345 GST_LOG_OBJECT (ximagesink,
346 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
347 "stride %d", mem->size, allocsize, width, mem->ximage->bytes_per_line);
349 XSync (xcontext->disp, FALSE);
352 if ((offset = ((guintptr) mem->ximage->data & align)))
353 offset = (align + 1) - offset;
355 GST_DEBUG_OBJECT (ximagesink, "memory %p, align %d, offset %d",
356 mem->ximage->data, align, offset);
358 /* Reset error handler */
359 error_caught = FALSE;
360 XSetErrorHandler (handler);
362 gst_memory_init (GST_MEMORY_CAST (mem), 0, xpool->allocator,
363 NULL, mem->size + align, align, offset, mem->size);
365 g_mutex_unlock (&ximagesink->x_lock);
371 g_slice_free (GstXImageMemory, mem);
375 return GST_MEMORY_CAST (mem);
380 g_mutex_unlock (&ximagesink->x_lock);
381 /* Reset error handler */
382 error_caught = FALSE;
383 XSetErrorHandler (handler);
385 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
386 ("Failed to create output image buffer of %dx%d pixels",
388 ("could not XShmCreateImage a %dx%d image", width, height));
394 g_mutex_unlock (&ximagesink->x_lock);
395 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
396 ("Failed to create output image buffer of %dx%d pixels",
398 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
404 g_mutex_unlock (&ximagesink->x_lock);
405 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
406 ("Failed to create output image buffer of %dx%d pixels",
407 width, height), ("Failed to shmat: %s", g_strerror (errno)));
408 /* Clean up the shared memory segment */
409 shmctl (mem->SHMInfo.shmid, IPC_RMID, NULL);
414 /* Clean up the shared memory segment */
415 shmctl (mem->SHMInfo.shmid, IPC_RMID, NULL);
416 g_mutex_unlock (&ximagesink->x_lock);
418 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
419 ("Failed to create output image buffer of %dx%d pixels",
420 width, height), ("Failed to XShmAttach"));
427 /* This function checks that it is actually really possible to create an image
430 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
431 GstXContext * xcontext)
434 XShmSegmentInfo SHMInfo;
436 int (*handler) (Display *, XErrorEvent *);
437 gboolean result = FALSE;
438 gboolean did_attach = FALSE;
440 g_return_val_if_fail (xcontext != NULL, FALSE);
442 /* Sync to ensure any older errors are already processed */
443 XSync (xcontext->disp, FALSE);
445 /* Set defaults so we don't free these later unnecessarily */
446 SHMInfo.shmaddr = ((void *) -1);
449 /* Setting an error handler to catch failure */
450 error_caught = FALSE;
451 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
453 /* Trying to create a 1x1 ximage */
454 GST_DEBUG ("XShmCreateImage of 1x1");
456 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
457 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
459 /* Might cause an error, sync to ensure it is noticed */
460 XSync (xcontext->disp, FALSE);
461 if (!ximage || error_caught) {
462 GST_WARNING ("could not XShmCreateImage a 1x1 image");
465 size = ximage->height * ximage->bytes_per_line;
467 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
468 if (SHMInfo.shmid == -1) {
469 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
474 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
475 if (SHMInfo.shmaddr == ((void *) -1)) {
476 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
477 /* Clean up the shared memory segment */
478 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
482 ximage->data = SHMInfo.shmaddr;
483 SHMInfo.readOnly = FALSE;
485 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
486 GST_WARNING ("Failed to XShmAttach");
487 /* Clean up the shared memory segment */
488 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
492 /* Sync to ensure we see any errors we caused */
493 XSync (xcontext->disp, FALSE);
495 /* Delete the shared memory segment as soon as everyone is attached.
496 * This way, it will be deleted as soon as we detach later, and not
497 * leaked if we crash. */
498 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
501 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
505 /* store whether we succeeded in result */
508 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
509 "Not using shared memory.");
513 /* Sync to ensure we swallow any errors we caused and reset error_caught */
514 XSync (xcontext->disp, FALSE);
516 error_caught = FALSE;
517 XSetErrorHandler (handler);
520 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
521 SHMInfo.shmid, SHMInfo.shmseg);
522 XShmDetach (xcontext->disp, &SHMInfo);
523 XSync (xcontext->disp, FALSE);
525 if (SHMInfo.shmaddr != ((void *) -1))
526 shmdt (SHMInfo.shmaddr);
528 XDestroyImage (ximage);
531 #endif /* HAVE_XSHM */
534 static void gst_ximage_buffer_pool_finalize (GObject * object);
536 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj) \
537 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
539 #define gst_ximage_buffer_pool_parent_class parent_class
540 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
541 GST_TYPE_BUFFER_POOL);
543 static const gchar **
544 ximage_buffer_pool_get_options (GstBufferPool * pool)
546 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
547 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
554 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
556 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
557 GstXImageBufferPoolPrivate *priv = xpool->priv;
561 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
567 /* now parse the caps from the config */
568 if (!gst_video_info_from_caps (&info, caps))
571 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
574 /* keep track of the width and height and caps */
576 gst_caps_unref (priv->caps);
577 priv->caps = gst_caps_ref (caps);
579 /* check for the configured metadata */
580 priv->add_metavideo =
581 gst_buffer_pool_config_has_option (config,
582 GST_BUFFER_POOL_OPTION_VIDEO_META);
584 /* parse extra alignment info */
585 priv->need_alignment = gst_buffer_pool_config_has_option (config,
586 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
588 if (priv->need_alignment) {
589 gst_buffer_pool_config_get_video_alignment (config, &priv->align);
591 GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
592 priv->align.padding_left, priv->align.padding_left,
593 priv->align.padding_bottom);
595 /* do padding and alignment */
596 gst_video_info_align (&info, &priv->align);
598 /* we need the video metadata too now */
599 priv->add_metavideo = TRUE;
601 gst_video_alignment_reset (&priv->align);
604 /* add the padding */
606 GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
607 priv->align.padding_right;
608 priv->padded_height =
609 GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
610 priv->align.padding_bottom;
614 return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
619 GST_WARNING_OBJECT (pool, "invalid config");
624 GST_WARNING_OBJECT (pool, "no caps in config");
629 GST_WARNING_OBJECT (pool,
630 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
635 /* This function handles GstXImageBuffer creation depending on XShm availability */
637 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
638 GstBufferPoolAcquireParams * params)
640 GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
641 GstXImageBufferPoolPrivate *priv = xpool->priv;
648 ximage = gst_buffer_new ();
649 mem = ximage_memory_alloc (xpool);
651 gst_buffer_unref (ximage);
654 gst_buffer_append_memory (ximage, mem);
656 if (priv->add_metavideo) {
657 GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
658 /* these are just the defaults for now */
659 gst_buffer_add_video_meta_full (ximage, GST_VIDEO_FRAME_FLAG_NONE,
660 GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
661 GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
662 info->offset, info->stride);
671 GST_WARNING_OBJECT (pool, "can't create image");
672 return GST_FLOW_ERROR;
677 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
679 GstXImageBufferPool *pool;
681 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
683 pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
684 pool->sink = gst_object_ref (ximagesink);
685 pool->allocator = g_object_new (GST_TYPE_XIMAGE_MEMORY_ALLOCATOR, NULL);
687 GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
689 return GST_BUFFER_POOL_CAST (pool);
693 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
695 GObjectClass *gobject_class = (GObjectClass *) klass;
696 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
698 g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
700 gobject_class->finalize = gst_ximage_buffer_pool_finalize;
702 gstbufferpool_class->get_options = ximage_buffer_pool_get_options;
703 gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
704 gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
708 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
710 pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
714 gst_ximage_buffer_pool_finalize (GObject * object)
716 GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
717 GstXImageBufferPoolPrivate *priv = pool->priv;
719 GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
722 gst_caps_unref (priv->caps);
723 gst_object_unref (pool->sink);
724 gst_object_unref (pool->allocator);
726 G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);