Send seek event to baseparse when aacparse seek failed in push mode
[platform/upstream/gst-plugins-good.git] / sys / ximage / 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., 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
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
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 *
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
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
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 *
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
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
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
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
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 *
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
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 }