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