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