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