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