Fix buffer_alloc in ximagesink
[platform/upstream/gstreamer.git] / sys / ximage / ximagesink.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-ximagesink
22  *
23  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24  * display. This element can receive a Window ID from the application through
25  * the XOverlay interface and will then render video frames in this drawable.
26  * If no Window ID was provided by the application, the element will create its
27  * own internal window and render into it.
28  *
29  * <refsect2>
30  * <title>Scaling</title>
31  * <para>
32  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33  * reverse caps negotiation to try to get scaled video frames for the drawable.
34  * This is accomplished by asking the peer pad if it accepts some different caps
35  * which in most cases implies that there is a scaling element in the pipeline,
36  * or that an element generating the video frames can generate them with a 
37  * different geometry. This mechanism is handled during buffer allocations, for
38  * each allocation request the video sink will check the drawable geometry, look
39  * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40  * desired video frames and then check that the peer pad accept those new caps.
41  * If it does it will then allocate a buffer in video memory with this new
42  * geometry and return it with the new caps.
43  * </para>
44  * </refsect2>
45  * <refsect2>
46  * <title>Events</title>
47  * <para>
48  * XImageSink creates a thread to handle events coming from the drawable. There
49  * are several kind of events that can be grouped in 2 big categories: input 
50  * events and window state related events. Input events will be translated to
51  * navigation events and pushed upstream for other elements to react on them.
52  * This includes events such as pointer moves, key press/release, clicks etc...
53  * Other events are used to handle the drawable appearance even when the data
54  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55  * paused, it will receive expose events from the drawable and draw the latest
56  * frame with correct borders/aspect-ratio.
57  * </para>
58  * </refsect2>
59  * <refsect2>
60  * <title>Pixel aspect ratio</title>
61  * <para>
62  * When changing state to GST_STATE_READY, XImageSink will open a connection to
63  * the display specified in the #GstXImageSink:display property or the default
64  * display if nothing specified. Once this connection is open it will inspect 
65  * the display configuration including the physical display geometry and 
66  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67  * video sink will set the calculated pixel aspect ratio on the caps to make 
68  * sure that incoming video frames will have the correct pixel aspect ratio for
69  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70  * then possible to enforce a specific pixel aspect ratio using the
71  * #GstXImageSink:pixel-aspect-ratio property.
72  * </para>
73  * </refsect2>
74  * <refsect2>
75  * <title>Examples</title>
76  * |[
77  * gst-launch -v videotestsrc ! queue ! ximagesink
78  * ]| A pipeline to test reverse negotiation. When the test video signal appears
79  * you can resize the window and see that scaled buffers of the desired size are
80  * going to arrive with a short delay. This illustrates how buffers of desired
81  * size are allocated along the way. If you take away the queue, scaling will
82  * happen almost immediately.
83  * |[
84  * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85  * ]| A pipeline to test navigation events.
86  * While moving the mouse pointer over the test signal you will see a black box
87  * following the mouse pointer. If you press the mouse button somewhere on the 
88  * video and release it somewhere else a green box will appear where you pressed
89  * the button and a red one where you released it. (The navigationtest element
90  * is part of gst-plugins-good.)
91  * |[
92  * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94  * videotestsrc, in most cases the pixel aspect ratio of the display will be
95  * 1/1. This means that videoscale will have to do the scaling to convert 
96  * incoming frames to a size that will match the display pixel aspect ratio
97  * (from 320x240 to 320x180 in this case). Note that you might have to escape 
98  * some characters for your shell like '\(fraction\)'.
99  * </refsect2>
100  */
101
102 #ifdef HAVE_CONFIG_H
103 #include "config.h"
104 #endif
105
106 /* Our interfaces */
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
109
110 /* Object header */
111 #include "ximagesink.h"
112
113 /* Debugging category */
114 #include <gst/gstinfo.h>
115
116 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117 #define GST_CAT_DEFAULT gst_debug_ximagesink
118
119 typedef struct
120 {
121   unsigned long flags;
122   unsigned long functions;
123   unsigned long decorations;
124   long input_mode;
125   unsigned long status;
126 }
127 MotifWmHints, MwmHints;
128
129 #define MWM_HINTS_DECORATIONS   (1L << 1)
130
131 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
133     GstXImageBuffer * ximage);
134 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
135     GstXWindow * xwindow);
136 static void gst_ximagesink_expose (GstXOverlay * overlay);
137
138 /* ElementFactory information */
139 static const GstElementDetails gst_ximagesink_details =
140 GST_ELEMENT_DETAILS ("Video sink",
141     "Sink/Video",
142     "A standard X based videosink",
143     "Julien Moutte <julien@moutte.net>");
144
145 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
146 GST_STATIC_PAD_TEMPLATE ("sink",
147     GST_PAD_SINK,
148     GST_PAD_ALWAYS,
149     GST_STATIC_CAPS ("video/x-raw-rgb, "
150         "framerate = (fraction) [ 0, MAX ], "
151         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
152     );
153
154 enum
155 {
156   PROP_0,
157   PROP_DISPLAY,
158   PROP_SYNCHRONOUS,
159   PROP_PIXEL_ASPECT_RATIO,
160   PROP_FORCE_ASPECT_RATIO,
161   PROP_HANDLE_EVENTS,
162   PROP_HANDLE_EXPOSE
163 };
164
165 static GstVideoSinkClass *parent_class = NULL;
166
167 /* ============================================================= */
168 /*                                                               */
169 /*                       Private Methods                         */
170 /*                                                               */
171 /* ============================================================= */
172
173 /* ximage buffers */
174
175 static GstBufferClass *ximage_buffer_parent_class = NULL;
176
177 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
178
179 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
180 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
181 #define GST_XIMAGE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
182
183 /* So some words about GstMiniObject, this is pretty messy...
184    GstMiniObject does not use the standard finalizing of GObjects, you are 
185    supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
186    which will handle its own refcount system and call gst_mini_object_free.
187    gst_mini_object_free will call the class finalize method which is not the 
188    one from GObject, after calling this finalize method it will free the object
189    instance for you if the refcount is still 0 so you should not chain up */
190 static void
191 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
192 {
193   GstXImageSink *ximagesink = NULL;
194   gboolean recycled = FALSE;
195   gboolean running;
196
197   g_return_if_fail (ximage != NULL);
198
199   ximagesink = ximage->ximagesink;
200   if (G_UNLIKELY (ximagesink == NULL)) {
201     GST_WARNING_OBJECT (ximagesink, "no sink found");
202     goto beach;
203   }
204
205   GST_OBJECT_LOCK (ximagesink);
206   running = ximagesink->running;
207   GST_OBJECT_UNLOCK (ximagesink);
208
209   if (running == FALSE) {
210     /* If the sink is shutting down, need to clear the image */
211     GST_DEBUG_OBJECT (ximagesink,
212         "destroy image %p because the sink is shutting down", ximage);
213     gst_ximagesink_ximage_destroy (ximagesink, ximage);
214   } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
215       (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
216     /* If our geometry changed we can't reuse that image. */
217     GST_DEBUG_OBJECT (ximagesink,
218         "destroy image %p as its size changed %dx%d vs current %dx%d",
219         ximage, ximage->width, ximage->height,
220         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
221     gst_ximagesink_ximage_destroy (ximagesink, ximage);
222   } else {
223     /* In that case we can reuse the image and add it to our image pool. */
224     GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
225     /* need to increment the refcount again to recycle */
226     gst_buffer_ref (GST_BUFFER_CAST (ximage));
227     g_mutex_lock (ximagesink->pool_lock);
228     ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
229     g_mutex_unlock (ximagesink->pool_lock);
230     recycled = TRUE;
231   }
232
233   if (!recycled)
234     GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
235         (GST_MINI_OBJECT (ximage));
236
237 beach:
238   return;
239 }
240
241 static void
242 gst_ximage_buffer_free (GstXImageBuffer * ximage)
243 {
244   /* make sure it is not recycled */
245   ximage->width = -1;
246   ximage->height = -1;
247   gst_buffer_unref (GST_BUFFER_CAST (ximage));
248 }
249
250 static void
251 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
252 {
253 #ifdef HAVE_XSHM
254   ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
255   ximage_buffer->SHMInfo.shmid = -1;
256 #endif
257 }
258
259 static void
260 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
261 {
262   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
263
264   ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
265
266   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
267       gst_ximage_buffer_finalize;
268 }
269
270 static GType
271 gst_ximage_buffer_get_type (void)
272 {
273   static GType _gst_ximage_buffer_type;
274
275   if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
276     static const GTypeInfo ximage_buffer_info = {
277       sizeof (GstBufferClass),
278       NULL,
279       NULL,
280       gst_ximage_buffer_class_init,
281       NULL,
282       NULL,
283       sizeof (GstXImageBuffer),
284       0,
285       (GInstanceInitFunc) gst_ximage_buffer_init,
286       NULL
287     };
288     _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
289         "GstXImageBuffer", &ximage_buffer_info, 0);
290   }
291   return _gst_ximage_buffer_type;
292 }
293
294 /* X11 stuff */
295
296 static gboolean error_caught = FALSE;
297
298 static int
299 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
300 {
301   char error_msg[1024];
302
303   XGetErrorText (display, xevent->error_code, error_msg, 1024);
304   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
305   error_caught = TRUE;
306   return 0;
307 }
308
309 #ifdef HAVE_XSHM                /* Check that XShm calls actually work */
310
311 static gboolean
312 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
313     GstXContext * xcontext)
314 {
315   XImage *ximage;
316   XShmSegmentInfo SHMInfo;
317   size_t size;
318   int (*handler) (Display *, XErrorEvent *);
319   gboolean result = FALSE;
320   gboolean did_attach = FALSE;
321
322   g_return_val_if_fail (xcontext != NULL, FALSE);
323
324   /* Sync to ensure any older errors are already processed */
325   XSync (xcontext->disp, FALSE);
326
327   /* Set defaults so we don't free these later unnecessarily */
328   SHMInfo.shmaddr = ((void *) -1);
329   SHMInfo.shmid = -1;
330
331   /* Setting an error handler to catch failure */
332   error_caught = FALSE;
333   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
334
335   /* Trying to create a 1x1 ximage */
336   GST_DEBUG ("XShmCreateImage of 1x1");
337
338   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
339       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
340
341   /* Might cause an error, sync to ensure it is noticed */
342   XSync (xcontext->disp, FALSE);
343   if (!ximage || error_caught) {
344     GST_WARNING ("could not XShmCreateImage a 1x1 image");
345     goto beach;
346   }
347   size = ximage->height * ximage->bytes_per_line;
348
349   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
350   if (SHMInfo.shmid == -1) {
351     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
352         size);
353     goto beach;
354   }
355
356   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
357   if (SHMInfo.shmaddr == ((void *) -1)) {
358     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
359     /* Clean up shm seg */
360     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
361     goto beach;
362   }
363
364   ximage->data = SHMInfo.shmaddr;
365   SHMInfo.readOnly = FALSE;
366
367   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
368     GST_WARNING ("Failed to XShmAttach");
369     /* Clean up shm seg */
370     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
371     goto beach;
372   }
373
374   /* Sync to ensure we see any errors we caused */
375   XSync (xcontext->disp, FALSE);
376
377   /* Delete the shared memory segment as soon as everyone is attached. 
378    * This way, it will be deleted as soon as we detach later, and not
379    * leaked if we crash. */
380   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
381
382   if (!error_caught) {
383     did_attach = TRUE;
384     /* store whether we succeeded in result */
385     result = TRUE;
386   }
387
388 beach:
389   /* Sync to ensure we swallow any errors we caused and reset error_caught */
390   XSync (xcontext->disp, FALSE);
391   error_caught = FALSE;
392   XSetErrorHandler (handler);
393
394   if (did_attach) {
395     XShmDetach (xcontext->disp, &SHMInfo);
396     XSync (xcontext->disp, FALSE);
397   }
398   if (SHMInfo.shmaddr != ((void *) -1))
399     shmdt (SHMInfo.shmaddr);
400   if (ximage)
401     XDestroyImage (ximage);
402   return result;
403 }
404 #endif /* HAVE_XSHM */
405
406 /* This function handles GstXImageBuffer creation depending on XShm availability */
407 static GstXImageBuffer *
408 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
409 {
410   GstXImageBuffer *ximage = NULL;
411   GstStructure *structure = NULL;
412   gboolean succeeded = FALSE;
413   int (*handler) (Display *, XErrorEvent *);
414
415   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
416
417   ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
418
419   structure = gst_caps_get_structure (caps, 0);
420
421   if (!gst_structure_get_int (structure, "width", &ximage->width) ||
422       !gst_structure_get_int (structure, "height", &ximage->height)) {
423     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
424   }
425
426   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
427       ximage->width, ximage->height);
428
429   g_mutex_lock (ximagesink->x_lock);
430
431   /* Setting an error handler to catch failure */
432   error_caught = FALSE;
433   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
434
435 #ifdef HAVE_XSHM
436   if (ximagesink->xcontext->use_xshm) {
437     ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
438         ximagesink->xcontext->visual,
439         ximagesink->xcontext->depth,
440         ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
441     if (!ximage->ximage || error_caught) {
442       g_mutex_unlock (ximagesink->x_lock);
443       /* Reset error handler */
444       error_caught = FALSE;
445       XSetErrorHandler (handler);
446       /* Push an error */
447       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
448           ("Failed to create output image buffer of %dx%d pixels",
449               ximage->width, ximage->height),
450           ("could not XShmCreateImage a %dx%d image",
451               ximage->width, ximage->height));
452       goto beach;
453     }
454
455     /* we have to use the returned bytes_per_line for our shm size */
456     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
457     GST_LOG_OBJECT (ximagesink,
458         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
459         ximage->size, ximage->width, ximage->ximage->bytes_per_line);
460
461     ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
462         IPC_CREAT | 0777);
463     if (ximage->SHMInfo.shmid == -1) {
464       g_mutex_unlock (ximagesink->x_lock);
465       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
466           ("Failed to create output image buffer of %dx%d pixels",
467               ximage->width, ximage->height),
468           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
469               ximage->size));
470       goto beach;
471     }
472
473     ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
474     if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
475       g_mutex_unlock (ximagesink->x_lock);
476       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
477           ("Failed to create output image buffer of %dx%d pixels",
478               ximage->width, ximage->height),
479           ("Failed to shmat: %s", g_strerror (errno)));
480       /* Clean up the shared memory segment */
481       shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
482       goto beach;
483     }
484
485     ximage->ximage->data = ximage->SHMInfo.shmaddr;
486     ximage->SHMInfo.readOnly = FALSE;
487
488     if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
489       /* Clean up shm seg */
490       shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
491
492       g_mutex_unlock (ximagesink->x_lock);
493       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
494           ("Failed to create output image buffer of %dx%d pixels",
495               ximage->width, ximage->height), ("Failed to XShmAttach"));
496       goto beach;
497     }
498
499     XSync (ximagesink->xcontext->disp, FALSE);
500
501     /* Now that everyone has attached, we can delete the shared memory segment.
502      * This way, it will be deleted as soon as we detach later, and not
503      * leaked if we crash. */
504     shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
505
506   } else
507 #endif /* HAVE_XSHM */
508   {
509     guint allocsize;
510
511     ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
512         ximagesink->xcontext->visual,
513         ximagesink->xcontext->depth,
514         ZPixmap, 0, NULL,
515         ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
516     if (!ximage->ximage || error_caught) {
517       g_mutex_unlock (ximagesink->x_lock);
518       /* Reset error handler */
519       error_caught = FALSE;
520       XSetErrorHandler (handler);
521       /* Push an error */
522       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
523           ("Failed to create output image buffer of %dx%d pixels",
524               ximage->width, ximage->height),
525           ("could not XCreateImage a %dx%d image",
526               ximage->width, ximage->height));
527       goto beach;
528     }
529
530     /* upstream will assume that rowstrides are multiples of 4, but this
531      * doesn't always seem to be the case with XCreateImage() */
532     if ((ximage->ximage->bytes_per_line % 4) != 0) {
533       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
534           "usually assumed");
535     }
536
537     /* we have to use the returned bytes_per_line for our image size */
538     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
539
540     /* alloc a bit more for unexpected strides to avoid crashes upstream.
541      * FIXME: if we get an unrounded stride, the image will be displayed
542      * distorted, since all upstream elements assume a rounded stride */
543     allocsize =
544         GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
545         ximage->ximage->height;
546     ximage->ximage->data = g_malloc (allocsize);
547     GST_LOG_OBJECT (ximagesink,
548         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
549         "stride %d", ximage->size, allocsize, ximage->width,
550         ximage->ximage->bytes_per_line);
551
552     XSync (ximagesink->xcontext->disp, FALSE);
553   }
554
555   /* Reset error handler */
556   error_caught = FALSE;
557   XSetErrorHandler (handler);
558
559   succeeded = TRUE;
560
561   GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
562   GST_BUFFER_SIZE (ximage) = ximage->size;
563
564   /* Keep a ref to our sink */
565   ximage->ximagesink = gst_object_ref (ximagesink);
566
567   g_mutex_unlock (ximagesink->x_lock);
568 beach:
569   if (!succeeded) {
570     gst_ximage_buffer_free (ximage);
571     ximage = NULL;
572   }
573
574   return ximage;
575 }
576
577 /* This function destroys a GstXImageBuffer handling XShm availability */
578 static void
579 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
580     GstXImageBuffer * ximage)
581 {
582   g_return_if_fail (ximage != NULL);
583   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
584
585   /* Hold the object lock to ensure the XContext doesn't disappear */
586   GST_OBJECT_LOCK (ximagesink);
587
588   /* If the destroyed image is the current one we destroy our reference too */
589   if (ximagesink->cur_image == ximage) {
590     ximagesink->cur_image = NULL;
591   }
592
593   /* We might have some buffers destroyed after changing state to NULL */
594   if (!ximagesink->xcontext) {
595     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
596 #ifdef HAVE_XSHM
597     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
598       shmdt (ximage->SHMInfo.shmaddr);
599     }
600 #endif
601     goto beach;
602   }
603
604   g_mutex_lock (ximagesink->x_lock);
605
606 #ifdef HAVE_XSHM
607   if (ximagesink->xcontext->use_xshm) {
608     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
609       XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
610       XSync (ximagesink->xcontext->disp, 0);
611       shmdt (ximage->SHMInfo.shmaddr);
612     }
613     if (ximage->ximage)
614       XDestroyImage (ximage->ximage);
615
616   } else
617 #endif /* HAVE_XSHM */
618   {
619     if (ximage->ximage) {
620       XDestroyImage (ximage->ximage);
621     }
622   }
623
624   XSync (ximagesink->xcontext->disp, FALSE);
625
626   g_mutex_unlock (ximagesink->x_lock);
627
628 beach:
629   GST_OBJECT_UNLOCK (ximagesink);
630
631   if (ximage->ximagesink) {
632     /* Release the ref to our sink */
633     ximage->ximagesink = NULL;
634     gst_object_unref (ximagesink);
635   }
636
637   return;
638 }
639
640 /* We are called with the x_lock taken */
641 static void
642 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
643     GstXWindow * xwindow, GstVideoRectangle rect)
644 {
645   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
646   g_return_if_fail (xwindow != NULL);
647
648   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
649       ximagesink->xcontext->black);
650
651   /* Left border */
652   if (rect.x > 0) {
653     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
654         0, 0, rect.x, xwindow->height);
655   }
656
657   /* Right border */
658   if ((rect.x + rect.w) < xwindow->width) {
659     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
660         rect.x + rect.w, 0, xwindow->width, xwindow->height);
661   }
662
663   /* Top border */
664   if (rect.y > 0) {
665     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
666         0, 0, xwindow->width, rect.y);
667   }
668
669   /* Bottom border */
670   if ((rect.y + rect.h) < xwindow->height) {
671     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
672         0, rect.y + rect.h, xwindow->width, xwindow->height);
673   }
674 }
675
676 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
677 static gboolean
678 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
679 {
680   GstVideoRectangle src, dst, result;
681   gboolean draw_border = FALSE;
682
683   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
684
685   /* We take the flow_lock. If expose is in there we don't want to run
686      concurrently from the data flow thread */
687   g_mutex_lock (ximagesink->flow_lock);
688
689   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
690     g_mutex_unlock (ximagesink->flow_lock);
691     return FALSE;
692   }
693
694   /* Draw borders when displaying the first frame. After this
695      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
696   if (!ximagesink->cur_image || ximagesink->draw_border) {
697     draw_border = TRUE;
698   }
699
700   /* Store a reference to the last image we put, lose the previous one */
701   if (ximage && ximagesink->cur_image != ximage) {
702     if (ximagesink->cur_image) {
703       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
704       gst_buffer_unref (ximagesink->cur_image);
705     }
706     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
707     ximagesink->cur_image =
708         GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
709   }
710
711   /* Expose sends a NULL image, we take the latest frame */
712   if (!ximage) {
713     draw_border = TRUE;
714     if (ximagesink->cur_image) {
715       ximage = ximagesink->cur_image;
716     } else {
717       g_mutex_unlock (ximagesink->flow_lock);
718       return TRUE;
719     }
720   }
721
722   gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
723
724   src.w = ximage->width;
725   src.h = ximage->height;
726   dst.w = ximagesink->xwindow->width;
727   dst.h = ximagesink->xwindow->height;
728
729   gst_video_sink_center_rect (src, dst, &result, FALSE);
730
731   g_mutex_lock (ximagesink->x_lock);
732
733   if (draw_border) {
734     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
735         result);
736     ximagesink->draw_border = FALSE;
737   }
738 #ifdef HAVE_XSHM
739   if (ximagesink->xcontext->use_xshm) {
740     GST_LOG_OBJECT (ximagesink,
741         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
742         ximage, 0, 0, result.x, result.y, result.w, result.h,
743         ximagesink->xwindow->width, ximagesink->xwindow->height);
744     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
745         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
746         result.w, result.h, FALSE);
747   } else
748 #endif /* HAVE_XSHM */
749   {
750     GST_LOG_OBJECT (ximagesink,
751         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
752         ximage, 0, 0, result.x, result.y, result.w, result.h,
753         ximagesink->xwindow->width, ximagesink->xwindow->height);
754     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
755         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
756         result.w, result.h);
757   }
758
759   XSync (ximagesink->xcontext->disp, FALSE);
760
761   g_mutex_unlock (ximagesink->x_lock);
762
763   g_mutex_unlock (ximagesink->flow_lock);
764
765   return TRUE;
766 }
767
768 static gboolean
769 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
770     GstXWindow * window)
771 {
772   Atom hints_atom = None;
773   MotifWmHints *hints;
774
775   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
776   g_return_val_if_fail (window != NULL, FALSE);
777
778   g_mutex_lock (ximagesink->x_lock);
779
780   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
781   if (hints_atom == None) {
782     g_mutex_unlock (ximagesink->x_lock);
783     return FALSE;
784   }
785
786   hints = g_malloc0 (sizeof (MotifWmHints));
787
788   hints->flags |= MWM_HINTS_DECORATIONS;
789   hints->decorations = 1 << 0;
790
791   XChangeProperty (ximagesink->xcontext->disp, window->win,
792       hints_atom, hints_atom, 32, PropModeReplace,
793       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
794
795   XSync (ximagesink->xcontext->disp, FALSE);
796
797   g_mutex_unlock (ximagesink->x_lock);
798
799   g_free (hints);
800
801   return TRUE;
802 }
803
804 /* This function handles a GstXWindow creation */
805 static GstXWindow *
806 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
807 {
808   GstXWindow *xwindow = NULL;
809   XGCValues values;
810
811   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
812
813   xwindow = g_new0 (GstXWindow, 1);
814
815   xwindow->width = width;
816   xwindow->height = height;
817   xwindow->internal = TRUE;
818
819   g_mutex_lock (ximagesink->x_lock);
820
821   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
822       ximagesink->xcontext->root,
823       0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
824
825   /* We have to do that to prevent X from redrawing the background on 
826      ConfigureNotify. This takes away flickering of video when resizing. */
827   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
828
829   if (ximagesink->handle_events) {
830     Atom wm_delete;
831
832     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
833         StructureNotifyMask | PointerMotionMask | KeyPressMask |
834         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
835
836     /* Tell the window manager we'd like delete client messages instead of
837      * being killed */
838     wm_delete = XInternAtom (ximagesink->xcontext->disp,
839         "WM_DELETE_WINDOW", False);
840     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
841         &wm_delete, 1);
842   }
843
844   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
845       0, &values);
846
847   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
848
849   XSync (ximagesink->xcontext->disp, FALSE);
850
851   g_mutex_unlock (ximagesink->x_lock);
852
853   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
854
855   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
856
857   return xwindow;
858 }
859
860 /* This function destroys a GstXWindow */
861 static void
862 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
863     GstXWindow * xwindow)
864 {
865   g_return_if_fail (xwindow != NULL);
866   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
867
868   g_mutex_lock (ximagesink->x_lock);
869
870   /* If we did not create that window we just free the GC and let it live */
871   if (xwindow->internal)
872     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
873   else
874     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
875
876   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
877
878   XSync (ximagesink->xcontext->disp, FALSE);
879
880   g_mutex_unlock (ximagesink->x_lock);
881
882   g_free (xwindow);
883 }
884
885 static void
886 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
887     GstXWindow * xwindow)
888 {
889   XWindowAttributes attr;
890
891   g_return_if_fail (xwindow != NULL);
892   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
893
894   /* Update the window geometry */
895   g_mutex_lock (ximagesink->x_lock);
896
897   XGetWindowAttributes (ximagesink->xcontext->disp,
898       ximagesink->xwindow->win, &attr);
899
900   ximagesink->xwindow->width = attr.width;
901   ximagesink->xwindow->height = attr.height;
902
903   g_mutex_unlock (ximagesink->x_lock);
904 }
905
906 static void
907 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
908 {
909   g_return_if_fail (xwindow != NULL);
910   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
911
912   g_mutex_lock (ximagesink->x_lock);
913
914   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
915       ximagesink->xcontext->black);
916
917   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
918       0, 0, xwindow->width, xwindow->height);
919
920   XSync (ximagesink->xcontext->disp, FALSE);
921
922   g_mutex_unlock (ximagesink->x_lock);
923 }
924
925 /* This function handles XEvents that might be in the queue. It generates
926    GstEvent that will be sent upstream in the pipeline to handle interactivity
927    and navigation.*/
928 static void
929 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
930 {
931   XEvent e;
932   guint pointer_x = 0, pointer_y = 0;
933   gboolean pointer_moved = FALSE;
934   gboolean exposed = FALSE, configured = FALSE;
935
936   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
937
938   /* Then we get all pointer motion events, only the last position is
939      interesting. */
940   g_mutex_lock (ximagesink->flow_lock);
941   g_mutex_lock (ximagesink->x_lock);
942   while (XCheckWindowEvent (ximagesink->xcontext->disp,
943           ximagesink->xwindow->win, PointerMotionMask, &e)) {
944     g_mutex_unlock (ximagesink->x_lock);
945     g_mutex_unlock (ximagesink->flow_lock);
946
947     switch (e.type) {
948       case MotionNotify:
949         pointer_x = e.xmotion.x;
950         pointer_y = e.xmotion.y;
951         pointer_moved = TRUE;
952         break;
953       default:
954         break;
955     }
956     g_mutex_lock (ximagesink->flow_lock);
957     g_mutex_lock (ximagesink->x_lock);
958   }
959
960   if (pointer_moved) {
961     g_mutex_unlock (ximagesink->x_lock);
962     g_mutex_unlock (ximagesink->flow_lock);
963
964     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
965         pointer_x, pointer_y);
966     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
967         "mouse-move", 0, pointer_x, pointer_y);
968
969     g_mutex_lock (ximagesink->flow_lock);
970     g_mutex_lock (ximagesink->x_lock);
971   }
972
973   /* We get all remaining events on our window to throw them upstream */
974   while (XCheckWindowEvent (ximagesink->xcontext->disp,
975           ximagesink->xwindow->win,
976           KeyPressMask | KeyReleaseMask |
977           ButtonPressMask | ButtonReleaseMask, &e)) {
978     KeySym keysym;
979
980     /* We lock only for the X function call */
981     g_mutex_unlock (ximagesink->x_lock);
982     g_mutex_unlock (ximagesink->flow_lock);
983
984     switch (e.type) {
985       case ButtonPress:
986         /* Mouse button pressed/released over our window. We send upstream
987            events for interactivity/navigation */
988         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
989             e.xbutton.button, e.xbutton.x, e.xbutton.x);
990         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
991             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
992         break;
993       case ButtonRelease:
994         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
995             e.xbutton.button, e.xbutton.x, e.xbutton.x);
996         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
997             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
998         break;
999       case KeyPress:
1000       case KeyRelease:
1001         /* Key pressed/released over our window. We send upstream
1002            events for interactivity/navigation */
1003         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1004             e.xkey.keycode, e.xkey.x, e.xkey.x);
1005         g_mutex_lock (ximagesink->x_lock);
1006         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1007             e.xkey.keycode, 0);
1008         g_mutex_unlock (ximagesink->x_lock);
1009         if (keysym != NoSymbol) {
1010           char *key_str = NULL;
1011
1012           g_mutex_lock (ximagesink->x_lock);
1013           key_str = XKeysymToString (keysym);
1014           g_mutex_unlock (ximagesink->x_lock);
1015           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1016               e.type == KeyPress ? "key-press" : "key-release", key_str);
1017
1018         } else {
1019           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1020               e.type == KeyPress ? "key-press" : "key-release", "unknown");
1021         }
1022         break;
1023       default:
1024         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1025             e.type);
1026     }
1027     g_mutex_lock (ximagesink->flow_lock);
1028     g_mutex_lock (ximagesink->x_lock);
1029   }
1030
1031   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1032           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1033     switch (e.type) {
1034       case Expose:
1035         exposed = TRUE;
1036         break;
1037       case ConfigureNotify:
1038         configured = TRUE;
1039         break;
1040       default:
1041         break;
1042     }
1043   }
1044
1045   if (ximagesink->handle_expose && (exposed || configured)) {
1046     g_mutex_unlock (ximagesink->x_lock);
1047     g_mutex_unlock (ximagesink->flow_lock);
1048
1049     gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1050
1051     g_mutex_lock (ximagesink->flow_lock);
1052     g_mutex_lock (ximagesink->x_lock);
1053   }
1054
1055   /* Handle Display events */
1056   while (XPending (ximagesink->xcontext->disp)) {
1057     XNextEvent (ximagesink->xcontext->disp, &e);
1058
1059     switch (e.type) {
1060       case ClientMessage:{
1061         Atom wm_delete;
1062
1063         wm_delete = XInternAtom (ximagesink->xcontext->disp,
1064             "WM_DELETE_WINDOW", False);
1065         if (wm_delete == (Atom) e.xclient.data.l[0]) {
1066           /* Handle window deletion by posting an error on the bus */
1067           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1068               ("Output window was closed"), (NULL));
1069
1070           g_mutex_unlock (ximagesink->x_lock);
1071           gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1072           ximagesink->xwindow = NULL;
1073           g_mutex_lock (ximagesink->x_lock);
1074         }
1075         break;
1076       }
1077       default:
1078         break;
1079     }
1080   }
1081
1082   g_mutex_unlock (ximagesink->x_lock);
1083   g_mutex_unlock (ximagesink->flow_lock);
1084 }
1085
1086 static gpointer
1087 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1088 {
1089   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1090
1091   GST_OBJECT_LOCK (ximagesink);
1092   while (ximagesink->running) {
1093     GST_OBJECT_UNLOCK (ximagesink);
1094
1095     if (ximagesink->xwindow) {
1096       gst_ximagesink_handle_xevents (ximagesink);
1097     }
1098     g_usleep (100000);
1099
1100     GST_OBJECT_LOCK (ximagesink);
1101   }
1102   GST_OBJECT_UNLOCK (ximagesink);
1103
1104   return NULL;
1105 }
1106
1107 /* This function calculates the pixel aspect ratio based on the properties
1108  * in the xcontext structure and stores it there. */
1109 static void
1110 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1111 {
1112   static const gint par[][2] = {
1113     {1, 1},                     /* regular screen */
1114     {16, 15},                   /* PAL TV */
1115     {11, 10},                   /* 525 line Rec.601 video */
1116     {54, 59},                   /* 625 line Rec.601 video */
1117     {64, 45},                   /* 1280x1024 on 16:9 display */
1118     {5, 3},                     /* 1280x1024 on 4:3 display */
1119     {4, 3}                      /*  800x600 on 16:9 display */
1120   };
1121   gint i;
1122   gint index;
1123   gdouble ratio;
1124   gdouble delta;
1125
1126 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1127
1128   /* first calculate the "real" ratio based on the X values;
1129    * which is the "physical" w/h divided by the w/h in pixels of the display */
1130   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1131       / (xcontext->heightmm * xcontext->width);
1132
1133   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1134    * override here */
1135   if (xcontext->width == 720 && xcontext->height == 576) {
1136     ratio = 4.0 * 576 / (3.0 * 720);
1137   }
1138   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1139
1140   /* now find the one from par[][2] with the lowest delta to the real one */
1141   delta = DELTA (0);
1142   index = 0;
1143
1144   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1145     gdouble this_delta = DELTA (i);
1146
1147     if (this_delta < delta) {
1148       index = i;
1149       delta = this_delta;
1150     }
1151   }
1152
1153   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1154       par[index][0], par[index][1]);
1155
1156   g_free (xcontext->par);
1157   xcontext->par = g_new0 (GValue, 1);
1158   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1159   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1160   GST_DEBUG ("set xcontext PAR to %d/%d",
1161       gst_value_get_fraction_numerator (xcontext->par),
1162       gst_value_get_fraction_denominator (xcontext->par));
1163 }
1164
1165 /* This function gets the X Display and global info about it. Everything is
1166    stored in our object and will be cleaned when the object is disposed. Note
1167    here that caps for supported format are generated without any window or
1168    image creation */
1169 static GstXContext *
1170 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1171 {
1172   GstXContext *xcontext = NULL;
1173   XPixmapFormatValues *px_formats = NULL;
1174   gint nb_formats = 0, i;
1175
1176   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1177
1178   xcontext = g_new0 (GstXContext, 1);
1179
1180   g_mutex_lock (ximagesink->x_lock);
1181
1182   xcontext->disp = XOpenDisplay (ximagesink->display_name);
1183
1184   if (!xcontext->disp) {
1185     g_mutex_unlock (ximagesink->x_lock);
1186     g_free (xcontext);
1187     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1188         ("Could not initialise X output"), ("Could not open display"));
1189     return NULL;
1190   }
1191
1192   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1193   xcontext->screen_num = DefaultScreen (xcontext->disp);
1194   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1195   xcontext->root = DefaultRootWindow (xcontext->disp);
1196   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1197   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1198   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1199
1200   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1201   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1202   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1203   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1204
1205   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1206       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1207
1208   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1209
1210   /* We get supported pixmap formats at supported depth */
1211   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1212
1213   if (!px_formats) {
1214     XCloseDisplay (xcontext->disp);
1215     g_mutex_unlock (ximagesink->x_lock);
1216     g_free (xcontext->par);
1217     g_free (xcontext);
1218     return NULL;
1219   }
1220
1221   /* We get bpp value corresponding to our running depth */
1222   for (i = 0; i < nb_formats; i++) {
1223     if (px_formats[i].depth == xcontext->depth)
1224       xcontext->bpp = px_formats[i].bits_per_pixel;
1225   }
1226
1227   XFree (px_formats);
1228
1229   xcontext->endianness =
1230       (ImageByteOrder (xcontext->disp) ==
1231       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1232
1233   /* Search for XShm extension support */
1234 #ifdef HAVE_XSHM
1235   if (XShmQueryExtension (xcontext->disp) &&
1236       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1237     xcontext->use_xshm = TRUE;
1238     GST_DEBUG ("ximagesink is using XShm extension");
1239   } else
1240 #endif
1241   {
1242     xcontext->use_xshm = FALSE;
1243     GST_DEBUG ("ximagesink is not using XShm extension");
1244   }
1245
1246   /* our caps system handles 24/32bpp RGB as big-endian. */
1247   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1248       xcontext->endianness == G_LITTLE_ENDIAN) {
1249     xcontext->endianness = G_BIG_ENDIAN;
1250     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1251     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1252     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1253     if (xcontext->bpp == 24) {
1254       xcontext->visual->red_mask >>= 8;
1255       xcontext->visual->green_mask >>= 8;
1256       xcontext->visual->blue_mask >>= 8;
1257     }
1258   }
1259
1260   /* update object's par with calculated one if not set yet */
1261   if (!ximagesink->par) {
1262     ximagesink->par = g_new0 (GValue, 1);
1263     gst_value_init_and_copy (ximagesink->par, xcontext->par);
1264     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1265   }
1266   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1267       "bpp", G_TYPE_INT, xcontext->bpp,
1268       "depth", G_TYPE_INT, xcontext->depth,
1269       "endianness", G_TYPE_INT, xcontext->endianness,
1270       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1271       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1272       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1273       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1274       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1275       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1276   if (ximagesink->par) {
1277     int nom, den;
1278
1279     nom = gst_value_get_fraction_numerator (ximagesink->par);
1280     den = gst_value_get_fraction_denominator (ximagesink->par);
1281     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1282         GST_TYPE_FRACTION, nom, den, NULL);
1283   }
1284
1285   g_mutex_unlock (ximagesink->x_lock);
1286
1287   /* Setup our event listening thread */
1288   GST_OBJECT_LOCK (ximagesink);
1289   ximagesink->running = TRUE;
1290   ximagesink->event_thread = g_thread_create (
1291       (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1292   GST_OBJECT_UNLOCK (ximagesink);
1293
1294   return xcontext;
1295 }
1296
1297 /* This function cleans the X context. Closing the Display and unrefing the
1298    caps for supported formats. */
1299 static void
1300 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1301 {
1302   GstXContext *xcontext;
1303
1304   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1305
1306   GST_OBJECT_LOCK (ximagesink);
1307   if (ximagesink->xcontext == NULL) {
1308     GST_OBJECT_UNLOCK (ximagesink);
1309     return;
1310   }
1311
1312   /* Take the xcontext reference and NULL it while we
1313    * clean it up, so that any buffer-alloced buffers 
1314    * arriving after this will be freed correctly */
1315   xcontext = ximagesink->xcontext;
1316   ximagesink->xcontext = NULL;
1317
1318   GST_OBJECT_UNLOCK (ximagesink);
1319
1320   gst_caps_unref (xcontext->caps);
1321   g_free (xcontext->par);
1322   g_free (ximagesink->par);
1323   ximagesink->par = NULL;
1324
1325   g_mutex_lock (ximagesink->x_lock);
1326
1327   XCloseDisplay (xcontext->disp);
1328
1329   g_mutex_unlock (ximagesink->x_lock);
1330
1331   g_free (xcontext);
1332 }
1333
1334 static void
1335 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1336 {
1337
1338   g_mutex_lock (ximagesink->pool_lock);
1339
1340   while (ximagesink->buffer_pool) {
1341     GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1342
1343     ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1344         ximagesink->buffer_pool);
1345     gst_ximage_buffer_free (ximage);
1346   }
1347
1348   g_mutex_unlock (ximagesink->pool_lock);
1349 }
1350
1351 /* Element stuff */
1352
1353 static GstCaps *
1354 gst_ximagesink_getcaps (GstBaseSink * bsink)
1355 {
1356   GstXImageSink *ximagesink;
1357   GstCaps *caps;
1358   int i;
1359
1360   ximagesink = GST_XIMAGESINK (bsink);
1361
1362   if (ximagesink->xcontext)
1363     return gst_caps_ref (ximagesink->xcontext->caps);
1364
1365   /* get a template copy and add the pixel aspect ratio */
1366   caps =
1367       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1368           (ximagesink)->sinkpad));
1369   for (i = 0; i < gst_caps_get_size (caps); ++i) {
1370     GstStructure *structure = gst_caps_get_structure (caps, i);
1371
1372     if (ximagesink->par) {
1373       int nom, den;
1374
1375       nom = gst_value_get_fraction_numerator (ximagesink->par);
1376       den = gst_value_get_fraction_denominator (ximagesink->par);
1377       gst_structure_set (structure, "pixel-aspect-ratio",
1378           GST_TYPE_FRACTION, nom, den, NULL);
1379     }
1380   }
1381
1382   return caps;
1383 }
1384
1385 static gboolean
1386 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1387 {
1388   GstXImageSink *ximagesink;
1389   gboolean ret = TRUE;
1390   GstStructure *structure;
1391   GstCaps *intersection;
1392   const GValue *par;
1393   gint new_width, new_height;
1394   const GValue *fps;
1395
1396   ximagesink = GST_XIMAGESINK (bsink);
1397
1398   if (!ximagesink->xcontext)
1399     return FALSE;
1400
1401   GST_DEBUG_OBJECT (ximagesink,
1402       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1403       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1404
1405   /* We intersect those caps with our template to make sure they are correct */
1406   intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1407   GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1408       intersection);
1409   if (gst_caps_is_empty (intersection)) {
1410     gst_caps_unref (intersection);
1411     return FALSE;
1412   }
1413
1414   gst_caps_unref (intersection);
1415
1416   structure = gst_caps_get_structure (caps, 0);
1417
1418   ret &= gst_structure_get_int (structure, "width", &new_width);
1419   ret &= gst_structure_get_int (structure, "height", &new_height);
1420   fps = gst_structure_get_value (structure, "framerate");
1421   ret &= (fps != NULL);
1422   if (!ret)
1423     return FALSE;
1424
1425   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1426    * otherwise linking should fail */
1427   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1428   if (par) {
1429     if (ximagesink->par) {
1430       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1431         goto wrong_aspect;
1432       }
1433     } else if (ximagesink->xcontext->par) {
1434       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1435         goto wrong_aspect;
1436       }
1437     }
1438   }
1439
1440   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1441   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1442   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1443   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1444
1445   /* Notify application to set xwindow id now */
1446   g_mutex_lock (ximagesink->flow_lock);
1447   if (!ximagesink->xwindow) {
1448     g_mutex_unlock (ximagesink->flow_lock);
1449     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1450   } else {
1451     g_mutex_unlock (ximagesink->flow_lock);
1452   }
1453
1454   /* Creating our window and our image */
1455   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1456       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1457     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1458         ("Invalid image size."));
1459     return FALSE;
1460   }
1461
1462   g_mutex_lock (ximagesink->flow_lock);
1463   if (!ximagesink->xwindow) {
1464     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1465         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1466   }
1467   /* Remember to draw borders for next frame */
1468   ximagesink->draw_border = TRUE;
1469   g_mutex_unlock (ximagesink->flow_lock);
1470
1471   /* If our ximage has changed we destroy it, next chain iteration will create
1472      a new one */
1473   if ((ximagesink->ximage) &&
1474       ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1475           (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1476     GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1477         ximagesink->ximage);
1478     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1479     ximagesink->ximage = NULL;
1480   }
1481
1482   return TRUE;
1483
1484   /* ERRORS */
1485 wrong_aspect:
1486   {
1487     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1488     return FALSE;
1489   }
1490 }
1491
1492 static GstStateChangeReturn
1493 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1494 {
1495   GstXImageSink *ximagesink;
1496   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1497   GstXContext *xcontext = NULL;
1498
1499   ximagesink = GST_XIMAGESINK (element);
1500
1501   switch (transition) {
1502     case GST_STATE_CHANGE_NULL_TO_READY:
1503
1504       /* Initializing the XContext */
1505       if (ximagesink->xcontext == NULL) {
1506         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1507         if (xcontext == NULL) {
1508           ret = GST_STATE_CHANGE_FAILURE;
1509           goto beach;
1510         }
1511         GST_OBJECT_LOCK (ximagesink);
1512         if (xcontext)
1513           ximagesink->xcontext = xcontext;
1514         GST_OBJECT_UNLOCK (ximagesink);
1515       }
1516
1517       /* call XSynchronize with the current value of synchronous */
1518       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1519           ximagesink->synchronous ? "TRUE" : "FALSE");
1520       g_mutex_lock (ximagesink->x_lock);
1521       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1522       g_mutex_unlock (ximagesink->x_lock);
1523       break;
1524     case GST_STATE_CHANGE_READY_TO_PAUSED:
1525       g_mutex_lock (ximagesink->flow_lock);
1526       if (ximagesink->xwindow)
1527         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1528       g_mutex_unlock (ximagesink->flow_lock);
1529       break;
1530     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1531       break;
1532     default:
1533       break;
1534   }
1535
1536   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1537
1538   switch (transition) {
1539     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1540       break;
1541     case GST_STATE_CHANGE_PAUSED_TO_READY:
1542       ximagesink->fps_n = 0;
1543       ximagesink->fps_d = 1;
1544       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1545       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1546       break;
1547     case GST_STATE_CHANGE_READY_TO_NULL:
1548       gst_ximagesink_reset (ximagesink);
1549       break;
1550     default:
1551       break;
1552   }
1553
1554 beach:
1555   return ret;
1556 }
1557
1558 static void
1559 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1560     GstClockTime * start, GstClockTime * end)
1561 {
1562   GstXImageSink *ximagesink;
1563
1564   ximagesink = GST_XIMAGESINK (bsink);
1565
1566   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1567     *start = GST_BUFFER_TIMESTAMP (buf);
1568     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1569       *end = *start + GST_BUFFER_DURATION (buf);
1570     } else {
1571       if (ximagesink->fps_n > 0) {
1572         *end = *start +
1573             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1574             ximagesink->fps_n);
1575       }
1576     }
1577   }
1578 }
1579
1580 static GstFlowReturn
1581 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1582 {
1583   GstXImageSink *ximagesink;
1584
1585   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1586
1587   ximagesink = GST_XIMAGESINK (bsink);
1588
1589   /* If this buffer has been allocated using our buffer management we simply
1590      put the ximage which is in the PRIVATE pointer */
1591   if (GST_IS_XIMAGE_BUFFER (buf)) {
1592     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1593     if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1594       goto no_window;
1595   } else {
1596     /* Else we have to copy the data into our private image, */
1597     /* if we have one... */
1598     GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1599     if (!ximagesink->ximage) {
1600       GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1601       ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1602           GST_BUFFER_CAPS (buf));
1603       if (!ximagesink->ximage)
1604         /* The create method should have posted an informative error */
1605         goto no_ximage;
1606
1607       if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1608         GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1609             ("Failed to create output image buffer of %dx%d pixels",
1610                 ximagesink->ximage->width, ximagesink->ximage->height),
1611             ("XServer allocated buffer size did not match input buffer"));
1612
1613         gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1614         ximagesink->ximage = NULL;
1615         goto no_ximage;
1616       }
1617     }
1618     memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1619         MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1620     if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1621       goto no_window;
1622   }
1623
1624   return GST_FLOW_OK;
1625
1626   /* ERRORS */
1627 no_ximage:
1628   {
1629     /* No image available. That's very bad ! */
1630     GST_DEBUG ("could not create image");
1631     return GST_FLOW_ERROR;
1632   }
1633 no_window:
1634   {
1635     /* No Window available to put our image into */
1636     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1637     return GST_FLOW_ERROR;
1638   }
1639 }
1640
1641 /* Buffer management
1642  *
1643  * The buffer_alloc function must either return a buffer with given size and
1644  * caps or create a buffer with different caps attached to the buffer. This
1645  * last option is called reverse negotiation, ie, where the sink suggests a
1646  * different format from the upstream peer. 
1647  *
1648  * We try to do reverse negotiation when our geometry changes and we like a
1649  * resized buffer.
1650  */
1651 static GstFlowReturn
1652 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1653     GstCaps * caps, GstBuffer ** buf)
1654 {
1655   GstXImageSink *ximagesink;
1656   GstXImageBuffer *ximage = NULL;
1657   GstStructure *structure = NULL;
1658   GstFlowReturn ret = GST_FLOW_OK;
1659   GstCaps *alloc_caps;
1660   gboolean alloc_unref = FALSE;
1661   gint width, height;
1662
1663   ximagesink = GST_XIMAGESINK (bsink);
1664
1665   GST_LOG_OBJECT (ximagesink,
1666       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1667       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1668
1669   /* assume we're going to alloc what was requested, keep track of
1670    * wheter we need to unref or not. When we suggest a new format 
1671    * upstream we will create a new caps that we need to unref. */
1672   alloc_caps = caps;
1673   alloc_unref = FALSE;
1674
1675   /* get struct to see what is requested */
1676   structure = gst_caps_get_structure (caps, 0);
1677
1678   if (gst_structure_get_int (structure, "width", &width) &&
1679       gst_structure_get_int (structure, "height", &height)) {
1680     GstVideoRectangle dst, src, result;
1681
1682     src.w = width;
1683     src.h = height;
1684
1685     /* We take the flow_lock because the window might go away */
1686     g_mutex_lock (ximagesink->flow_lock);
1687     if (!ximagesink->xwindow) {
1688       g_mutex_unlock (ximagesink->flow_lock);
1689       goto alloc;
1690     }
1691
1692     /* What is our geometry */
1693     gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1694     dst.w = ximagesink->xwindow->width;
1695     dst.h = ximagesink->xwindow->height;
1696
1697     g_mutex_unlock (ximagesink->flow_lock);
1698
1699     if (ximagesink->keep_aspect) {
1700       GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1701           "negotiation");
1702       gst_video_sink_center_rect (src, dst, &result, TRUE);
1703     } else {
1704       GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1705           "ignoring aspect ratio");
1706       result.x = result.y = 0;
1707       result.w = dst.w;
1708       result.h = dst.h;
1709     }
1710
1711     /* We would like another geometry */
1712     if (width != result.w || height != result.h) {
1713       int nom, den;
1714       GstCaps *desired_caps;
1715       GstStructure *desired_struct;
1716
1717       /* make a copy of the incomming caps to create the new
1718        * suggestion. We can't use make_writable because we might
1719        * then destroy the original caps which we still need when the
1720        * peer does not accept the suggestion. */
1721       desired_caps = gst_caps_copy (caps);
1722       desired_struct = gst_caps_get_structure (desired_caps, 0);
1723
1724       GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1725       gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1726       gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1727
1728       /* PAR property overrides the X calculated one */
1729       if (ximagesink->par) {
1730         nom = gst_value_get_fraction_numerator (ximagesink->par);
1731         den = gst_value_get_fraction_denominator (ximagesink->par);
1732         gst_structure_set (desired_struct, "pixel-aspect-ratio",
1733             GST_TYPE_FRACTION, nom, den, NULL);
1734       } else if (ximagesink->xcontext->par) {
1735         nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1736         den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1737         gst_structure_set (desired_struct, "pixel-aspect-ratio",
1738             GST_TYPE_FRACTION, nom, den, NULL);
1739       }
1740
1741       /* see if peer accepts our new suggestion, if there is no peer, this 
1742        * function returns true. */
1743       if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1744               desired_caps)) {
1745         /* we will not alloc a buffer of the new suggested caps. Make sure
1746          * we also unref this new caps after we set it on the buffer. */
1747         alloc_caps = desired_caps;
1748         alloc_unref = TRUE;
1749         width = result.w;
1750         height = result.h;
1751         GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1752             desired_caps);
1753       } else {
1754         GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1755             desired_caps);
1756         /* we alloc a buffer with the original incomming caps already in the
1757          * width and height variables */
1758       }
1759     }
1760   }
1761
1762 alloc:
1763   /* Inspect our buffer pool */
1764   g_mutex_lock (ximagesink->pool_lock);
1765   while (ximagesink->buffer_pool) {
1766     ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1767
1768     if (ximage) {
1769       /* Removing from the pool */
1770       ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1771           ximagesink->buffer_pool);
1772
1773       /* If the ximage is invalid for our need, destroy */
1774       if ((ximage->width != width) || (ximage->height != height)) {
1775         gst_ximage_buffer_free (ximage);
1776         ximage = NULL;
1777       } else {
1778         /* We found a suitable ximage */
1779         break;
1780       }
1781     }
1782   }
1783   g_mutex_unlock (ximagesink->pool_lock);
1784
1785   /* We haven't found anything, creating a new one */
1786   if (!ximage) {
1787     ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1788   }
1789   /* Now we should have a ximage, set appropriate caps on it */
1790   if (ximage) {
1791     /* Make sure the buffer is cleared of any previously used flags */
1792     GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1793     gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1794   }
1795
1796   /* could be our new reffed suggestion or the original unreffed caps */
1797   if (alloc_unref)
1798     gst_caps_unref (alloc_caps);
1799
1800   *buf = GST_BUFFER_CAST (ximage);
1801
1802   return ret;
1803 }
1804
1805 /* Interfaces stuff */
1806
1807 static gboolean
1808 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1809 {
1810   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1811   return TRUE;
1812 }
1813
1814 static void
1815 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1816 {
1817   klass->supported = gst_ximagesink_interface_supported;
1818 }
1819
1820 static void
1821 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1822     GstStructure * structure)
1823 {
1824   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1825   GstEvent *event;
1826   gint x_offset, y_offset;
1827   gdouble x, y;
1828   GstPad *pad = NULL;
1829
1830   event = gst_event_new_navigation (structure);
1831
1832   /* We are not converting the pointer coordinates as there's no hardware
1833      scaling done here. The only possible scaling is done by videoscale and
1834      videoscale will have to catch those events and tranform the coordinates
1835      to match the applied scaling. So here we just add the offset if the image
1836      is centered in the window.  */
1837
1838   /* We take the flow_lock while we look at the window */
1839   g_mutex_lock (ximagesink->flow_lock);
1840
1841   if (!ximagesink->xwindow) {
1842     g_mutex_unlock (ximagesink->flow_lock);
1843     return;
1844   }
1845
1846   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1847   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1848
1849   g_mutex_unlock (ximagesink->flow_lock);
1850
1851   if (gst_structure_get_double (structure, "pointer_x", &x)) {
1852     x -= x_offset / 2;
1853     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1854   }
1855   if (gst_structure_get_double (structure, "pointer_y", &y)) {
1856     y -= y_offset / 2;
1857     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1858   }
1859
1860   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1861
1862   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1863     gst_pad_send_event (pad, event);
1864
1865     gst_object_unref (pad);
1866   }
1867 }
1868
1869 static void
1870 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1871 {
1872   iface->send_event = gst_ximagesink_navigation_send_event;
1873 }
1874
1875 static void
1876 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1877 {
1878   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1879   GstXWindow *xwindow = NULL;
1880   XWindowAttributes attr;
1881
1882   /* We acquire the stream lock while setting this window in the element.
1883      We are basically cleaning tons of stuff replacing the old window, putting
1884      images while we do that would surely crash */
1885   g_mutex_lock (ximagesink->flow_lock);
1886
1887   /* If we already use that window return */
1888   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1889     g_mutex_unlock (ximagesink->flow_lock);
1890     return;
1891   }
1892
1893   /* If the element has not initialized the X11 context try to do so */
1894   if (!ximagesink->xcontext &&
1895       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1896     g_mutex_unlock (ximagesink->flow_lock);
1897     /* we have thrown a GST_ELEMENT_ERROR now */
1898     return;
1899   }
1900
1901   /* If a window is there already we destroy it */
1902   if (ximagesink->xwindow) {
1903     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1904     ximagesink->xwindow = NULL;
1905   }
1906
1907   /* If the xid is 0 we go back to an internal window */
1908   if (xwindow_id == 0) {
1909     /* If no width/height caps nego did not happen window will be created
1910        during caps nego then */
1911     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1912       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1913           GST_VIDEO_SINK_WIDTH (ximagesink),
1914           GST_VIDEO_SINK_HEIGHT (ximagesink));
1915     }
1916   } else {
1917     xwindow = g_new0 (GstXWindow, 1);
1918
1919     xwindow->win = xwindow_id;
1920
1921     /* We get window geometry, set the event we want to receive,
1922        and create a GC */
1923     g_mutex_lock (ximagesink->x_lock);
1924     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1925     xwindow->width = attr.width;
1926     xwindow->height = attr.height;
1927     xwindow->internal = FALSE;
1928     if (ximagesink->handle_events) {
1929       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1930           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1931           KeyReleaseMask);
1932     }
1933
1934     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1935     g_mutex_unlock (ximagesink->x_lock);
1936   }
1937
1938   if (xwindow)
1939     ximagesink->xwindow = xwindow;
1940
1941   g_mutex_unlock (ximagesink->flow_lock);
1942 }
1943
1944 static void
1945 gst_ximagesink_expose (GstXOverlay * overlay)
1946 {
1947   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1948
1949   gst_ximagesink_ximage_put (ximagesink, NULL);
1950 }
1951
1952 static void
1953 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1954     gboolean handle_events)
1955 {
1956   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1957
1958   ximagesink->handle_events = handle_events;
1959
1960   g_mutex_lock (ximagesink->flow_lock);
1961
1962   if (G_UNLIKELY (!ximagesink->xwindow)) {
1963     g_mutex_unlock (ximagesink->flow_lock);
1964     return;
1965   }
1966
1967   g_mutex_lock (ximagesink->x_lock);
1968
1969   if (handle_events) {
1970     if (ximagesink->xwindow->internal) {
1971       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1972           ExposureMask | StructureNotifyMask | PointerMotionMask |
1973           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1974     } else {
1975       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1976           ExposureMask | StructureNotifyMask | PointerMotionMask |
1977           KeyPressMask | KeyReleaseMask);
1978     }
1979   } else {
1980     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1981   }
1982
1983   g_mutex_unlock (ximagesink->x_lock);
1984
1985   g_mutex_unlock (ximagesink->flow_lock);
1986 }
1987
1988 static void
1989 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1990 {
1991   iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1992   iface->expose = gst_ximagesink_expose;
1993   iface->handle_events = gst_ximagesink_set_event_handling;
1994 }
1995
1996 /* =========================================== */
1997 /*                                             */
1998 /*              Init & Class init              */
1999 /*                                             */
2000 /* =========================================== */
2001
2002 static void
2003 gst_ximagesink_set_property (GObject * object, guint prop_id,
2004     const GValue * value, GParamSpec * pspec)
2005 {
2006   GstXImageSink *ximagesink;
2007
2008   g_return_if_fail (GST_IS_XIMAGESINK (object));
2009
2010   ximagesink = GST_XIMAGESINK (object);
2011
2012   switch (prop_id) {
2013     case PROP_DISPLAY:
2014       ximagesink->display_name = g_strdup (g_value_get_string (value));
2015       break;
2016     case PROP_SYNCHRONOUS:
2017       ximagesink->synchronous = g_value_get_boolean (value);
2018       if (ximagesink->xcontext) {
2019         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2020             ximagesink->synchronous ? "TRUE" : "FALSE");
2021         g_mutex_lock (ximagesink->x_lock);
2022         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2023         g_mutex_unlock (ximagesink->x_lock);
2024       }
2025       break;
2026     case PROP_FORCE_ASPECT_RATIO:
2027       ximagesink->keep_aspect = g_value_get_boolean (value);
2028       break;
2029     case PROP_PIXEL_ASPECT_RATIO:
2030     {
2031       GValue *tmp;
2032
2033       tmp = g_new0 (GValue, 1);
2034       g_value_init (tmp, GST_TYPE_FRACTION);
2035
2036       if (!g_value_transform (value, tmp)) {
2037         GST_WARNING_OBJECT (ximagesink,
2038             "Could not transform string to aspect ratio");
2039         g_free (tmp);
2040       } else {
2041         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2042             gst_value_get_fraction_numerator (tmp),
2043             gst_value_get_fraction_denominator (tmp));
2044         g_free (ximagesink->par);
2045         ximagesink->par = tmp;
2046       }
2047     }
2048       break;
2049     case PROP_HANDLE_EVENTS:
2050       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2051           g_value_get_boolean (value));
2052       break;
2053     case PROP_HANDLE_EXPOSE:
2054       ximagesink->handle_expose = g_value_get_boolean (value);
2055       break;
2056     default:
2057       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2058       break;
2059   }
2060 }
2061
2062 static void
2063 gst_ximagesink_get_property (GObject * object, guint prop_id,
2064     GValue * value, GParamSpec * pspec)
2065 {
2066   GstXImageSink *ximagesink;
2067
2068   g_return_if_fail (GST_IS_XIMAGESINK (object));
2069
2070   ximagesink = GST_XIMAGESINK (object);
2071
2072   switch (prop_id) {
2073     case PROP_DISPLAY:
2074       g_value_set_string (value, ximagesink->display_name);
2075       break;
2076     case PROP_SYNCHRONOUS:
2077       g_value_set_boolean (value, ximagesink->synchronous);
2078       break;
2079     case PROP_FORCE_ASPECT_RATIO:
2080       g_value_set_boolean (value, ximagesink->keep_aspect);
2081       break;
2082     case PROP_PIXEL_ASPECT_RATIO:
2083       if (ximagesink->par)
2084         g_value_transform (ximagesink->par, value);
2085       break;
2086     case PROP_HANDLE_EVENTS:
2087       g_value_set_boolean (value, ximagesink->handle_events);
2088       break;
2089     case PROP_HANDLE_EXPOSE:
2090       g_value_set_boolean (value, ximagesink->handle_expose);
2091       break;
2092     default:
2093       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2094       break;
2095   }
2096 }
2097
2098 static void
2099 gst_ximagesink_reset (GstXImageSink * ximagesink)
2100 {
2101   GThread *thread;
2102
2103   GST_OBJECT_LOCK (ximagesink);
2104   ximagesink->running = FALSE;
2105   /* grab thread and mark it as NULL */
2106   thread = ximagesink->event_thread;
2107   ximagesink->event_thread = NULL;
2108   GST_OBJECT_UNLOCK (ximagesink);
2109
2110   /* Wait for our event thread to finish before we clean up our stuff. */
2111   if (thread)
2112     g_thread_join (thread);
2113
2114   if (ximagesink->ximage) {
2115     gst_buffer_unref (ximagesink->ximage);
2116     ximagesink->ximage = NULL;
2117   }
2118   if (ximagesink->cur_image) {
2119     gst_buffer_unref (ximagesink->cur_image);
2120     ximagesink->cur_image = NULL;
2121   }
2122
2123   gst_ximagesink_bufferpool_clear (ximagesink);
2124
2125   g_mutex_lock (ximagesink->flow_lock);
2126   if (ximagesink->xwindow) {
2127     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2128     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2129     ximagesink->xwindow = NULL;
2130   }
2131   g_mutex_unlock (ximagesink->flow_lock);
2132
2133   gst_ximagesink_xcontext_clear (ximagesink);
2134 }
2135
2136 static void
2137 gst_ximagesink_finalize (GObject * object)
2138 {
2139   GstXImageSink *ximagesink;
2140
2141   ximagesink = GST_XIMAGESINK (object);
2142
2143   gst_ximagesink_reset (ximagesink);
2144
2145   if (ximagesink->display_name) {
2146     g_free (ximagesink->display_name);
2147     ximagesink->display_name = NULL;
2148   }
2149   if (ximagesink->par) {
2150     g_free (ximagesink->par);
2151     ximagesink->par = NULL;
2152   }
2153   if (ximagesink->x_lock) {
2154     g_mutex_free (ximagesink->x_lock);
2155     ximagesink->x_lock = NULL;
2156   }
2157   if (ximagesink->flow_lock) {
2158     g_mutex_free (ximagesink->flow_lock);
2159     ximagesink->flow_lock = NULL;
2160   }
2161   if (ximagesink->pool_lock) {
2162     g_mutex_free (ximagesink->pool_lock);
2163     ximagesink->pool_lock = NULL;
2164   }
2165
2166   G_OBJECT_CLASS (parent_class)->finalize (object);
2167 }
2168
2169 static void
2170 gst_ximagesink_init (GstXImageSink * ximagesink)
2171 {
2172   ximagesink->display_name = NULL;
2173   ximagesink->xcontext = NULL;
2174   ximagesink->xwindow = NULL;
2175   ximagesink->ximage = NULL;
2176   ximagesink->cur_image = NULL;
2177
2178   ximagesink->event_thread = NULL;
2179   ximagesink->running = FALSE;
2180
2181   ximagesink->fps_n = 0;
2182   ximagesink->fps_d = 1;
2183
2184   ximagesink->x_lock = g_mutex_new ();
2185   ximagesink->flow_lock = g_mutex_new ();
2186
2187   ximagesink->par = NULL;
2188
2189   ximagesink->pool_lock = g_mutex_new ();
2190   ximagesink->buffer_pool = NULL;
2191
2192   ximagesink->synchronous = FALSE;
2193   ximagesink->keep_aspect = FALSE;
2194   ximagesink->handle_events = TRUE;
2195   ximagesink->handle_expose = TRUE;
2196 }
2197
2198 static void
2199 gst_ximagesink_base_init (gpointer g_class)
2200 {
2201   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2202
2203   gst_element_class_set_details (element_class, &gst_ximagesink_details);
2204
2205   gst_element_class_add_pad_template (element_class,
2206       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2207 }
2208
2209 static void
2210 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2211 {
2212   GObjectClass *gobject_class;
2213   GstElementClass *gstelement_class;
2214   GstBaseSinkClass *gstbasesink_class;
2215
2216   gobject_class = (GObjectClass *) klass;
2217   gstelement_class = (GstElementClass *) klass;
2218   gstbasesink_class = (GstBaseSinkClass *) klass;
2219
2220   parent_class = g_type_class_peek_parent (klass);
2221
2222   gobject_class->finalize = gst_ximagesink_finalize;
2223   gobject_class->set_property = gst_ximagesink_set_property;
2224   gobject_class->get_property = gst_ximagesink_get_property;
2225
2226   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2227       g_param_spec_string ("display", "Display", "X Display name",
2228           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2229   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2230       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2231           "the X display in synchronous mode. (used only for debugging)", FALSE,
2232           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2233   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2234       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2235           "When enabled, reverse caps negotiation (scaling) will respect "
2236           "original aspect ratio", FALSE,
2237           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2238   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2239       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2240           "The pixel aspect ratio of the device", "1/1",
2241           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2242   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2243       g_param_spec_boolean ("handle-events", "Handle XEvents",
2244           "When enabled, XEvents will be selected and handled", TRUE,
2245           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2246   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2247       g_param_spec_boolean ("handle-expose", "Handle expose",
2248           "When enabled, "
2249           "the current frame will always be drawn in response to X Expose "
2250           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2251
2252   gstelement_class->change_state = gst_ximagesink_change_state;
2253
2254   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2255   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2256   gstbasesink_class->buffer_alloc =
2257       GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2258   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2259   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2260   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2261 }
2262
2263 /* ============================================================= */
2264 /*                                                               */
2265 /*                       Public Methods                          */
2266 /*                                                               */
2267 /* ============================================================= */
2268
2269 /* =========================================== */
2270 /*                                             */
2271 /*          Object typing & Creation           */
2272 /*                                             */
2273 /* =========================================== */
2274
2275 GType
2276 gst_ximagesink_get_type (void)
2277 {
2278   static GType ximagesink_type = 0;
2279
2280   if (!ximagesink_type) {
2281     static const GTypeInfo ximagesink_info = {
2282       sizeof (GstXImageSinkClass),
2283       gst_ximagesink_base_init,
2284       NULL,
2285       (GClassInitFunc) gst_ximagesink_class_init,
2286       NULL,
2287       NULL,
2288       sizeof (GstXImageSink),
2289       0,
2290       (GInstanceInitFunc) gst_ximagesink_init,
2291     };
2292     static const GInterfaceInfo iface_info = {
2293       (GInterfaceInitFunc) gst_ximagesink_interface_init,
2294       NULL,
2295       NULL,
2296     };
2297     static const GInterfaceInfo navigation_info = {
2298       (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2299       NULL,
2300       NULL,
2301     };
2302     static const GInterfaceInfo overlay_info = {
2303       (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2304       NULL,
2305       NULL,
2306     };
2307
2308     ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2309         "GstXImageSink", &ximagesink_info, 0);
2310
2311     g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2312         &iface_info);
2313     g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2314         &navigation_info);
2315     g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2316         &overlay_info);
2317
2318     /* register type and create class in a more safe place instead of at
2319      * runtime since the type registration and class creation is not
2320      * threadsafe. */
2321     g_type_class_ref (gst_ximage_buffer_get_type ());
2322   }
2323
2324   return ximagesink_type;
2325 }