Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / sys / ximage / ximagepool.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 /* Object header */
25 #include "ximagesink.h"
26
27 /* Debugging category */
28 #include <gst/gstinfo.h>
29
30 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagepool);
31 #define GST_CAT_DEFAULT gst_debug_ximagepool
32
33 static void gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer);
34
35 /* ximage metadata */
36 const GstMetaInfo *
37 gst_meta_ximage_get_info (void)
38 {
39   static const GstMetaInfo *meta_ximage_info = NULL;
40
41   if (meta_ximage_info == NULL) {
42     meta_ximage_info = gst_meta_register ("GstMetaXImage", "GstMetaXImage",
43         sizeof (GstMetaXImage),
44         (GstMetaInitFunction) NULL,
45         (GstMetaFreeFunction) gst_meta_ximage_free,
46         (GstMetaCopyFunction) NULL, (GstMetaTransformFunction) NULL);
47   }
48   return meta_ximage_info;
49 }
50
51 /* X11 stuff */
52 static gboolean error_caught = FALSE;
53
54 static int
55 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
56 {
57   char error_msg[1024];
58
59   XGetErrorText (display, xevent->error_code, error_msg, 1024);
60   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
61   error_caught = TRUE;
62   return 0;
63 }
64
65 GstMetaXImage *
66 gst_buffer_add_meta_ximage (GstBuffer * buffer, GstXImageSink * ximagesink,
67     gint width, gint height)
68 {
69   int (*handler) (Display *, XErrorEvent *);
70   gboolean success = FALSE;
71   GstXContext *xcontext;
72   GstMetaXImage *meta;
73
74   xcontext = ximagesink->xcontext;
75
76   meta =
77       (GstMetaXImage *) gst_buffer_add_meta (buffer, GST_META_INFO_XIMAGE,
78       NULL);
79 #ifdef HAVE_XSHM
80   meta->SHMInfo.shmaddr = ((void *) -1);
81   meta->SHMInfo.shmid = -1;
82 #endif
83   meta->width = width;
84   meta->height = height;
85   meta->sink = gst_object_ref (ximagesink);
86
87   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
88       meta->width, meta->height);
89
90   g_mutex_lock (ximagesink->x_lock);
91
92   /* Setting an error handler to catch failure */
93   error_caught = FALSE;
94   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
95
96 #ifdef HAVE_XSHM
97   if (xcontext->use_xshm) {
98     meta->ximage = XShmCreateImage (xcontext->disp,
99         xcontext->visual,
100         xcontext->depth,
101         ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
102     if (!meta->ximage || error_caught) {
103       g_mutex_unlock (ximagesink->x_lock);
104
105       /* Reset error flag */
106       error_caught = FALSE;
107
108       /* Push a warning */
109       GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
110           ("Failed to create output image buffer of %dx%d pixels",
111               meta->width, meta->height),
112           ("could not XShmCreateImage a %dx%d image",
113               meta->width, meta->height));
114
115       /* Retry without XShm */
116       ximagesink->xcontext->use_xshm = FALSE;
117
118       /* Hold X mutex again to try without XShm */
119       g_mutex_lock (ximagesink->x_lock);
120
121       goto no_xshm;
122     }
123
124     /* we have to use the returned bytes_per_line for our shm size */
125     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
126     GST_LOG_OBJECT (ximagesink,
127         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
128         meta->size, meta->width, meta->ximage->bytes_per_line);
129
130     /* get shared memory */
131     meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
132     if (meta->SHMInfo.shmid == -1)
133       goto shmget_failed;
134
135     /* attach */
136     meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
137     if (meta->SHMInfo.shmaddr == ((void *) -1))
138       goto shmat_failed;
139
140     /* now we can set up the image data */
141     meta->ximage->data = meta->SHMInfo.shmaddr;
142     meta->SHMInfo.readOnly = FALSE;
143
144     if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
145       goto xattach_failed;
146
147     XSync (xcontext->disp, FALSE);
148
149     /* Now that everyone has attached, we can delete the shared memory segment.
150      * This way, it will be deleted as soon as we detach later, and not
151      * leaked if we crash. */
152     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
153
154     GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
155         meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
156   } else
157   no_xshm:
158 #endif /* HAVE_XSHM */
159   {
160     guint allocsize;
161
162     meta->ximage = XCreateImage (xcontext->disp,
163         xcontext->visual,
164         xcontext->depth,
165         ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
166     if (!meta->ximage || error_caught)
167       goto create_failed;
168
169     /* upstream will assume that rowstrides are multiples of 4, but this
170      * doesn't always seem to be the case with XCreateImage() */
171     if ((meta->ximage->bytes_per_line % 4) != 0) {
172       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
173           "usually assumed");
174     }
175
176     /* we have to use the returned bytes_per_line for our image size */
177     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
178
179     /* alloc a bit more for unexpected strides to avoid crashes upstream.
180      * FIXME: if we get an unrounded stride, the image will be displayed
181      * distorted, since all upstream elements assume a rounded stride */
182     allocsize =
183         GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
184
185     meta->ximage->data = g_malloc (allocsize);
186     GST_LOG_OBJECT (ximagesink,
187         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
188         "stride %d", meta->size, allocsize, meta->width,
189         meta->ximage->bytes_per_line);
190
191     XSync (xcontext->disp, FALSE);
192   }
193
194   /* Reset error handler */
195   error_caught = FALSE;
196   XSetErrorHandler (handler);
197
198   gst_buffer_take_memory (buffer,
199       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
200           NULL, meta->size, 0, meta->size));
201
202   g_mutex_unlock (ximagesink->x_lock);
203
204   success = TRUE;
205
206 beach:
207   if (!success)
208     meta = NULL;
209
210   return meta;
211
212   /* ERRORS */
213 create_failed:
214   {
215     g_mutex_unlock (ximagesink->x_lock);
216     /* Reset error handler */
217     error_caught = FALSE;
218     XSetErrorHandler (handler);
219     /* Push an error */
220     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
221         ("Failed to create output image buffer of %dx%d pixels",
222             meta->width, meta->height),
223         ("could not XShmCreateImage a %dx%d image", meta->width, meta->height));
224     goto beach;
225   }
226 shmget_failed:
227   {
228     g_mutex_unlock (ximagesink->x_lock);
229     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
230         ("Failed to create output image buffer of %dx%d pixels",
231             meta->width, meta->height),
232         ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
233             meta->size));
234     goto beach;
235   }
236 shmat_failed:
237   {
238     g_mutex_unlock (ximagesink->x_lock);
239     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
240         ("Failed to create output image buffer of %dx%d pixels",
241             meta->width, meta->height),
242         ("Failed to shmat: %s", g_strerror (errno)));
243     /* Clean up the shared memory segment */
244     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
245     goto beach;
246   }
247 xattach_failed:
248   {
249     /* Clean up the shared memory segment */
250     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
251     g_mutex_unlock (ximagesink->x_lock);
252
253     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
254         ("Failed to create output image buffer of %dx%d pixels",
255             meta->width, meta->height), ("Failed to XShmAttach"));
256     goto beach;
257   }
258 }
259
260 static void
261 gst_meta_ximage_free (GstMetaXImage * meta, GstBuffer * buffer)
262 {
263   GstXImageSink *ximagesink;
264
265   ximagesink = meta->sink;
266
267   GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
268
269   /* Hold the object lock to ensure the XContext doesn't disappear */
270   GST_OBJECT_LOCK (ximagesink);
271   /* We might have some buffers destroyed after changing state to NULL */
272   if (ximagesink->xcontext == NULL) {
273     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
274 #ifdef HAVE_XSHM
275     /* Need to free the shared memory segment even if the x context
276      * was already cleaned up */
277     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
278       shmdt (meta->SHMInfo.shmaddr);
279     }
280 #endif
281     goto beach;
282   }
283
284   g_mutex_lock (ximagesink->x_lock);
285
286 #ifdef HAVE_XSHM
287   if (ximagesink->xcontext->use_xshm) {
288     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
289       GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
290           meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
291       XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
292       XSync (ximagesink->xcontext->disp, FALSE);
293       shmdt (meta->SHMInfo.shmaddr);
294       meta->SHMInfo.shmaddr = (void *) -1;
295     }
296     if (meta->ximage)
297       XDestroyImage (meta->ximage);
298   } else
299 #endif /* HAVE_XSHM */
300   {
301     if (meta->ximage) {
302       XDestroyImage (meta->ximage);
303     }
304   }
305
306   XSync (ximagesink->xcontext->disp, FALSE);
307
308   g_mutex_unlock (ximagesink->x_lock);
309
310 beach:
311   GST_OBJECT_UNLOCK (ximagesink);
312
313   gst_object_unref (meta->sink);
314 }
315
316 GstBuffer *
317 gst_ximage_buffer_new (GstXImageSink * ximagesink, gint width, gint height)
318 {
319   GstBuffer *buffer;
320   GstMetaXImage *meta;
321
322   buffer = gst_buffer_new ();
323   meta = gst_buffer_add_meta_ximage (buffer, ximagesink, width, height);
324   if (meta == NULL) {
325     gst_buffer_unref (buffer);
326     buffer = NULL;
327   }
328   return buffer;
329 }
330
331 #ifdef HAVE_XSHM
332 /* This function checks that it is actually really possible to create an image
333    using XShm */
334 gboolean
335 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
336     GstXContext * xcontext)
337 {
338   XImage *ximage;
339   XShmSegmentInfo SHMInfo;
340   size_t size;
341   int (*handler) (Display *, XErrorEvent *);
342   gboolean result = FALSE;
343   gboolean did_attach = FALSE;
344
345   g_return_val_if_fail (xcontext != NULL, FALSE);
346
347   /* Sync to ensure any older errors are already processed */
348   XSync (xcontext->disp, FALSE);
349
350   /* Set defaults so we don't free these later unnecessarily */
351   SHMInfo.shmaddr = ((void *) -1);
352   SHMInfo.shmid = -1;
353
354   /* Setting an error handler to catch failure */
355   error_caught = FALSE;
356   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
357
358   /* Trying to create a 1x1 ximage */
359   GST_DEBUG ("XShmCreateImage of 1x1");
360
361   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
362       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
363
364   /* Might cause an error, sync to ensure it is noticed */
365   XSync (xcontext->disp, FALSE);
366   if (!ximage || error_caught) {
367     GST_WARNING ("could not XShmCreateImage a 1x1 image");
368     goto beach;
369   }
370   size = ximage->height * ximage->bytes_per_line;
371
372   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
373   if (SHMInfo.shmid == -1) {
374     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
375         size);
376     goto beach;
377   }
378
379   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
380   if (SHMInfo.shmaddr == ((void *) -1)) {
381     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
382     /* Clean up the shared memory segment */
383     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
384     goto beach;
385   }
386
387   ximage->data = SHMInfo.shmaddr;
388   SHMInfo.readOnly = FALSE;
389
390   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
391     GST_WARNING ("Failed to XShmAttach");
392     /* Clean up the shared memory segment */
393     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
394     goto beach;
395   }
396
397   /* Sync to ensure we see any errors we caused */
398   XSync (xcontext->disp, FALSE);
399
400   /* Delete the shared memory segment as soon as everyone is attached.
401    * This way, it will be deleted as soon as we detach later, and not
402    * leaked if we crash. */
403   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
404
405   if (!error_caught) {
406     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
407         SHMInfo.shmseg);
408
409     did_attach = TRUE;
410     /* store whether we succeeded in result */
411     result = TRUE;
412   } else {
413     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
414         "Not using shared memory.");
415   }
416
417 beach:
418   /* Sync to ensure we swallow any errors we caused and reset error_caught */
419   XSync (xcontext->disp, FALSE);
420
421   error_caught = FALSE;
422   XSetErrorHandler (handler);
423
424   if (did_attach) {
425     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
426         SHMInfo.shmid, SHMInfo.shmseg);
427     XShmDetach (xcontext->disp, &SHMInfo);
428     XSync (xcontext->disp, FALSE);
429   }
430   if (SHMInfo.shmaddr != ((void *) -1))
431     shmdt (SHMInfo.shmaddr);
432   if (ximage)
433     XDestroyImage (ximage);
434   return result;
435 }
436 #endif /* HAVE_XSHM */
437
438 /* bufferpool */
439 static void gst_ximage_buffer_pool_finalize (GObject * object);
440
441 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj)  \
442    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
443
444 struct _GstXImageBufferPoolPrivate
445 {
446   GstCaps *caps;
447   gint width, height;
448 };
449
450 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
451     GST_TYPE_BUFFER_POOL);
452
453 static gboolean
454 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
455 {
456   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
457   GstXImageBufferPoolPrivate *priv = xpool->priv;
458   GstStructure *structure;
459   gint width, height;
460   const GstCaps *caps;
461
462   if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL,
463           NULL, NULL))
464     goto wrong_config;
465
466   if (caps == NULL)
467     goto no_caps;
468
469   /* now parse the caps from the config */
470   structure = gst_caps_get_structure (caps, 0);
471
472   if (!gst_structure_get_int (structure, "width", &width) ||
473       !gst_structure_get_int (structure, "height", &height))
474     goto wrong_caps;
475
476   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, width, height, caps);
477
478   /* keep track of the width and height and caps */
479   if (priv->caps)
480     gst_caps_unref (priv->caps);
481   priv->caps = gst_caps_copy (caps);
482   priv->width = width;
483   priv->height = height;
484
485   return TRUE;
486
487   /* ERRORS */
488 wrong_config:
489   {
490     GST_WARNING_OBJECT (pool, "invalid config");
491     return FALSE;
492   }
493 no_caps:
494   {
495     GST_WARNING_OBJECT (pool, "no caps in config");
496     return FALSE;
497   }
498 wrong_caps:
499   {
500     GST_WARNING_OBJECT (pool,
501         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
502     return FALSE;
503   }
504 }
505
506 /* This function handles GstXImageBuffer creation depending on XShm availability */
507 static GstFlowReturn
508 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
509     GstBufferPoolParams * params)
510 {
511   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
512   GstXImageBufferPoolPrivate *priv = xpool->priv;
513   GstBuffer *ximage;
514
515   ximage = gst_ximage_buffer_new (xpool->sink, priv->width, priv->height);
516   if (ximage == NULL)
517     goto no_buffer;
518
519   *buffer = ximage;
520
521   return GST_FLOW_OK;
522
523   /* ERROR */
524 no_buffer:
525   {
526     GST_WARNING_OBJECT (pool, "can't create image");
527     return GST_FLOW_ERROR;
528   }
529 }
530
531 static void
532 ximage_buffer_pool_free (GstBufferPool * pool, GstBuffer * buffer)
533 {
534   gst_buffer_unref (buffer);
535 }
536
537 GstBufferPool *
538 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
539 {
540   GstXImageBufferPool *pool;
541
542   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
543
544   pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
545   pool->sink = gst_object_ref (ximagesink);
546
547   GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
548
549   return GST_BUFFER_POOL_CAST (pool);
550 }
551
552 static void
553 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
554 {
555   GObjectClass *gobject_class = (GObjectClass *) klass;
556   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
557
558   g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
559
560   gobject_class->finalize = gst_ximage_buffer_pool_finalize;
561
562   gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
563   gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
564   gstbufferpool_class->free_buffer = ximage_buffer_pool_free;
565 }
566
567 static void
568 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
569 {
570   pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
571 }
572
573 static void
574 gst_ximage_buffer_pool_finalize (GObject * object)
575 {
576   GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
577   GstXImageBufferPoolPrivate *priv = pool->priv;
578
579   GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
580
581   if (priv->caps)
582     gst_caps_unref (priv->caps);
583   gst_object_unref (pool->sink);
584
585   G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);
586 }