Merge remote-tracking branch 'origin/master' into 0.11
[platform/upstream/gst-plugins-good.git] / sys / ximage / ximageutil.c
1 /* GStreamer
2  * Copyright (C) <2005> Luca Ognibene <luogni@tin.it>
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 #include "ximageutil.h"
25
26 const GstMetaInfo *
27 gst_meta_ximage_get_info (void)
28 {
29   static const GstMetaInfo *meta_ximage_info = NULL;
30
31   if (meta_ximage_info == NULL) {
32     meta_ximage_info =
33         gst_meta_register ("GstMetaXImageSrc", "GstMetaXImageSrc",
34         sizeof (GstMetaXImage), (GstMetaInitFunction) NULL,
35         (GstMetaFreeFunction) NULL, (GstMetaTransformFunction) NULL);
36   }
37   return meta_ximage_info;
38 }
39
40 #ifdef HAVE_XSHM
41 static gboolean error_caught = FALSE;
42
43 static int
44 ximageutil_handle_xerror (Display * display, XErrorEvent * xevent)
45 {
46   char error_msg[1024];
47
48   XGetErrorText (display, xevent->error_code, error_msg, 1024);
49   GST_DEBUG ("ximageutil failed to use XShm calls. error: %s", error_msg);
50   error_caught = TRUE;
51   return 0;
52 }
53
54 /* This function checks that it is actually really possible to create an image
55    using XShm */
56 gboolean
57 ximageutil_check_xshm_calls (GstXContext * xcontext)
58 {
59   XImage *ximage;
60   XShmSegmentInfo SHMInfo;
61   size_t size;
62   int (*handler) (Display *, XErrorEvent *);
63   gboolean result = FALSE;
64   gboolean did_attach = FALSE;
65
66   g_return_val_if_fail (xcontext != NULL, FALSE);
67
68   /* Sync to ensure any older errors are already processed */
69   XSync (xcontext->disp, FALSE);
70
71   /* Set defaults so we don't free these later unnecessarily */
72   SHMInfo.shmaddr = ((void *) -1);
73   SHMInfo.shmid = -1;
74
75   /* Setting an error handler to catch failure */
76   error_caught = FALSE;
77   handler = XSetErrorHandler (ximageutil_handle_xerror);
78
79   /* Trying to create a 1x1 ximage */
80   GST_DEBUG ("XShmCreateImage of 1x1");
81
82   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
83       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
84
85   /* Might cause an error, sync to ensure it is noticed */
86   XSync (xcontext->disp, FALSE);
87   if (!ximage || error_caught) {
88     GST_WARNING ("could not XShmCreateImage a 1x1 image");
89     goto beach;
90   }
91   size = ximage->height * ximage->bytes_per_line;
92
93   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
94   if (SHMInfo.shmid == -1) {
95     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
96         size);
97     goto beach;
98   }
99
100   SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
101   if (SHMInfo.shmaddr == ((void *) -1)) {
102     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
103     goto beach;
104   }
105
106   /* Delete the SHM segment. It will actually go away automatically
107    * when we detach now */
108   shmctl (SHMInfo.shmid, IPC_RMID, 0);
109
110   ximage->data = SHMInfo.shmaddr;
111   SHMInfo.readOnly = FALSE;
112
113   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
114     GST_WARNING ("Failed to XShmAttach");
115     goto beach;
116   }
117
118   /* Sync to ensure we see any errors we caused */
119   XSync (xcontext->disp, FALSE);
120
121   if (!error_caught) {
122     did_attach = TRUE;
123     /* store whether we succeeded in result */
124     result = TRUE;
125   }
126 beach:
127   /* Sync to ensure we swallow any errors we caused and reset error_caught */
128   XSync (xcontext->disp, FALSE);
129   error_caught = FALSE;
130   XSetErrorHandler (handler);
131
132   if (did_attach) {
133     XShmDetach (xcontext->disp, &SHMInfo);
134     XSync (xcontext->disp, FALSE);
135   }
136   if (SHMInfo.shmaddr != ((void *) -1))
137     shmdt (SHMInfo.shmaddr);
138   if (ximage)
139     XDestroyImage (ximage);
140   return result;
141 }
142 #endif /* HAVE_XSHM */
143
144 /* This function gets the X Display and global info about it. Everything is
145    stored in our object and will be cleaned when the object is disposed. Note
146    here that caps for supported format are generated without any window or
147    image creation */
148 GstXContext *
149 ximageutil_xcontext_get (GstElement * parent, const gchar * display_name)
150 {
151   GstXContext *xcontext = NULL;
152   XPixmapFormatValues *px_formats = NULL;
153   gint nb_formats = 0, i;
154
155   xcontext = g_new0 (GstXContext, 1);
156
157   xcontext->disp = XOpenDisplay (display_name);
158   GST_DEBUG_OBJECT (parent, "opened display %p", xcontext->disp);
159   if (!xcontext->disp) {
160     g_free (xcontext);
161     return NULL;
162   }
163   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
164   xcontext->screen_num = DefaultScreen (xcontext->disp);
165   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
166   xcontext->root = DefaultRootWindow (xcontext->disp);
167   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
168   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
169   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
170
171   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
172   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
173
174   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
175   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
176
177   xcontext->caps = NULL;
178
179   GST_DEBUG_OBJECT (parent, "X reports %dx%d pixels and %d mm x %d mm",
180       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
181   ximageutil_calculate_pixel_aspect_ratio (xcontext);
182
183   /* We get supported pixmap formats at supported depth */
184   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
185
186   if (!px_formats) {
187     XCloseDisplay (xcontext->disp);
188     g_free (xcontext);
189     return NULL;
190   }
191
192   /* We get bpp value corresponding to our running depth */
193   for (i = 0; i < nb_formats; i++) {
194     if (px_formats[i].depth == xcontext->depth)
195       xcontext->bpp = px_formats[i].bits_per_pixel;
196   }
197
198   XFree (px_formats);
199
200   xcontext->endianness =
201       (ImageByteOrder (xcontext->disp) ==
202       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
203
204 #ifdef HAVE_XSHM
205   /* Search for XShm extension support */
206   if (XShmQueryExtension (xcontext->disp) &&
207       ximageutil_check_xshm_calls (xcontext)) {
208     xcontext->use_xshm = TRUE;
209     GST_DEBUG ("ximageutil is using XShm extension");
210   } else {
211     xcontext->use_xshm = FALSE;
212     GST_DEBUG ("ximageutil is not using XShm extension");
213   }
214 #endif /* HAVE_XSHM */
215
216   /* our caps system handles 24/32bpp RGB as big-endian. */
217   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
218       xcontext->endianness == G_LITTLE_ENDIAN) {
219     xcontext->endianness = G_BIG_ENDIAN;
220     xcontext->r_mask_output = GUINT32_TO_BE (xcontext->visual->red_mask);
221     xcontext->g_mask_output = GUINT32_TO_BE (xcontext->visual->green_mask);
222     xcontext->b_mask_output = GUINT32_TO_BE (xcontext->visual->blue_mask);
223     if (xcontext->bpp == 24) {
224       xcontext->r_mask_output >>= 8;
225       xcontext->g_mask_output >>= 8;
226       xcontext->b_mask_output >>= 8;
227     }
228   } else {
229     xcontext->r_mask_output = xcontext->visual->red_mask;
230     xcontext->g_mask_output = xcontext->visual->green_mask;
231     xcontext->b_mask_output = xcontext->visual->blue_mask;
232   }
233
234   return xcontext;
235 }
236
237 /* This function cleans the X context. Closing the Display and unrefing the
238    caps for supported formats. */
239 void
240 ximageutil_xcontext_clear (GstXContext * xcontext)
241 {
242   g_return_if_fail (xcontext != NULL);
243
244   if (xcontext->caps != NULL)
245     gst_caps_unref (xcontext->caps);
246
247   if (xcontext->par) {
248     g_value_unset (xcontext->par);
249     g_free (xcontext->par);
250   }
251
252   XCloseDisplay (xcontext->disp);
253
254   g_free (xcontext);
255 }
256
257 /* This function calculates the pixel aspect ratio based on the properties
258  * in the xcontext structure and stores it there. */
259 void
260 ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext)
261 {
262   gint par[][2] = {
263     {1, 1},                     /* regular screen */
264     {16, 15},                   /* PAL TV */
265     {11, 10},                   /* 525 line Rec.601 video */
266     {54, 59}                    /* 625 line Rec.601 video */
267   };
268   gint i;
269   gint index;
270   gdouble ratio;
271   gdouble delta;
272
273 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
274
275   /* first calculate the "real" ratio based on the X values;
276    * which is the "physical" w/h divided by the w/h in pixels of the display */
277   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
278       / (xcontext->heightmm * xcontext->width);
279
280   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
281    * override here */
282   if (xcontext->width == 720 && xcontext->height == 576) {
283     ratio = 4.0 * 576 / (3.0 * 720);
284   }
285   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
286
287   /* now find the one from par[][2] with the lowest delta to the real one */
288   delta = DELTA (0);
289   index = 0;
290
291   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
292     gdouble this_delta = DELTA (i);
293
294     if (this_delta < delta) {
295       index = i;
296       delta = this_delta;
297     }
298   }
299
300   GST_DEBUG ("Decided on index %d (%d/%d)", index,
301       par[index][0], par[index][1]);
302
303   if (xcontext->par)
304     g_free (xcontext->par);
305   xcontext->par = g_new0 (GValue, 1);
306   g_value_init (xcontext->par, GST_TYPE_FRACTION);
307   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
308   GST_DEBUG ("set xcontext PAR to %d/%d\n",
309       gst_value_get_fraction_numerator (xcontext->par),
310       gst_value_get_fraction_denominator (xcontext->par));
311 }
312
313 static void
314 gst_ximagesrc_buffer_dispose (GstBuffer * ximage)
315 {
316   GstElement *parent;
317   GstMetaXImage *meta;
318
319   g_return_if_fail (ximage != NULL);
320
321   meta = GST_META_XIMAGE_GET (ximage);
322
323   parent = meta->parent;
324   if (parent == NULL) {
325     g_warning ("XImageSrcBuffer->ximagesrc == NULL");
326     goto beach;
327   }
328
329   if (meta->return_func)
330     meta->return_func (parent, ximage);
331
332 beach:
333   return;
334 }
335
336 void
337 gst_ximage_buffer_free (GstBuffer * ximage)
338 {
339   GstMetaXImage *meta;
340
341   meta = GST_META_XIMAGE_GET (ximage);
342
343   /* make sure it is not recycled */
344   meta->width = -1;
345   meta->height = -1;
346   gst_buffer_unref (ximage);
347 }
348
349 /* This function handles GstXImageSrcBuffer creation depending on XShm availability */
350 GstBuffer *
351 gst_ximageutil_ximage_new (GstXContext * xcontext,
352     GstElement * parent, int width, int height, BufferReturnFunc return_func)
353 {
354   GstBuffer *ximage = NULL;
355   GstMetaXImage *meta;
356   gboolean succeeded = FALSE;
357
358   ximage = gst_buffer_new ();
359   GST_MINI_OBJECT_CAST (ximage)->dispose =
360       (GstMiniObjectDisposeFunction) gst_ximagesrc_buffer_dispose;
361
362   meta = GST_META_XIMAGE_ADD (ximage);
363   meta->width = width;
364   meta->height = height;
365
366 #ifdef HAVE_XSHM
367   meta->SHMInfo.shmaddr = ((void *) -1);
368   meta->SHMInfo.shmid = -1;
369
370   if (xcontext->use_xshm) {
371     meta->ximage = XShmCreateImage (xcontext->disp,
372         xcontext->visual, xcontext->depth,
373         ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
374     if (!meta->ximage) {
375       GST_WARNING_OBJECT (parent,
376           "could not XShmCreateImage a %dx%d image", meta->width, meta->height);
377
378       /* Retry without XShm */
379       xcontext->use_xshm = FALSE;
380       goto no_xshm;
381     }
382
383     /* we have to use the returned bytes_per_line for our shm size */
384     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
385     meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
386     if (meta->SHMInfo.shmid == -1)
387       goto beach;
388
389     meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, 0, 0);
390     if (meta->SHMInfo.shmaddr == ((void *) -1))
391       goto beach;
392
393     /* Delete the SHM segment. It will actually go away automatically
394      * when we detach now */
395     shmctl (meta->SHMInfo.shmid, IPC_RMID, 0);
396
397     meta->ximage->data = meta->SHMInfo.shmaddr;
398     meta->SHMInfo.readOnly = FALSE;
399
400     if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
401       goto beach;
402
403     XSync (xcontext->disp, FALSE);
404   } else
405   no_xshm:
406 #endif /* HAVE_XSHM */
407   {
408     meta->ximage = XCreateImage (xcontext->disp,
409         xcontext->visual,
410         xcontext->depth,
411         ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
412     if (!meta->ximage)
413       goto beach;
414
415     /* we have to use the returned bytes_per_line for our image size */
416     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
417     meta->ximage->data = g_malloc (meta->size);
418
419     XSync (xcontext->disp, FALSE);
420   }
421   succeeded = TRUE;
422
423   gst_buffer_take_memory (ximage, -1,
424       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
425           meta->size, 0, meta->size, NULL, NULL));
426
427   /* Keep a ref to our src */
428   meta->parent = gst_object_ref (parent);
429   meta->return_func = return_func;
430 beach:
431   if (!succeeded) {
432     gst_ximage_buffer_free (ximage);
433     ximage = NULL;
434   }
435
436   return ximage;
437 }
438
439 /* This function destroys a GstXImageBuffer handling XShm availability */
440 void
441 gst_ximageutil_ximage_destroy (GstXContext * xcontext, GstBuffer * ximage)
442 {
443   GstMetaXImage *meta;
444
445   meta = GST_META_XIMAGE_GET (ximage);
446
447   /* We might have some buffers destroyed after changing state to NULL */
448   if (!xcontext)
449     goto beach;
450
451   g_return_if_fail (ximage != NULL);
452
453 #ifdef HAVE_XSHM
454   if (xcontext->use_xshm) {
455     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
456       XShmDetach (xcontext->disp, &meta->SHMInfo);
457       XSync (xcontext->disp, 0);
458       shmdt (meta->SHMInfo.shmaddr);
459     }
460     if (meta->ximage)
461       XDestroyImage (meta->ximage);
462
463   } else
464 #endif /* HAVE_XSHM */
465   {
466     if (meta->ximage) {
467       XDestroyImage (meta->ximage);
468     }
469   }
470
471   XSync (xcontext->disp, FALSE);
472 beach:
473   if (meta->parent) {
474     /* Release the ref to our parent */
475     gst_object_unref (meta->parent);
476     meta->parent = NULL;
477   }
478
479   return;
480 }