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