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