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