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