configure.ac, changelog, TODO
[cpumem-applet] / gst / 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)->
320       finalize (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 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       goto beach;
399     }
400
401     /* we have to use the returned bytes_per_line for our shm size */
402     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
403     ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
404         IPC_CREAT | 0777);
405     if (ximage->SHMInfo.shmid == -1)
406       goto beach;
407
408     ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
409     if (ximage->SHMInfo.shmaddr == ((void *) -1))
410       goto beach;
411
412     /* Delete the SHM segment. It will actually go away automatically
413      * when we detach now */
414     shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
415
416     ximage->ximage->data = ximage->SHMInfo.shmaddr;
417     ximage->SHMInfo.readOnly = FALSE;
418
419     if (XShmAttach (xcontext->disp, &ximage->SHMInfo) == 0)
420       goto beach;
421
422     XSync (xcontext->disp, FALSE);
423   } else
424 #endif /* HAVE_XSHM */
425   {
426     ximage->ximage = XCreateImage (xcontext->disp,
427         xcontext->visual,
428         xcontext->depth,
429         ZPixmap, 0, NULL, ximage->width, ximage->height, xcontext->bpp, 0);
430     if (!ximage->ximage)
431       goto beach;
432
433     /* we have to use the returned bytes_per_line for our image size */
434     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
435     ximage->ximage->data = g_malloc (ximage->size);
436
437     XSync (xcontext->disp, FALSE);
438   }
439   succeeded = TRUE;
440
441   GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
442   GST_BUFFER_SIZE (ximage) = ximage->size;
443
444   /* Keep a ref to our src */
445   ximage->parent = gst_object_ref (parent);
446   ximage->return_func = return_func;
447 beach:
448   if (!succeeded) {
449     gst_ximage_buffer_free (ximage);
450     ximage = NULL;
451   }
452
453   return ximage;
454 }
455
456 /* This function destroys a GstXImageBuffer handling XShm availability */
457 void
458 gst_ximageutil_ximage_destroy (GstXContext * xcontext,
459     GstXImageSrcBuffer * ximage)
460 {
461   /* We might have some buffers destroyed after changing state to NULL */
462   if (!xcontext)
463     goto beach;
464
465   g_return_if_fail (ximage != NULL);
466
467 #ifdef HAVE_XSHM
468   if (xcontext->use_xshm) {
469     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
470       XShmDetach (xcontext->disp, &ximage->SHMInfo);
471       XSync (xcontext->disp, 0);
472       shmdt (ximage->SHMInfo.shmaddr);
473     }
474     if (ximage->ximage)
475       XDestroyImage (ximage->ximage);
476
477   } else
478 #endif /* HAVE_XSHM */
479   {
480     if (ximage->ximage) {
481       XDestroyImage (ximage->ximage);
482     }
483   }
484
485   XSync (xcontext->disp, FALSE);
486 beach:
487   if (ximage->parent) {
488     /* Release the ref to our parent */
489     gst_object_unref (ximage->parent);
490     ximage->parent = NULL;
491   }
492
493   return;
494 }