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 gst_meta_ximage_get_info (void)
29 static const GstMetaInfo *meta_ximage_info = NULL;
31 if (meta_ximage_info == NULL) {
33 gst_meta_register ("GstMetaXImageSrc", "GstMetaXImageSrc",
34 sizeof (GstMetaXImage), (GstMetaInitFunction) NULL,
35 (GstMetaFreeFunction) NULL, (GstMetaTransformFunction) NULL);
37 return meta_ximage_info;
41 static gboolean error_caught = FALSE;
44 ximageutil_handle_xerror (Display * display, XErrorEvent * xevent)
48 XGetErrorText (display, xevent->error_code, error_msg, 1024);
49 GST_DEBUG ("ximageutil failed to use XShm calls. error: %s", error_msg);
54 /* This function checks that it is actually really possible to create an image
57 ximageutil_check_xshm_calls (GstXContext * xcontext)
60 XShmSegmentInfo SHMInfo;
62 int (*handler) (Display *, XErrorEvent *);
63 gboolean result = FALSE;
64 gboolean did_attach = FALSE;
66 g_return_val_if_fail (xcontext != NULL, FALSE);
68 /* Sync to ensure any older errors are already processed */
69 XSync (xcontext->disp, FALSE);
71 /* Set defaults so we don't free these later unnecessarily */
72 SHMInfo.shmaddr = ((void *) -1);
75 /* Setting an error handler to catch failure */
77 handler = XSetErrorHandler (ximageutil_handle_xerror);
79 /* Trying to create a 1x1 ximage */
80 GST_DEBUG ("XShmCreateImage of 1x1");
82 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
83 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
85 /* Might cause an error, sync to ensure it is noticed */
86 XSync (xcontext->disp, FALSE);
87 if (!ximage || error_caught) {
88 GST_WARNING ("could not XShmCreateImage a 1x1 image");
91 size = ximage->height * ximage->bytes_per_line;
93 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
94 if (SHMInfo.shmid == -1) {
95 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
100 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
101 if (SHMInfo.shmaddr == ((void *) -1)) {
102 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
106 /* Delete the SHM segment. It will actually go away automatically
107 * when we detach now */
108 shmctl (SHMInfo.shmid, IPC_RMID, 0);
110 ximage->data = SHMInfo.shmaddr;
111 SHMInfo.readOnly = FALSE;
113 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
114 GST_WARNING ("Failed to XShmAttach");
118 /* Sync to ensure we see any errors we caused */
119 XSync (xcontext->disp, FALSE);
123 /* store whether we succeeded in result */
127 /* Sync to ensure we swallow any errors we caused and reset error_caught */
128 XSync (xcontext->disp, FALSE);
129 error_caught = FALSE;
130 XSetErrorHandler (handler);
133 XShmDetach (xcontext->disp, &SHMInfo);
134 XSync (xcontext->disp, FALSE);
136 if (SHMInfo.shmaddr != ((void *) -1))
137 shmdt (SHMInfo.shmaddr);
139 XDestroyImage (ximage);
142 #endif /* HAVE_XSHM */
144 /* This function gets the X Display and global info about it. Everything is
145 stored in our object and will be cleaned when the object is disposed. Note
146 here that caps for supported format are generated without any window or
149 ximageutil_xcontext_get (GstElement * parent, const gchar * display_name)
151 GstXContext *xcontext = NULL;
152 XPixmapFormatValues *px_formats = NULL;
153 gint nb_formats = 0, i;
155 xcontext = g_new0 (GstXContext, 1);
157 xcontext->disp = XOpenDisplay (display_name);
158 GST_DEBUG_OBJECT (parent, "opened display %p", xcontext->disp);
159 if (!xcontext->disp) {
163 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
164 xcontext->screen_num = DefaultScreen (xcontext->disp);
165 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
166 xcontext->root = DefaultRootWindow (xcontext->disp);
167 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
168 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
169 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
171 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
172 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
174 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
175 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
177 xcontext->caps = NULL;
179 GST_DEBUG_OBJECT (parent, "X reports %dx%d pixels and %d mm x %d mm",
180 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
181 ximageutil_calculate_pixel_aspect_ratio (xcontext);
183 /* We get supported pixmap formats at supported depth */
184 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
187 XCloseDisplay (xcontext->disp);
192 /* We get bpp value corresponding to our running depth */
193 for (i = 0; i < nb_formats; i++) {
194 if (px_formats[i].depth == xcontext->depth)
195 xcontext->bpp = px_formats[i].bits_per_pixel;
200 xcontext->endianness =
201 (ImageByteOrder (xcontext->disp) ==
202 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
205 /* Search for XShm extension support */
206 if (XShmQueryExtension (xcontext->disp) &&
207 ximageutil_check_xshm_calls (xcontext)) {
208 xcontext->use_xshm = TRUE;
209 GST_DEBUG ("ximageutil is using XShm extension");
211 xcontext->use_xshm = FALSE;
212 GST_DEBUG ("ximageutil is not using XShm extension");
214 #endif /* HAVE_XSHM */
216 /* our caps system handles 24/32bpp RGB as big-endian. */
217 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
218 xcontext->endianness == G_LITTLE_ENDIAN) {
219 xcontext->endianness = G_BIG_ENDIAN;
220 xcontext->r_mask_output = GUINT32_TO_BE (xcontext->visual->red_mask);
221 xcontext->g_mask_output = GUINT32_TO_BE (xcontext->visual->green_mask);
222 xcontext->b_mask_output = GUINT32_TO_BE (xcontext->visual->blue_mask);
223 if (xcontext->bpp == 24) {
224 xcontext->r_mask_output >>= 8;
225 xcontext->g_mask_output >>= 8;
226 xcontext->b_mask_output >>= 8;
229 xcontext->r_mask_output = xcontext->visual->red_mask;
230 xcontext->g_mask_output = xcontext->visual->green_mask;
231 xcontext->b_mask_output = xcontext->visual->blue_mask;
237 /* This function cleans the X context. Closing the Display and unrefing the
238 caps for supported formats. */
240 ximageutil_xcontext_clear (GstXContext * xcontext)
242 g_return_if_fail (xcontext != NULL);
244 if (xcontext->caps != NULL)
245 gst_caps_unref (xcontext->caps);
248 g_value_unset (xcontext->par);
249 g_free (xcontext->par);
252 XCloseDisplay (xcontext->disp);
257 /* This function calculates the pixel aspect ratio based on the properties
258 * in the xcontext structure and stores it there. */
260 ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext)
263 {1, 1}, /* regular screen */
264 {16, 15}, /* PAL TV */
265 {11, 10}, /* 525 line Rec.601 video */
266 {54, 59} /* 625 line Rec.601 video */
273 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
275 /* first calculate the "real" ratio based on the X values;
276 * which is the "physical" w/h divided by the w/h in pixels of the display */
277 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
278 / (xcontext->heightmm * xcontext->width);
280 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
282 if (xcontext->width == 720 && xcontext->height == 576) {
283 ratio = 4.0 * 576 / (3.0 * 720);
285 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
287 /* now find the one from par[][2] with the lowest delta to the real one */
291 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
292 gdouble this_delta = DELTA (i);
294 if (this_delta < delta) {
300 GST_DEBUG ("Decided on index %d (%d/%d)", index,
301 par[index][0], par[index][1]);
304 g_free (xcontext->par);
305 xcontext->par = g_new0 (GValue, 1);
306 g_value_init (xcontext->par, GST_TYPE_FRACTION);
307 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
308 GST_DEBUG ("set xcontext PAR to %d/%d\n",
309 gst_value_get_fraction_numerator (xcontext->par),
310 gst_value_get_fraction_denominator (xcontext->par));
314 gst_ximagesrc_buffer_dispose (GstBuffer * ximage)
319 g_return_if_fail (ximage != NULL);
321 meta = GST_META_XIMAGE_GET (ximage);
323 parent = meta->parent;
324 if (parent == NULL) {
325 g_warning ("XImageSrcBuffer->ximagesrc == NULL");
329 if (meta->return_func)
330 meta->return_func (parent, ximage);
337 gst_ximage_buffer_free (GstBuffer * ximage)
341 meta = GST_META_XIMAGE_GET (ximage);
343 /* make sure it is not recycled */
346 gst_buffer_unref (ximage);
349 /* This function handles GstXImageSrcBuffer creation depending on XShm availability */
351 gst_ximageutil_ximage_new (GstXContext * xcontext,
352 GstElement * parent, int width, int height, BufferReturnFunc return_func)
354 GstBuffer *ximage = NULL;
356 gboolean succeeded = FALSE;
358 ximage = gst_buffer_new ();
359 GST_MINI_OBJECT_CAST (ximage)->dispose =
360 (GstMiniObjectDisposeFunction) gst_ximagesrc_buffer_dispose;
362 meta = GST_META_XIMAGE_ADD (ximage);
364 meta->height = height;
367 meta->SHMInfo.shmaddr = ((void *) -1);
368 meta->SHMInfo.shmid = -1;
370 if (xcontext->use_xshm) {
371 meta->ximage = XShmCreateImage (xcontext->disp,
372 xcontext->visual, xcontext->depth,
373 ZPixmap, NULL, &meta->SHMInfo, meta->width, meta->height);
375 GST_WARNING_OBJECT (parent,
376 "could not XShmCreateImage a %dx%d image", meta->width, meta->height);
378 /* Retry without XShm */
379 xcontext->use_xshm = FALSE;
383 /* we have to use the returned bytes_per_line for our shm size */
384 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
385 meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
386 if (meta->SHMInfo.shmid == -1)
389 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, 0, 0);
390 if (meta->SHMInfo.shmaddr == ((void *) -1))
393 /* Delete the SHM segment. It will actually go away automatically
394 * when we detach now */
395 shmctl (meta->SHMInfo.shmid, IPC_RMID, 0);
397 meta->ximage->data = meta->SHMInfo.shmaddr;
398 meta->SHMInfo.readOnly = FALSE;
400 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
403 XSync (xcontext->disp, FALSE);
406 #endif /* HAVE_XSHM */
408 meta->ximage = XCreateImage (xcontext->disp,
411 ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
415 /* we have to use the returned bytes_per_line for our image size */
416 meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
417 meta->ximage->data = g_malloc (meta->size);
419 XSync (xcontext->disp, FALSE);
423 gst_buffer_take_memory (ximage, -1,
424 gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
425 meta->size, 0, meta->size, NULL, NULL));
427 /* Keep a ref to our src */
428 meta->parent = gst_object_ref (parent);
429 meta->return_func = return_func;
432 gst_ximage_buffer_free (ximage);
439 /* This function destroys a GstXImageBuffer handling XShm availability */
441 gst_ximageutil_ximage_destroy (GstXContext * xcontext, GstBuffer * ximage)
445 meta = GST_META_XIMAGE_GET (ximage);
447 /* We might have some buffers destroyed after changing state to NULL */
451 g_return_if_fail (ximage != NULL);
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);
461 XDestroyImage (meta->ximage);
464 #endif /* HAVE_XSHM */
467 XDestroyImage (meta->ximage);
471 XSync (xcontext->disp, FALSE);
474 /* Release the ref to our parent */
475 gst_object_unref (meta->parent);