sys/: Don't leak the PAR on errors. Fixes #496731.
[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   GST_OBJECT_LOCK (ximagesink);
1093   while (ximagesink->running) {
1094     GST_OBJECT_UNLOCK (ximagesink);
1095
1096     if (ximagesink->xwindow) {
1097       gst_ximagesink_handle_xevents (ximagesink);
1098     }
1099     g_usleep (100000);
1100
1101     GST_OBJECT_LOCK (ximagesink);
1102   }
1103   GST_OBJECT_UNLOCK (ximagesink);
1104
1105   return NULL;
1106 }
1107
1108 /* This function calculates the pixel aspect ratio based on the properties
1109  * in the xcontext structure and stores it there. */
1110 static void
1111 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1112 {
1113   static const gint par[][2] = {
1114     {1, 1},                     /* regular screen */
1115     {16, 15},                   /* PAL TV */
1116     {11, 10},                   /* 525 line Rec.601 video */
1117     {54, 59},                   /* 625 line Rec.601 video */
1118     {64, 45},                   /* 1280x1024 on 16:9 display */
1119     {5, 3},                     /* 1280x1024 on 4:3 display */
1120     {4, 3}                      /*  800x600 on 16:9 display */
1121   };
1122   gint i;
1123   gint index;
1124   gdouble ratio;
1125   gdouble delta;
1126
1127 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1128
1129   /* first calculate the "real" ratio based on the X values;
1130    * which is the "physical" w/h divided by the w/h in pixels of the display */
1131   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1132       / (xcontext->heightmm * xcontext->width);
1133
1134   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1135    * override here */
1136   if (xcontext->width == 720 && xcontext->height == 576) {
1137     ratio = 4.0 * 576 / (3.0 * 720);
1138   }
1139   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1140
1141   /* now find the one from par[][2] with the lowest delta to the real one */
1142   delta = DELTA (0);
1143   index = 0;
1144
1145   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1146     gdouble this_delta = DELTA (i);
1147
1148     if (this_delta < delta) {
1149       index = i;
1150       delta = this_delta;
1151     }
1152   }
1153
1154   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1155       par[index][0], par[index][1]);
1156
1157   g_free (xcontext->par);
1158   xcontext->par = g_new0 (GValue, 1);
1159   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1160   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1161   GST_DEBUG ("set xcontext PAR to %d/%d",
1162       gst_value_get_fraction_numerator (xcontext->par),
1163       gst_value_get_fraction_denominator (xcontext->par));
1164 }
1165
1166 /* This function gets the X Display and global info about it. Everything is
1167    stored in our object and will be cleaned when the object is disposed. Note
1168    here that caps for supported format are generated without any window or
1169    image creation */
1170 static GstXContext *
1171 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1172 {
1173   GstXContext *xcontext = NULL;
1174   XPixmapFormatValues *px_formats = NULL;
1175   gint nb_formats = 0, i;
1176
1177   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1178
1179   xcontext = g_new0 (GstXContext, 1);
1180
1181   g_mutex_lock (ximagesink->x_lock);
1182
1183   xcontext->disp = XOpenDisplay (ximagesink->display_name);
1184
1185   if (!xcontext->disp) {
1186     g_mutex_unlock (ximagesink->x_lock);
1187     g_free (xcontext);
1188     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1189         ("Could not initialise X output"), ("Could not open display"));
1190     return NULL;
1191   }
1192
1193   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1194   xcontext->screen_num = DefaultScreen (xcontext->disp);
1195   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1196   xcontext->root = DefaultRootWindow (xcontext->disp);
1197   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1198   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1199   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1200
1201   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1202   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1203   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1204   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1205
1206   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1207       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1208
1209   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1210
1211   /* We get supported pixmap formats at supported depth */
1212   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1213
1214   if (!px_formats) {
1215     XCloseDisplay (xcontext->disp);
1216     g_mutex_unlock (ximagesink->x_lock);
1217     g_free (xcontext->par);
1218     g_free (xcontext);
1219     return NULL;
1220   }
1221
1222   /* We get bpp value corresponding to our running depth */
1223   for (i = 0; i < nb_formats; i++) {
1224     if (px_formats[i].depth == xcontext->depth)
1225       xcontext->bpp = px_formats[i].bits_per_pixel;
1226   }
1227
1228   XFree (px_formats);
1229
1230   xcontext->endianness =
1231       (ImageByteOrder (xcontext->disp) ==
1232       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1233
1234   /* Search for XShm extension support */
1235 #ifdef HAVE_XSHM
1236   if (XShmQueryExtension (xcontext->disp) &&
1237       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1238     xcontext->use_xshm = TRUE;
1239     GST_DEBUG ("ximagesink is using XShm extension");
1240   } else
1241 #endif
1242   {
1243     xcontext->use_xshm = FALSE;
1244     GST_DEBUG ("ximagesink is not using XShm extension");
1245   }
1246
1247   /* our caps system handles 24/32bpp RGB as big-endian. */
1248   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1249       xcontext->endianness == G_LITTLE_ENDIAN) {
1250     xcontext->endianness = G_BIG_ENDIAN;
1251     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1252     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1253     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1254     if (xcontext->bpp == 24) {
1255       xcontext->visual->red_mask >>= 8;
1256       xcontext->visual->green_mask >>= 8;
1257       xcontext->visual->blue_mask >>= 8;
1258     }
1259   }
1260
1261   /* update object's par with calculated one if not set yet */
1262   if (!ximagesink->par) {
1263     ximagesink->par = g_new0 (GValue, 1);
1264     gst_value_init_and_copy (ximagesink->par, xcontext->par);
1265     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1266   }
1267   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1268       "bpp", G_TYPE_INT, xcontext->bpp,
1269       "depth", G_TYPE_INT, xcontext->depth,
1270       "endianness", G_TYPE_INT, xcontext->endianness,
1271       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1272       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1273       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1274       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1275       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1276       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1277   if (ximagesink->par) {
1278     int nom, den;
1279
1280     nom = gst_value_get_fraction_numerator (ximagesink->par);
1281     den = gst_value_get_fraction_denominator (ximagesink->par);
1282     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1283         GST_TYPE_FRACTION, nom, den, NULL);
1284   }
1285
1286   g_mutex_unlock (ximagesink->x_lock);
1287
1288   /* Setup our event listening thread */
1289   GST_OBJECT_LOCK (ximagesink);
1290   ximagesink->running = TRUE;
1291   ximagesink->event_thread = g_thread_create (
1292       (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1293   GST_OBJECT_UNLOCK (ximagesink);
1294
1295   return xcontext;
1296 }
1297
1298 /* This function cleans the X context. Closing the Display and unrefing the
1299    caps for supported formats. */
1300 static void
1301 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1302 {
1303   GstXContext *xcontext;
1304
1305   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1306
1307   GST_OBJECT_LOCK (ximagesink);
1308   if (ximagesink->xcontext == NULL) {
1309     GST_OBJECT_UNLOCK (ximagesink);
1310     return;
1311   }
1312
1313   /* Take the xcontext reference and NULL it while we
1314    * clean it up, so that any buffer-alloced buffers 
1315    * arriving after this will be freed correctly */
1316   xcontext = ximagesink->xcontext;
1317   ximagesink->xcontext = NULL;
1318
1319   GST_OBJECT_UNLOCK (ximagesink);
1320
1321   gst_caps_unref (xcontext->caps);
1322   g_free (xcontext->par);
1323   g_free (ximagesink->par);
1324   ximagesink->par = NULL;
1325
1326   g_mutex_lock (ximagesink->x_lock);
1327
1328   XCloseDisplay (xcontext->disp);
1329
1330   g_mutex_unlock (ximagesink->x_lock);
1331
1332   g_free (xcontext);
1333 }
1334
1335 static void
1336 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1337 {
1338
1339   g_mutex_lock (ximagesink->pool_lock);
1340
1341   while (ximagesink->buffer_pool) {
1342     GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1343
1344     ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1345         ximagesink->buffer_pool);
1346     gst_ximage_buffer_free (ximage);
1347   }
1348
1349   g_mutex_unlock (ximagesink->pool_lock);
1350 }
1351
1352 /* Element stuff */
1353
1354 static GstCaps *
1355 gst_ximagesink_getcaps (GstBaseSink * bsink)
1356 {
1357   GstXImageSink *ximagesink;
1358   GstCaps *caps;
1359   int i;
1360
1361   ximagesink = GST_XIMAGESINK (bsink);
1362
1363   if (ximagesink->xcontext)
1364     return gst_caps_ref (ximagesink->xcontext->caps);
1365
1366   /* get a template copy and add the pixel aspect ratio */
1367   caps =
1368       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1369           sinkpad));
1370   for (i = 0; i < gst_caps_get_size (caps); ++i) {
1371     GstStructure *structure = gst_caps_get_structure (caps, i);
1372
1373     if (ximagesink->par) {
1374       int nom, den;
1375
1376       nom = gst_value_get_fraction_numerator (ximagesink->par);
1377       den = gst_value_get_fraction_denominator (ximagesink->par);
1378       gst_structure_set (structure, "pixel-aspect-ratio",
1379           GST_TYPE_FRACTION, nom, den, NULL);
1380     }
1381   }
1382
1383   return caps;
1384 }
1385
1386 static gboolean
1387 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1388 {
1389   GstXImageSink *ximagesink;
1390   gboolean ret = TRUE;
1391   GstStructure *structure;
1392   GstCaps *intersection;
1393   const GValue *par;
1394   gint new_width, new_height;
1395   const GValue *fps;
1396
1397   ximagesink = GST_XIMAGESINK (bsink);
1398
1399   if (!ximagesink->xcontext)
1400     return FALSE;
1401
1402   GST_DEBUG_OBJECT (ximagesink,
1403       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1404       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1405
1406   /* We intersect those caps with our template to make sure they are correct */
1407   intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1408   GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1409       intersection);
1410   if (gst_caps_is_empty (intersection)) {
1411     gst_caps_unref (intersection);
1412     return FALSE;
1413   }
1414
1415   gst_caps_unref (intersection);
1416
1417   structure = gst_caps_get_structure (caps, 0);
1418
1419   ret &= gst_structure_get_int (structure, "width", &new_width);
1420   ret &= gst_structure_get_int (structure, "height", &new_height);
1421   fps = gst_structure_get_value (structure, "framerate");
1422   ret &= (fps != NULL);
1423   if (!ret)
1424     return FALSE;
1425
1426   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1427    * otherwise linking should fail */
1428   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1429   if (par) {
1430     if (ximagesink->par) {
1431       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1432         goto wrong_aspect;
1433       }
1434     } else if (ximagesink->xcontext->par) {
1435       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1436         goto wrong_aspect;
1437       }
1438     }
1439   }
1440
1441   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1442   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1443   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1444   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1445
1446   /* Notify application to set xwindow id now */
1447   g_mutex_lock (ximagesink->flow_lock);
1448   if (!ximagesink->xwindow) {
1449     g_mutex_unlock (ximagesink->flow_lock);
1450     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1451   } else {
1452     g_mutex_unlock (ximagesink->flow_lock);
1453   }
1454
1455   /* Creating our window and our image */
1456   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1457       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1458     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1459         ("Invalid image size."));
1460     return FALSE;
1461   }
1462
1463   g_mutex_lock (ximagesink->flow_lock);
1464   if (!ximagesink->xwindow) {
1465     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1466         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1467   }
1468   g_mutex_unlock (ximagesink->flow_lock);
1469
1470   /* If our ximage has changed we destroy it, next chain iteration will create
1471      a new one */
1472   if ((ximagesink->ximage) &&
1473       ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1474           (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1475     GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1476         ximagesink->ximage);
1477     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1478     ximagesink->ximage = NULL;
1479   }
1480
1481   return TRUE;
1482
1483   /* ERRORS */
1484 wrong_aspect:
1485   {
1486     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1487     return FALSE;
1488   }
1489 }
1490
1491 static GstStateChangeReturn
1492 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1493 {
1494   GstXImageSink *ximagesink;
1495   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1496   GstXContext *xcontext = NULL;
1497
1498   ximagesink = GST_XIMAGESINK (element);
1499
1500   switch (transition) {
1501     case GST_STATE_CHANGE_NULL_TO_READY:
1502
1503       /* Initializing the XContext */
1504       if (ximagesink->xcontext == NULL) {
1505         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1506         if (xcontext == NULL) {
1507           ret = GST_STATE_CHANGE_FAILURE;
1508           goto beach;
1509         }
1510         GST_OBJECT_LOCK (ximagesink);
1511         if (xcontext)
1512           ximagesink->xcontext = xcontext;
1513         GST_OBJECT_UNLOCK (ximagesink);
1514       }
1515
1516       /* call XSynchronize with the current value of synchronous */
1517       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1518           ximagesink->synchronous ? "TRUE" : "FALSE");
1519       g_mutex_lock (ximagesink->x_lock);
1520       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1521       g_mutex_unlock (ximagesink->x_lock);
1522       break;
1523     case GST_STATE_CHANGE_READY_TO_PAUSED:
1524       g_mutex_lock (ximagesink->flow_lock);
1525       if (ximagesink->xwindow)
1526         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1527       g_mutex_unlock (ximagesink->flow_lock);
1528       break;
1529     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1530       break;
1531     default:
1532       break;
1533   }
1534
1535   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1536
1537   switch (transition) {
1538     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1539       break;
1540     case GST_STATE_CHANGE_PAUSED_TO_READY:
1541       ximagesink->fps_n = 0;
1542       ximagesink->fps_d = 1;
1543       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1544       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1545       break;
1546     case GST_STATE_CHANGE_READY_TO_NULL:
1547       gst_ximagesink_reset (ximagesink);
1548       break;
1549     default:
1550       break;
1551   }
1552
1553 beach:
1554   return ret;
1555 }
1556
1557 static void
1558 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1559     GstClockTime * start, GstClockTime * end)
1560 {
1561   GstXImageSink *ximagesink;
1562
1563   ximagesink = GST_XIMAGESINK (bsink);
1564
1565   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1566     *start = GST_BUFFER_TIMESTAMP (buf);
1567     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1568       *end = *start + GST_BUFFER_DURATION (buf);
1569     } else {
1570       if (ximagesink->fps_n > 0) {
1571         *end = *start +
1572             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1573             ximagesink->fps_n);
1574       }
1575     }
1576   }
1577 }
1578
1579 static GstFlowReturn
1580 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1581 {
1582   GstXImageSink *ximagesink;
1583
1584   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1585
1586   ximagesink = GST_XIMAGESINK (bsink);
1587
1588   /* If this buffer has been allocated using our buffer management we simply
1589      put the ximage which is in the PRIVATE pointer */
1590   if (GST_IS_XIMAGE_BUFFER (buf)) {
1591     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1592     if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1593       goto no_window;
1594   } else {
1595     /* Else we have to copy the data into our private image, */
1596     /* if we have one... */
1597     GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1598     if (!ximagesink->ximage) {
1599       GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1600       ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1601           GST_BUFFER_CAPS (buf));
1602       if (!ximagesink->ximage)
1603         /* The create method should have posted an informative error */
1604         goto no_ximage;
1605
1606       if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1607         GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1608             ("Failed to create output image buffer of %dx%d pixels",
1609                 ximagesink->ximage->width, ximagesink->ximage->height),
1610             ("XServer allocated buffer size did not match input buffer"));
1611
1612         gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1613         ximagesink->ximage = NULL;
1614         goto no_ximage;
1615       }
1616     }
1617     memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1618         MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1619     if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1620       goto no_window;
1621   }
1622
1623   return GST_FLOW_OK;
1624
1625   /* ERRORS */
1626 no_ximage:
1627   {
1628     /* No image available. That's very bad ! */
1629     GST_DEBUG ("could not create image");
1630     return GST_FLOW_ERROR;
1631   }
1632 no_window:
1633   {
1634     /* No Window available to put our image into */
1635     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1636     return GST_FLOW_ERROR;
1637   }
1638 }
1639
1640 /* Buffer management
1641  *
1642  * The buffer_alloc function must either return a buffer with given size and
1643  * caps or create a buffer with different caps attached to the buffer. This
1644  * last option is called reverse negotiation, ie, where the sink suggests a
1645  * different format from the upstream peer. 
1646  *
1647  * We try to do reverse negotiation when our geometry changes and we like a
1648  * resized buffer.
1649  */
1650 static GstFlowReturn
1651 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1652     GstCaps * caps, GstBuffer ** buf)
1653 {
1654   GstXImageSink *ximagesink;
1655   GstXImageBuffer *ximage = NULL;
1656   GstStructure *structure = NULL;
1657   GstFlowReturn ret = GST_FLOW_OK;
1658   GstCaps *alloc_caps;
1659   gboolean alloc_unref = FALSE;
1660   gint width, height;
1661
1662   ximagesink = GST_XIMAGESINK (bsink);
1663
1664   GST_LOG_OBJECT (ximagesink,
1665       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1666       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1667
1668   /* assume we're going to alloc what was requested, keep track of
1669    * wheter we need to unref or not. When we suggest a new format 
1670    * upstream we will create a new caps that we need to unref. */
1671   alloc_caps = caps;
1672   alloc_unref = FALSE;
1673
1674   /* get struct to see what is requested */
1675   structure = gst_caps_get_structure (caps, 0);
1676
1677   if (gst_structure_get_int (structure, "width", &width) &&
1678       gst_structure_get_int (structure, "height", &height)) {
1679     GstVideoRectangle dst, src, result;
1680
1681     src.w = width;
1682     src.h = height;
1683
1684     /* We take the flow_lock because the window might go away */
1685     g_mutex_lock (ximagesink->flow_lock);
1686     if (!ximagesink->xwindow) {
1687       g_mutex_unlock (ximagesink->flow_lock);
1688       goto alloc;
1689     }
1690
1691     /* What is our geometry */
1692     gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1693     dst.w = ximagesink->xwindow->width;
1694     dst.h = ximagesink->xwindow->height;
1695
1696     g_mutex_unlock (ximagesink->flow_lock);
1697
1698     if (ximagesink->keep_aspect) {
1699       GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1700           "negotiation");
1701       gst_video_sink_center_rect (src, dst, &result, TRUE);
1702     } else {
1703       GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1704           "ignoring aspect ratio");
1705       result.x = result.y = 0;
1706       result.w = dst.w;
1707       result.h = dst.h;
1708     }
1709
1710     /* We would like another geometry */
1711     if (width != result.w || height != result.h) {
1712       int nom, den;
1713       GstCaps *desired_caps;
1714       GstStructure *desired_struct;
1715
1716       /* make a copy of the incomming caps to create the new
1717        * suggestion. We can't use make_writable because we might
1718        * then destroy the original caps which we still need when the
1719        * peer does not accept the suggestion. */
1720       desired_caps = gst_caps_copy (caps);
1721       desired_struct = gst_caps_get_structure (desired_caps, 0);
1722
1723       GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1724       gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1725       gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1726
1727       /* PAR property overrides the X calculated one */
1728       if (ximagesink->par) {
1729         nom = gst_value_get_fraction_numerator (ximagesink->par);
1730         den = gst_value_get_fraction_denominator (ximagesink->par);
1731         gst_structure_set (desired_struct, "pixel-aspect-ratio",
1732             GST_TYPE_FRACTION, nom, den, NULL);
1733       } else if (ximagesink->xcontext->par) {
1734         nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1735         den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1736         gst_structure_set (desired_struct, "pixel-aspect-ratio",
1737             GST_TYPE_FRACTION, nom, den, NULL);
1738       }
1739
1740       /* see if peer accepts our new suggestion, if there is no peer, this 
1741        * function returns true. */
1742       if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1743               desired_caps)) {
1744         gint bpp;
1745
1746         bpp = size / height / width;
1747         /* we will not alloc a buffer of the new suggested caps. Make sure
1748          * we also unref this new caps after we set it on the buffer. */
1749         alloc_caps = desired_caps;
1750         alloc_unref = TRUE;
1751         width = result.w;
1752         height = result.h;
1753         size = bpp * width * height;
1754         GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1755             " buffer size is now %d bytes", desired_caps, size);
1756       } else {
1757         GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1758             desired_caps);
1759         /* we alloc a buffer with the original incomming caps */
1760         width = GST_VIDEO_SINK_WIDTH (ximagesink);
1761         height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1762       }
1763     }
1764   }
1765
1766 alloc:
1767   /* Inspect our buffer pool */
1768   g_mutex_lock (ximagesink->pool_lock);
1769   while (ximagesink->buffer_pool) {
1770     ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1771
1772     if (ximage) {
1773       /* Removing from the pool */
1774       ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1775           ximagesink->buffer_pool);
1776
1777       /* If the ximage is invalid for our need, destroy */
1778       if ((ximage->width != width) || (ximage->height != height)) {
1779         gst_ximage_buffer_free (ximage);
1780         ximage = NULL;
1781       } else {
1782         /* We found a suitable ximage */
1783         break;
1784       }
1785     }
1786   }
1787   g_mutex_unlock (ximagesink->pool_lock);
1788
1789   /* We haven't found anything, creating a new one */
1790   if (!ximage) {
1791     ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1792   }
1793   /* Now we should have a ximage, set appropriate caps on it */
1794   if (ximage) {
1795     gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1796   }
1797
1798   /* could be our new reffed suggestion or the original unreffed caps */
1799   if (alloc_unref)
1800     gst_caps_unref (alloc_caps);
1801
1802   *buf = GST_BUFFER_CAST (ximage);
1803
1804   return ret;
1805 }
1806
1807 /* Interfaces stuff */
1808
1809 static gboolean
1810 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1811 {
1812   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1813   return TRUE;
1814 }
1815
1816 static void
1817 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1818 {
1819   klass->supported = gst_ximagesink_interface_supported;
1820 }
1821
1822 static void
1823 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1824     GstStructure * structure)
1825 {
1826   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1827   GstEvent *event;
1828   gint x_offset, y_offset;
1829   gdouble x, y;
1830   GstPad *pad = NULL;
1831
1832   event = gst_event_new_navigation (structure);
1833
1834   /* We are not converting the pointer coordinates as there's no hardware
1835      scaling done here. The only possible scaling is done by videoscale and
1836      videoscale will have to catch those events and tranform the coordinates
1837      to match the applied scaling. So here we just add the offset if the image
1838      is centered in the window.  */
1839
1840   /* We take the flow_lock while we look at the window */
1841   g_mutex_lock (ximagesink->flow_lock);
1842
1843   if (!ximagesink->xwindow) {
1844     g_mutex_unlock (ximagesink->flow_lock);
1845     return;
1846   }
1847
1848   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1849   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1850
1851   g_mutex_unlock (ximagesink->flow_lock);
1852
1853   if (gst_structure_get_double (structure, "pointer_x", &x)) {
1854     x -= x_offset / 2;
1855     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1856   }
1857   if (gst_structure_get_double (structure, "pointer_y", &y)) {
1858     y -= y_offset / 2;
1859     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1860   }
1861
1862   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1863
1864   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1865     gst_pad_send_event (pad, event);
1866
1867     gst_object_unref (pad);
1868   }
1869 }
1870
1871 static void
1872 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1873 {
1874   iface->send_event = gst_ximagesink_navigation_send_event;
1875 }
1876
1877 static void
1878 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1879 {
1880   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1881   GstXWindow *xwindow = NULL;
1882   XWindowAttributes attr;
1883
1884   /* We acquire the stream lock while setting this window in the element.
1885      We are basically cleaning tons of stuff replacing the old window, putting
1886      images while we do that would surely crash */
1887   g_mutex_lock (ximagesink->flow_lock);
1888
1889   /* If we already use that window return */
1890   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1891     g_mutex_unlock (ximagesink->flow_lock);
1892     return;
1893   }
1894
1895   /* If the element has not initialized the X11 context try to do so */
1896   if (!ximagesink->xcontext &&
1897       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1898     g_mutex_unlock (ximagesink->flow_lock);
1899     /* we have thrown a GST_ELEMENT_ERROR now */
1900     return;
1901   }
1902
1903   /* If a window is there already we destroy it */
1904   if (ximagesink->xwindow) {
1905     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1906     ximagesink->xwindow = NULL;
1907   }
1908
1909   /* If the xid is 0 we go back to an internal window */
1910   if (xwindow_id == 0) {
1911     /* If no width/height caps nego did not happen window will be created
1912        during caps nego then */
1913     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1914       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1915           GST_VIDEO_SINK_WIDTH (ximagesink),
1916           GST_VIDEO_SINK_HEIGHT (ximagesink));
1917     }
1918   } else {
1919     xwindow = g_new0 (GstXWindow, 1);
1920
1921     xwindow->win = xwindow_id;
1922
1923     /* We get window geometry, set the event we want to receive,
1924        and create a GC */
1925     g_mutex_lock (ximagesink->x_lock);
1926     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1927     xwindow->width = attr.width;
1928     xwindow->height = attr.height;
1929     xwindow->internal = FALSE;
1930     if (ximagesink->handle_events) {
1931       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1932           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1933           KeyReleaseMask);
1934     }
1935
1936     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1937     g_mutex_unlock (ximagesink->x_lock);
1938   }
1939
1940   if (xwindow)
1941     ximagesink->xwindow = xwindow;
1942
1943   g_mutex_unlock (ximagesink->flow_lock);
1944 }
1945
1946 static void
1947 gst_ximagesink_expose (GstXOverlay * overlay)
1948 {
1949   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1950
1951   gst_ximagesink_ximage_put (ximagesink, NULL);
1952 }
1953
1954 static void
1955 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1956     gboolean handle_events)
1957 {
1958   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1959
1960   ximagesink->handle_events = handle_events;
1961
1962   g_mutex_lock (ximagesink->flow_lock);
1963
1964   if (G_UNLIKELY (!ximagesink->xwindow)) {
1965     g_mutex_unlock (ximagesink->flow_lock);
1966     return;
1967   }
1968
1969   g_mutex_lock (ximagesink->x_lock);
1970
1971   if (handle_events) {
1972     if (ximagesink->xwindow->internal) {
1973       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1974           ExposureMask | StructureNotifyMask | PointerMotionMask |
1975           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1976     } else {
1977       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1978           ExposureMask | StructureNotifyMask | PointerMotionMask |
1979           KeyPressMask | KeyReleaseMask);
1980     }
1981   } else {
1982     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1983   }
1984
1985   g_mutex_unlock (ximagesink->x_lock);
1986
1987   g_mutex_unlock (ximagesink->flow_lock);
1988 }
1989
1990 static void
1991 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1992 {
1993   iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1994   iface->expose = gst_ximagesink_expose;
1995   iface->handle_events = gst_ximagesink_set_event_handling;
1996 }
1997
1998 /* =========================================== */
1999 /*                                             */
2000 /*              Init & Class init              */
2001 /*                                             */
2002 /* =========================================== */
2003
2004 static void
2005 gst_ximagesink_set_property (GObject * object, guint prop_id,
2006     const GValue * value, GParamSpec * pspec)
2007 {
2008   GstXImageSink *ximagesink;
2009
2010   g_return_if_fail (GST_IS_XIMAGESINK (object));
2011
2012   ximagesink = GST_XIMAGESINK (object);
2013
2014   switch (prop_id) {
2015     case PROP_DISPLAY:
2016       ximagesink->display_name = g_strdup (g_value_get_string (value));
2017       break;
2018     case PROP_SYNCHRONOUS:
2019       ximagesink->synchronous = g_value_get_boolean (value);
2020       if (ximagesink->xcontext) {
2021         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2022             ximagesink->synchronous ? "TRUE" : "FALSE");
2023         g_mutex_lock (ximagesink->x_lock);
2024         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2025         g_mutex_unlock (ximagesink->x_lock);
2026       }
2027       break;
2028     case PROP_FORCE_ASPECT_RATIO:
2029       ximagesink->keep_aspect = g_value_get_boolean (value);
2030       break;
2031     case PROP_PIXEL_ASPECT_RATIO:
2032     {
2033       GValue *tmp;
2034
2035       tmp = g_new0 (GValue, 1);
2036       g_value_init (tmp, GST_TYPE_FRACTION);
2037
2038       if (!g_value_transform (value, tmp)) {
2039         GST_WARNING_OBJECT (ximagesink,
2040             "Could not transform string to aspect ratio");
2041         g_free (tmp);
2042       } else {
2043         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2044             gst_value_get_fraction_numerator (tmp),
2045             gst_value_get_fraction_denominator (tmp));
2046         g_free (ximagesink->par);
2047         ximagesink->par = tmp;
2048       }
2049     }
2050       break;
2051     case PROP_HANDLE_EVENTS:
2052       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2053           g_value_get_boolean (value));
2054       break;
2055     case PROP_HANDLE_EXPOSE:
2056       ximagesink->handle_expose = g_value_get_boolean (value);
2057       break;
2058     default:
2059       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2060       break;
2061   }
2062 }
2063
2064 static void
2065 gst_ximagesink_get_property (GObject * object, guint prop_id,
2066     GValue * value, GParamSpec * pspec)
2067 {
2068   GstXImageSink *ximagesink;
2069
2070   g_return_if_fail (GST_IS_XIMAGESINK (object));
2071
2072   ximagesink = GST_XIMAGESINK (object);
2073
2074   switch (prop_id) {
2075     case PROP_DISPLAY:
2076       g_value_set_string (value, ximagesink->display_name);
2077       break;
2078     case PROP_SYNCHRONOUS:
2079       g_value_set_boolean (value, ximagesink->synchronous);
2080       break;
2081     case PROP_FORCE_ASPECT_RATIO:
2082       g_value_set_boolean (value, ximagesink->keep_aspect);
2083       break;
2084     case PROP_PIXEL_ASPECT_RATIO:
2085       if (ximagesink->par)
2086         g_value_transform (ximagesink->par, value);
2087       break;
2088     case PROP_HANDLE_EVENTS:
2089       g_value_set_boolean (value, ximagesink->handle_events);
2090       break;
2091     case PROP_HANDLE_EXPOSE:
2092       g_value_set_boolean (value, ximagesink->handle_expose);
2093       break;
2094     default:
2095       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2096       break;
2097   }
2098 }
2099
2100 static void
2101 gst_ximagesink_reset (GstXImageSink * ximagesink)
2102 {
2103   GThread *thread;
2104
2105   GST_OBJECT_LOCK (ximagesink);
2106   ximagesink->running = FALSE;
2107   /* grab thread and mark it as NULL */
2108   thread = ximagesink->event_thread;
2109   ximagesink->event_thread = NULL;
2110   GST_OBJECT_UNLOCK (ximagesink);
2111
2112   /* Wait for our event thread to finish before we clean up our stuff. */
2113   if (thread)
2114     g_thread_join (thread);
2115
2116   if (ximagesink->ximage) {
2117     gst_buffer_unref (ximagesink->ximage);
2118     ximagesink->ximage = NULL;
2119   }
2120   if (ximagesink->cur_image) {
2121     gst_buffer_unref (ximagesink->cur_image);
2122     ximagesink->cur_image = NULL;
2123   }
2124
2125   gst_ximagesink_bufferpool_clear (ximagesink);
2126
2127   g_mutex_lock (ximagesink->flow_lock);
2128   if (ximagesink->xwindow) {
2129     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2130     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2131     ximagesink->xwindow = NULL;
2132   }
2133   g_mutex_unlock (ximagesink->flow_lock);
2134
2135   gst_ximagesink_xcontext_clear (ximagesink);
2136 }
2137
2138 static void
2139 gst_ximagesink_finalize (GObject * object)
2140 {
2141   GstXImageSink *ximagesink;
2142
2143   ximagesink = GST_XIMAGESINK (object);
2144
2145   gst_ximagesink_reset (ximagesink);
2146
2147   if (ximagesink->display_name) {
2148     g_free (ximagesink->display_name);
2149     ximagesink->display_name = NULL;
2150   }
2151   if (ximagesink->par) {
2152     g_free (ximagesink->par);
2153     ximagesink->par = NULL;
2154   }
2155   if (ximagesink->x_lock) {
2156     g_mutex_free (ximagesink->x_lock);
2157     ximagesink->x_lock = NULL;
2158   }
2159   if (ximagesink->flow_lock) {
2160     g_mutex_free (ximagesink->flow_lock);
2161     ximagesink->flow_lock = NULL;
2162   }
2163   if (ximagesink->pool_lock) {
2164     g_mutex_free (ximagesink->pool_lock);
2165     ximagesink->pool_lock = NULL;
2166   }
2167
2168   G_OBJECT_CLASS (parent_class)->finalize (object);
2169 }
2170
2171 static void
2172 gst_ximagesink_init (GstXImageSink * ximagesink)
2173 {
2174   ximagesink->display_name = NULL;
2175   ximagesink->xcontext = NULL;
2176   ximagesink->xwindow = NULL;
2177   ximagesink->ximage = NULL;
2178   ximagesink->cur_image = NULL;
2179
2180   ximagesink->event_thread = NULL;
2181   ximagesink->running = FALSE;
2182
2183   ximagesink->fps_n = 0;
2184   ximagesink->fps_d = 1;
2185
2186   ximagesink->x_lock = g_mutex_new ();
2187   ximagesink->flow_lock = g_mutex_new ();
2188
2189   ximagesink->par = NULL;
2190
2191   ximagesink->pool_lock = g_mutex_new ();
2192   ximagesink->buffer_pool = NULL;
2193
2194   ximagesink->synchronous = FALSE;
2195   ximagesink->keep_aspect = FALSE;
2196   ximagesink->handle_events = TRUE;
2197   ximagesink->handle_expose = TRUE;
2198 }
2199
2200 static void
2201 gst_ximagesink_base_init (gpointer g_class)
2202 {
2203   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2204
2205   gst_element_class_set_details (element_class, &gst_ximagesink_details);
2206
2207   gst_element_class_add_pad_template (element_class,
2208       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2209 }
2210
2211 static void
2212 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2213 {
2214   GObjectClass *gobject_class;
2215   GstElementClass *gstelement_class;
2216   GstBaseSinkClass *gstbasesink_class;
2217
2218   gobject_class = (GObjectClass *) klass;
2219   gstelement_class = (GstElementClass *) klass;
2220   gstbasesink_class = (GstBaseSinkClass *) klass;
2221
2222   parent_class = g_type_class_peek_parent (klass);
2223
2224   gobject_class->finalize = gst_ximagesink_finalize;
2225   gobject_class->set_property = gst_ximagesink_set_property;
2226   gobject_class->get_property = gst_ximagesink_get_property;
2227
2228   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2229       g_param_spec_string ("display", "Display", "X Display name",
2230           NULL, G_PARAM_READWRITE));
2231   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2232       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2233           "the X display in synchronous mode. (used only for debugging)", FALSE,
2234           G_PARAM_READWRITE));
2235   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2236       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2237           "When enabled, reverse caps negotiation (scaling) will respect "
2238           "original aspect ratio", FALSE, G_PARAM_READWRITE));
2239   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2240       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2241           "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2242   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2243       g_param_spec_boolean ("handle-events", "Handle XEvents",
2244           "When enabled, XEvents will be selected and handled", TRUE,
2245           G_PARAM_READWRITE));
2246   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2247       g_param_spec_boolean ("handle-expose", "Handle expose", "When enabled, "
2248           "the current frame will always be drawn in response to X Expose "
2249           "events", TRUE, G_PARAM_READWRITE));
2250
2251   gstelement_class->change_state = gst_ximagesink_change_state;
2252
2253   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2254   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2255   gstbasesink_class->buffer_alloc =
2256       GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2257   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2258   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2259   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2260 }
2261
2262 /* ============================================================= */
2263 /*                                                               */
2264 /*                       Public Methods                          */
2265 /*                                                               */
2266 /* ============================================================= */
2267
2268 /* =========================================== */
2269 /*                                             */
2270 /*          Object typing & Creation           */
2271 /*                                             */
2272 /* =========================================== */
2273
2274 GType
2275 gst_ximagesink_get_type (void)
2276 {
2277   static GType ximagesink_type = 0;
2278
2279   if (!ximagesink_type) {
2280     static const GTypeInfo ximagesink_info = {
2281       sizeof (GstXImageSinkClass),
2282       gst_ximagesink_base_init,
2283       NULL,
2284       (GClassInitFunc) gst_ximagesink_class_init,
2285       NULL,
2286       NULL,
2287       sizeof (GstXImageSink),
2288       0,
2289       (GInstanceInitFunc) gst_ximagesink_init,
2290     };
2291     static const GInterfaceInfo iface_info = {
2292       (GInterfaceInitFunc) gst_ximagesink_interface_init,
2293       NULL,
2294       NULL,
2295     };
2296     static const GInterfaceInfo navigation_info = {
2297       (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2298       NULL,
2299       NULL,
2300     };
2301     static const GInterfaceInfo overlay_info = {
2302       (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2303       NULL,
2304       NULL,
2305     };
2306
2307     ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2308         "GstXImageSink", &ximagesink_info, 0);
2309
2310     g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2311         &iface_info);
2312     g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2313         &navigation_info);
2314     g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2315         &overlay_info);
2316
2317     /* register type and create class in a more safe place instead of at
2318      * runtime since the type registration and class creation is not
2319      * threadsafe. */
2320     g_type_class_ref (gst_ximage_buffer_get_type ());
2321   }
2322
2323   return ximagesink_type;
2324 }