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