2 * Copyright (C) <2005> Luca Ognibene <luogni@tin.it>
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.
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.
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.
24 #include "ximageutil.h"
27 static gboolean error_caught = FALSE;
30 ximageutil_handle_xerror (Display * display, XErrorEvent * xevent)
34 XGetErrorText (display, xevent->error_code, error_msg, 1024);
35 GST_DEBUG ("ximageutil failed to use XShm calls. error: %s", error_msg);
40 /* This function checks that it is actually really possible to create an image
43 ximageutil_check_xshm_calls (GstXContext * xcontext)
46 XShmSegmentInfo SHMInfo;
48 int (*handler) (Display *, XErrorEvent *);
49 gboolean result = FALSE;
50 gboolean did_attach = FALSE;
52 g_return_val_if_fail (xcontext != NULL, FALSE);
54 /* Sync to ensure any older errors are already processed */
55 XSync (xcontext->disp, FALSE);
57 /* Set defaults so we don't free these later unnecessarily */
58 SHMInfo.shmaddr = ((void *) -1);
61 /* Setting an error handler to catch failure */
63 handler = XSetErrorHandler (ximageutil_handle_xerror);
65 /* Trying to create a 1x1 ximage */
66 GST_DEBUG ("XShmCreateImage of 1x1");
68 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
69 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
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");
77 size = ximage->height * ximage->bytes_per_line;
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",
86 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
87 if (SHMInfo.shmaddr == ((void *) -1)) {
88 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
92 /* Delete the SHM segment. It will actually go away automatically
93 * when we detach now */
94 shmctl (SHMInfo.shmid, IPC_RMID, 0);
96 ximage->data = SHMInfo.shmaddr;
97 SHMInfo.readOnly = FALSE;
99 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
100 GST_WARNING ("Failed to XShmAttach");
104 /* Sync to ensure we see any errors we caused */
105 XSync (xcontext->disp, FALSE);
109 /* store whether we succeeded in result */
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);
119 XShmDetach (xcontext->disp, &SHMInfo);
120 XSync (xcontext->disp, FALSE);
122 if (SHMInfo.shmaddr != ((void *) -1))
123 shmdt (SHMInfo.shmaddr);
125 XDestroyImage (ximage);
128 #endif /* HAVE_XSHM */
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
135 ximageutil_xcontext_get (GstElement * parent, const gchar * display_name)
137 GstXContext *xcontext = NULL;
138 XPixmapFormatValues *px_formats = NULL;
139 gint nb_formats = 0, i;
141 xcontext = g_new0 (GstXContext, 1);
143 xcontext->disp = XOpenDisplay (display_name);
144 GST_DEBUG_OBJECT (parent, "opened display %p", xcontext->disp);
145 if (!xcontext->disp) {
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);
157 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
158 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
160 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
161 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
163 xcontext->caps = NULL;
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);
169 /* We get supported pixmap formats at supported depth */
170 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
173 XCloseDisplay (xcontext->disp);
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;
186 xcontext->endianness =
187 (ImageByteOrder (xcontext->disp) ==
188 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
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");
197 xcontext->use_xshm = FALSE;
198 GST_DEBUG ("ximageutil is not using XShm extension");
200 #endif /* HAVE_XSHM */
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;
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;
223 /* This function cleans the X context. Closing the Display and unrefing the
224 caps for supported formats. */
226 ximageutil_xcontext_clear (GstXContext * xcontext)
228 g_return_if_fail (xcontext != NULL);
230 if (xcontext->caps != NULL)
231 gst_caps_unref (xcontext->caps);
234 g_value_unset (xcontext->par);
235 g_free (xcontext->par);
238 XCloseDisplay (xcontext->disp);
243 /* This function calculates the pixel aspect ratio based on the properties
244 * in the xcontext structure and stores it there. */
246 ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext)
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 */
259 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
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);
266 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
268 if (xcontext->width == 720 && xcontext->height == 576) {
269 ratio = 4.0 * 576 / (3.0 * 720);
271 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
273 /* now find the one from par[][2] with the lowest delta to the real one */
277 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
278 gdouble this_delta = DELTA (i);
280 if (this_delta < delta) {
286 GST_DEBUG ("Decided on index %d (%d/%d)", index,
287 par[index][0], par[index][1]);
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));
299 static GstBufferClass *ximagesrc_buffer_parent_class = NULL;
302 gst_ximagesrc_buffer_finalize (GstXImageSrcBuffer * ximage)
306 g_return_if_fail (ximage != NULL);
308 parent = ximage->parent;
309 if (parent == NULL) {
310 g_warning ("XImageSrcBuffer->ximagesrc == NULL");
314 if (ximage->return_func)
315 ximage->return_func (parent, ximage);
319 GST_MINI_OBJECT_CLASS (ximagesrc_buffer_parent_class)->finalize
320 (GST_MINI_OBJECT (ximage));
326 gst_ximage_buffer_free (GstXImageSrcBuffer * ximage)
328 /* make sure it is not recycled */
331 gst_buffer_unref (GST_BUFFER (ximage));
335 gst_ximagesrc_buffer_init (GstXImageSrcBuffer * ximage_buffer, gpointer g_class)
338 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
339 ximage_buffer->SHMInfo.shmid = -1;
344 gst_ximagesrc_buffer_class_init (gpointer g_class, gpointer class_data)
346 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
348 ximagesrc_buffer_parent_class = g_type_class_peek_parent (g_class);
350 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
351 gst_ximagesrc_buffer_finalize;
355 gst_ximagesrc_buffer_get_type (void)
357 static GType _gst_ximagesrc_buffer_type;
359 if (G_UNLIKELY (_gst_ximagesrc_buffer_type == 0)) {
360 static const GTypeInfo ximagesrc_buffer_info = {
361 sizeof (GstBufferClass),
364 gst_ximagesrc_buffer_class_init,
367 sizeof (GstXImageSrcBuffer),
369 (GInstanceInitFunc) gst_ximagesrc_buffer_init,
372 _gst_ximagesrc_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
373 "GstXImageSrcBuffer", &ximagesrc_buffer_info, 0);
375 return _gst_ximagesrc_buffer_type;
378 /* This function handles GstXImageSrcBuffer creation depending on XShm availability */
380 gst_ximageutil_ximage_new (GstXContext * xcontext,
381 GstElement * parent, int width, int height, BufferReturnFunc return_func)
383 GstXImageSrcBuffer *ximage = NULL;
384 gboolean succeeded = FALSE;
387 (GstXImageSrcBuffer *) gst_mini_object_new (GST_TYPE_XIMAGESRC_BUFFER);
389 ximage->width = width;
390 ximage->height = height;
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) {
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,
405 if (ximage->SHMInfo.shmid == -1)
408 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
409 if (ximage->SHMInfo.shmaddr == ((void *) -1))
412 /* Delete the SHM segment. It will actually go away automatically
413 * when we detach now */
414 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
416 ximage->ximage->data = ximage->SHMInfo.shmaddr;
417 ximage->SHMInfo.readOnly = FALSE;
419 if (XShmAttach (xcontext->disp, &ximage->SHMInfo) == 0)
422 XSync (xcontext->disp, FALSE);
424 #endif /* HAVE_XSHM */
426 ximage->ximage = XCreateImage (xcontext->disp,
429 ZPixmap, 0, NULL, ximage->width, ximage->height, xcontext->bpp, 0);
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);
437 XSync (xcontext->disp, FALSE);
441 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
442 GST_BUFFER_SIZE (ximage) = ximage->size;
444 /* Keep a ref to our src */
445 ximage->parent = gst_object_ref (parent);
446 ximage->return_func = return_func;
449 gst_ximage_buffer_free (ximage);
456 /* This function destroys a GstXImageBuffer handling XShm availability */
458 gst_ximageutil_ximage_destroy (GstXContext * xcontext,
459 GstXImageSrcBuffer * ximage)
461 /* We might have some buffers destroyed after changing state to NULL */
465 g_return_if_fail (ximage != NULL);
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);
475 XDestroyImage (ximage->ximage);
478 #endif /* HAVE_XSHM */
480 if (ximage->ximage) {
481 XDestroyImage (ximage->ximage);
485 XSync (xcontext->disp, FALSE);
487 if (ximage->parent) {
488 /* Release the ref to our parent */
489 gst_object_unref (ximage->parent);
490 ximage->parent = NULL;