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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "ximageutil.h"
27 gst_meta_ximage_api_get_type (void)
29 static volatile GType type;
30 static const gchar *tags[] = { "memory", NULL };
32 if (g_once_init_enter (&type)) {
33 GType _type = gst_meta_api_type_register ("GstMetaXImageSrcAPI", tags);
34 g_once_init_leave (&type, _type);
40 gst_meta_ximage_get_info (void)
42 static const GstMetaInfo *meta_ximage_info = NULL;
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);
51 return meta_ximage_info;
55 static gboolean error_caught = FALSE;
58 ximageutil_handle_xerror (Display * display, XErrorEvent * xevent)
62 XGetErrorText (display, xevent->error_code, error_msg, 1024);
63 GST_DEBUG ("ximageutil failed to use XShm calls. error: %s", error_msg);
68 /* This function checks that it is actually really possible to create an image
71 ximageutil_check_xshm_calls (GstXContext * xcontext)
74 XShmSegmentInfo SHMInfo;
76 int (*handler) (Display *, XErrorEvent *);
77 gboolean result = FALSE;
78 gboolean did_attach = FALSE;
80 g_return_val_if_fail (xcontext != NULL, FALSE);
82 /* Sync to ensure any older errors are already processed */
83 XSync (xcontext->disp, FALSE);
85 /* Set defaults so we don't free these later unnecessarily */
86 SHMInfo.shmaddr = ((void *) -1);
89 /* Setting an error handler to catch failure */
91 handler = XSetErrorHandler (ximageutil_handle_xerror);
93 /* Trying to create a 1x1 ximage */
94 GST_DEBUG ("XShmCreateImage of 1x1");
96 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
97 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
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");
105 size = ximage->height * ximage->bytes_per_line;
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",
114 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
115 if (SHMInfo.shmaddr == ((void *) -1)) {
116 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
120 /* Delete the SHM segment. It will actually go away automatically
121 * when we detach now */
122 shmctl (SHMInfo.shmid, IPC_RMID, 0);
124 ximage->data = SHMInfo.shmaddr;
125 SHMInfo.readOnly = FALSE;
127 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
128 GST_WARNING ("Failed to XShmAttach");
132 /* Sync to ensure we see any errors we caused */
133 XSync (xcontext->disp, FALSE);
137 /* store whether we succeeded in result */
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);
147 XShmDetach (xcontext->disp, &SHMInfo);
148 XSync (xcontext->disp, FALSE);
150 if (SHMInfo.shmaddr != ((void *) -1))
151 shmdt (SHMInfo.shmaddr);
153 XDestroyImage (ximage);
156 #endif /* HAVE_XSHM */
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
163 ximageutil_xcontext_get (GstElement * parent, const gchar * display_name)
165 GstXContext *xcontext = NULL;
166 XPixmapFormatValues *px_formats = NULL;
167 gint nb_formats = 0, i;
169 xcontext = g_new0 (GstXContext, 1);
171 xcontext->disp = XOpenDisplay (display_name);
172 GST_DEBUG_OBJECT (parent, "opened display %p", xcontext->disp);
173 if (!xcontext->disp) {
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);
185 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
186 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
188 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
189 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
191 xcontext->caps = NULL;
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);
197 /* We get supported pixmap formats at supported depth */
198 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
201 XCloseDisplay (xcontext->disp);
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;
214 xcontext->endianness =
215 (ImageByteOrder (xcontext->disp) ==
216 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
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");
225 xcontext->use_xshm = FALSE;
226 GST_DEBUG ("ximageutil is not using XShm extension");
228 #endif /* HAVE_XSHM */
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;
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;
251 /* This function cleans the X context. Closing the Display and unrefing the
252 caps for supported formats. */
254 ximageutil_xcontext_clear (GstXContext * xcontext)
256 g_return_if_fail (xcontext != NULL);
258 if (xcontext->caps != NULL)
259 gst_caps_unref (xcontext->caps);
261 XCloseDisplay (xcontext->disp);
266 /* This function calculates the pixel aspect ratio based on the properties
267 * in the xcontext structure and stores it there. */
269 ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext)
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 */
282 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
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);
289 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
291 if (xcontext->width == 720 && xcontext->height == 576) {
292 ratio = 4.0 * 576 / (3.0 * 720);
294 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
296 /* now find the one from par[][2] with the lowest delta to the real one */
300 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
301 gdouble this_delta = DELTA (i);
303 if (this_delta < delta) {
309 GST_DEBUG ("Decided on index %d (%d/%d)", index,
310 par[index][0], par[index][1]);
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);
318 gst_ximagesrc_buffer_dispose (GstBuffer * ximage)
323 g_return_if_fail (ximage != NULL);
325 meta = GST_META_XIMAGE_GET (ximage);
327 parent = meta->parent;
328 if (parent == NULL) {
329 g_warning ("XImageSrcBuffer->ximagesrc == NULL");
333 if (meta->return_func)
334 meta->return_func (parent, ximage);
341 gst_ximage_buffer_free (GstBuffer * ximage)
345 meta = GST_META_XIMAGE_GET (ximage);
347 /* make sure it is not recycled */
350 gst_buffer_unref (ximage);
353 /* This function handles GstXImageSrcBuffer creation depending on XShm availability */
355 gst_ximageutil_ximage_new (GstXContext * xcontext,
356 GstElement * parent, int width, int height, BufferReturnFunc return_func)
358 GstBuffer *ximage = NULL;
360 gboolean succeeded = FALSE;
362 ximage = gst_buffer_new ();
363 GST_MINI_OBJECT_CAST (ximage)->dispose =
364 (GstMiniObjectDisposeFunction) gst_ximagesrc_buffer_dispose;
366 meta = GST_META_XIMAGE_ADD (ximage);
368 meta->height = height;
371 meta->SHMInfo.shmaddr = ((void *) -1);
372 meta->SHMInfo.shmid = -1;
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);
379 GST_WARNING_OBJECT (parent,
380 "could not XShmCreateImage a %dx%d image", meta->width, meta->height);
382 /* Retry without XShm */
383 xcontext->use_xshm = FALSE;
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)
393 meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, 0, 0);
394 if (meta->SHMInfo.shmaddr == ((void *) -1))
397 /* Delete the SHM segment. It will actually go away automatically
398 * when we detach now */
399 shmctl (meta->SHMInfo.shmid, IPC_RMID, 0);
401 meta->ximage->data = meta->SHMInfo.shmaddr;
402 meta->SHMInfo.readOnly = FALSE;
404 if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
407 XSync (xcontext->disp, FALSE);
410 #endif /* HAVE_XSHM */
412 meta->ximage = XCreateImage (xcontext->disp,
415 ZPixmap, 0, NULL, meta->width, meta->height, xcontext->bpp, 0);
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);
423 XSync (xcontext->disp, FALSE);
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));
431 /* Keep a ref to our src */
432 meta->parent = gst_object_ref (parent);
433 meta->return_func = return_func;
436 gst_ximage_buffer_free (ximage);
443 /* This function destroys a GstXImageBuffer handling XShm availability */
445 gst_ximageutil_ximage_destroy (GstXContext * xcontext, GstBuffer * ximage)
449 meta = GST_META_XIMAGE_GET (ximage);
451 /* We might have some buffers destroyed after changing state to NULL */
455 g_return_if_fail (ximage != NULL);
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);
465 XDestroyImage (meta->ximage);
468 #endif /* HAVE_XSHM */
471 XDestroyImage (meta->ximage);
475 XSync (xcontext->disp, FALSE);
478 /* Release the ref to our parent */
479 gst_object_unref (meta->parent);