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