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