sys/xvimage/xvimagesink.c: Oops - set the size of the image used for probing back...
[platform/upstream/gst-plugins-base.git] / sys / xvimage / xvimagesink.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-xvimagesink
22  *
23  * XvImageSink renders video frames to a drawable (XWindow) on a local display
24  * using the XVideo extension. Rendering to a remote display is theorically
25  * possible but i doubt that the XVideo extension is actually available when
26  * connecting to a remote display. This element can receive a Window ID from the
27  * application through the XOverlay interface and will then render video frames
28  * in this drawable. If no Window ID was provided by the application, the
29  * element will create its own internal window and render into it.
30  *
31  * <refsect2>
32  * <title>Scaling</title>
33  * <para>
34  * The XVideo extension, when it's available, handles hardware accelerated
35  * scaling of video frames. This means that the element will just accept
36  * incoming video frames no matter their geometry and will then put them to the
37  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
38  * property it is possible to enforce scaling with a constant aspect ratio,
39  * which means drawing black borders around the video frame.
40  * </para>
41  * </refsect2>
42  * <refsect2>
43  * <title>Events</title>
44  * <para>
45  * XvImageSink creates a thread to handle events coming from the drawable. There
46  * are several kind of events that can be grouped in 2 big categories: input
47  * events and window state related events. Input events will be translated to
48  * navigation events and pushed upstream for other elements to react on them.
49  * This includes events such as pointer moves, key press/release, clicks etc...
50  * Other events are used to handle the drawable appearance even when the data
51  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
52  * paused, it will receive expose events from the drawable and draw the latest
53  * frame with correct borders/aspect-ratio.
54  * </para>
55  * </refsect2>
56  * <refsect2>
57  * <title>Pixel aspect ratio</title>
58  * <para>
59  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
60  * the display specified in the #GstXvImageSink:display property or the
61  * default display if nothing specified. Once this connection is open it will
62  * inspect the display configuration including the physical display geometry and
63  * then calculate the pixel aspect ratio. When receiving video frames with a
64  * different pixel aspect ratio, XvImageSink will use hardware scaling to
65  * display the video frames correctly on display's pixel aspect ratio.
66  * Sometimes the calculated pixel aspect ratio can be wrong, it is
67  * then possible to enforce a specific pixel aspect ratio using the
68  * #GstXvImageSink:pixel-aspect-ratio property.
69  * </para>
70  * </refsect2>
71  * <refsect2>
72  * <title>Examples</title>
73  * |[
74  * gst-launch -v videotestsrc ! xvimagesink
75  * ]| A pipeline to test hardware scaling.
76  * When the test video signal appears you can resize the window and see that
77  * video frames are scaled through hardware (no extra CPU cost).
78  * |[
79  * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
80  * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
81  * You can observe the borders drawn around the scaled image respecting aspect
82  * ratio.
83  * |[
84  * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
85  * ]| A pipeline to test navigation events.
86  * While moving the mouse pointer over the test signal you will see a black box
87  * following the mouse pointer. If you press the mouse button somewhere on the
88  * video and release it somewhere else a green box will appear where you pressed
89  * the button and a red one where you released it. (The navigationtest element
90  * is part of gst-plugins-good.) You can observe here that even if the images
91  * are scaled through hardware the pointer coordinates are converted back to the
92  * original video frame geometry so that the box can be drawn to the correct
93  * position. This also handles borders correctly, limiting coordinates to the
94  * image area
95  * |[
96  * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
97  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
98  * videotestsrc, in most cases the pixel aspect ratio of the display will be
99  * 1/1. This means that XvImageSink will have to do the scaling to convert
100  * incoming frames to a size that will match the display pixel aspect ratio
101  * (from 320x240 to 320x180 in this case). Note that you might have to escape
102  * some characters for your shell like '\(fraction\)'.
103  * |[
104  * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
105  * ]| Demonstrates how to use the colorbalance interface.
106  * </refsect2>
107  */
108
109 /* for developers: there are two useful tools : xvinfo and xvattr */
110
111 #ifdef HAVE_CONFIG_H
112 #include "config.h"
113 #endif
114
115 /* Our interfaces */
116 #include <gst/interfaces/navigation.h>
117 #include <gst/interfaces/xoverlay.h>
118 #include <gst/interfaces/colorbalance.h>
119 #include <gst/interfaces/propertyprobe.h>
120 /* Helper functions */
121 #include <gst/video/video.h>
122
123 /* Object header */
124 #include "xvimagesink.h"
125
126 /* Debugging category */
127 #include <gst/gstinfo.h>
128 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
129 #define GST_CAT_DEFAULT gst_debug_xvimagesink
130
131 typedef struct
132 {
133   unsigned long flags;
134   unsigned long functions;
135   unsigned long decorations;
136   long input_mode;
137   unsigned long status;
138 }
139 MotifWmHints, MwmHints;
140
141 #define MWM_HINTS_DECORATIONS   (1L << 1)
142
143 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
144
145 static GstBufferClass *xvimage_buffer_parent_class = NULL;
146 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
147
148 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
149     xvimagesink, GstXWindow * xwindow);
150 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
151     GstCaps * caps);
152 static void gst_xvimagesink_expose (GstXOverlay * overlay);
153
154 /* ElementFactory information */
155 static const GstElementDetails gst_xvimagesink_details =
156 GST_ELEMENT_DETAILS ("Video sink",
157     "Sink/Video",
158     "A Xv based videosink",
159     "Julien Moutte <julien@moutte.net>");
160
161 /* Default template - initiated with class struct to allow gst-register to work
162    without X running */
163 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
164     GST_STATIC_PAD_TEMPLATE ("sink",
165     GST_PAD_SINK,
166     GST_PAD_ALWAYS,
167     GST_STATIC_CAPS ("video/x-raw-rgb, "
168         "framerate = (fraction) [ 0, MAX ], "
169         "width = (int) [ 1, MAX ], "
170         "height = (int) [ 1, MAX ]; "
171         "video/x-raw-yuv, "
172         "framerate = (fraction) [ 0, MAX ], "
173         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
174     );
175
176 enum
177 {
178   ARG_0,
179   ARG_CONTRAST,
180   ARG_BRIGHTNESS,
181   ARG_HUE,
182   ARG_SATURATION,
183   ARG_DISPLAY,
184   ARG_SYNCHRONOUS,
185   ARG_PIXEL_ASPECT_RATIO,
186   ARG_FORCE_ASPECT_RATIO,
187   ARG_HANDLE_EVENTS,
188   ARG_DEVICE,
189   ARG_DEVICE_NAME,
190   ARG_HANDLE_EXPOSE,
191   ARG_DOUBLE_BUFFER
192 };
193
194 static GstVideoSinkClass *parent_class = NULL;
195
196 /* ============================================================= */
197 /*                                                               */
198 /*                       Private Methods                         */
199 /*                                                               */
200 /* ============================================================= */
201
202 /* xvimage buffers */
203
204 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
205
206 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
207 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
208
209 /* This function destroys a GstXvImage handling XShm availability */
210 static void
211 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
212 {
213   GstXvImageSink *xvimagesink;
214
215   GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
216
217   xvimagesink = xvimage->xvimagesink;
218   if (G_UNLIKELY (xvimagesink == NULL))
219     goto no_sink;
220
221   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
222
223   GST_OBJECT_LOCK (xvimagesink);
224
225   /* If the destroyed image is the current one we destroy our reference too */
226   if (xvimagesink->cur_image == xvimage)
227     xvimagesink->cur_image = NULL;
228
229   /* We might have some buffers destroyed after changing state to NULL */
230   if (xvimagesink->xcontext == NULL) {
231     GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
232 #ifdef HAVE_XSHM
233     /* Need to free the shared memory segment even if the x context
234      * was already cleaned up */
235     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
236       shmdt (xvimage->SHMInfo.shmaddr);
237     }
238 #endif
239     goto beach;
240   }
241
242   g_mutex_lock (xvimagesink->x_lock);
243
244 #ifdef HAVE_XSHM
245   if (xvimagesink->xcontext->use_xshm) {
246     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
247       GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
248           xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
249       XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
250       XSync (xvimagesink->xcontext->disp, FALSE);
251
252       shmdt (xvimage->SHMInfo.shmaddr);
253     }
254     if (xvimage->xvimage)
255       XFree (xvimage->xvimage);
256   } else
257 #endif /* HAVE_XSHM */
258   {
259     if (xvimage->xvimage) {
260       if (xvimage->xvimage->data) {
261         g_free (xvimage->xvimage->data);
262       }
263       XFree (xvimage->xvimage);
264     }
265   }
266
267   XSync (xvimagesink->xcontext->disp, FALSE);
268
269   g_mutex_unlock (xvimagesink->x_lock);
270
271 beach:
272   GST_OBJECT_UNLOCK (xvimagesink);
273   xvimage->xvimagesink = NULL;
274   gst_object_unref (xvimagesink);
275
276   GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->
277       finalize (GST_MINI_OBJECT (xvimage));
278
279   return;
280
281 no_sink:
282   {
283     GST_WARNING ("no sink found");
284     return;
285   }
286 }
287
288 static void
289 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
290 {
291   GstXvImageSink *xvimagesink;
292   gboolean running;
293
294   xvimagesink = xvimage->xvimagesink;
295   if (G_UNLIKELY (xvimagesink == NULL))
296     goto no_sink;
297
298   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
299
300   GST_OBJECT_LOCK (xvimagesink);
301   running = xvimagesink->running;
302   GST_OBJECT_UNLOCK (xvimagesink);
303
304   /* If our geometry changed we can't reuse that image. */
305   if (running == FALSE) {
306     GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
307     gst_xvimage_buffer_destroy (xvimage);
308   } else if ((xvimage->width != xvimagesink->video_width) ||
309       (xvimage->height != xvimagesink->video_height)) {
310     GST_LOG_OBJECT (xvimage,
311         "destroy image as its size changed %dx%d vs current %dx%d",
312         xvimage->width, xvimage->height,
313         xvimagesink->video_width, xvimagesink->video_height);
314     gst_xvimage_buffer_destroy (xvimage);
315   } else {
316     /* In that case we can reuse the image and add it to our image pool. */
317     GST_LOG_OBJECT (xvimage, "recycling image in pool");
318     /* need to increment the refcount again to recycle */
319     gst_buffer_ref (GST_BUFFER (xvimage));
320     g_mutex_lock (xvimagesink->pool_lock);
321     xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
322         xvimage);
323     g_mutex_unlock (xvimagesink->pool_lock);
324   }
325   return;
326
327 no_sink:
328   {
329     GST_WARNING ("no sink found");
330     return;
331   }
332 }
333
334 static void
335 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
336 {
337   /* make sure it is not recycled */
338   xvimage->width = -1;
339   xvimage->height = -1;
340   gst_buffer_unref (GST_BUFFER (xvimage));
341 }
342
343 static void
344 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
345 {
346 #ifdef HAVE_XSHM
347   xvimage->SHMInfo.shmaddr = ((void *) -1);
348   xvimage->SHMInfo.shmid = -1;
349 #endif
350 }
351
352 static void
353 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
354 {
355   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
356
357   xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
358
359   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
360       gst_xvimage_buffer_finalize;
361 }
362
363 static GType
364 gst_xvimage_buffer_get_type (void)
365 {
366   static GType _gst_xvimage_buffer_type;
367
368   if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
369     static const GTypeInfo xvimage_buffer_info = {
370       sizeof (GstBufferClass),
371       NULL,
372       NULL,
373       gst_xvimage_buffer_class_init,
374       NULL,
375       NULL,
376       sizeof (GstXvImageBuffer),
377       0,
378       (GInstanceInitFunc) gst_xvimage_buffer_init,
379       NULL
380     };
381     _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
382         "GstXvImageBuffer", &xvimage_buffer_info, 0);
383   }
384   return _gst_xvimage_buffer_type;
385 }
386
387 /* X11 stuff */
388
389 static gboolean error_caught = FALSE;
390
391 static int
392 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
393 {
394   char error_msg[1024];
395
396   XGetErrorText (display, xevent->error_code, error_msg, 1024);
397   GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
398   error_caught = TRUE;
399   return 0;
400 }
401
402 #ifdef HAVE_XSHM
403 /* This function checks that it is actually really possible to create an image
404    using XShm */
405 static gboolean
406 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
407 {
408   XvImage *xvimage;
409   XShmSegmentInfo SHMInfo;
410   gint size;
411   int (*handler) (Display *, XErrorEvent *);
412   gboolean result = FALSE;
413   gboolean did_attach = FALSE;
414
415   g_return_val_if_fail (xcontext != NULL, FALSE);
416
417   /* Sync to ensure any older errors are already processed */
418   XSync (xcontext->disp, FALSE);
419
420   /* Set defaults so we don't free these later unnecessarily */
421   SHMInfo.shmaddr = ((void *) -1);
422   SHMInfo.shmid = -1;
423
424   /* Setting an error handler to catch failure */
425   error_caught = FALSE;
426   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
427
428   /* Trying to create a 1x1 picture */
429   GST_DEBUG ("XvShmCreateImage of 1x1");
430   xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
431       xcontext->im_format, NULL, 1, 1, &SHMInfo);
432
433   /* Might cause an error, sync to ensure it is noticed */
434   XSync (xcontext->disp, FALSE);
435   if (!xvimage || error_caught) {
436     GST_WARNING ("could not XvShmCreateImage a 1x1 image");
437     goto beach;
438   }
439   size = xvimage->data_size;
440
441   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
442   if (SHMInfo.shmid == -1) {
443     GST_WARNING ("could not get shared memory of %d bytes", size);
444     goto beach;
445   }
446
447   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
448   if (SHMInfo.shmaddr == ((void *) -1)) {
449     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
450     /* Clean up the shared memory segment */
451     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
452     goto beach;
453   }
454
455   xvimage->data = SHMInfo.shmaddr;
456   SHMInfo.readOnly = FALSE;
457
458   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
459     GST_WARNING ("Failed to XShmAttach");
460     /* Clean up the shared memory segment */
461     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
462     goto beach;
463   }
464
465   /* Sync to ensure we see any errors we caused */
466   XSync (xcontext->disp, FALSE);
467
468   /* Delete the shared memory segment as soon as everyone is attached.
469    * This way, it will be deleted as soon as we detach later, and not
470    * leaked if we crash. */
471   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
472
473   if (!error_caught) {
474     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
475         SHMInfo.shmseg);
476
477     did_attach = TRUE;
478     /* store whether we succeeded in result */
479     result = TRUE;
480   } else {
481     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
482         "Not using shared memory.");
483   }
484
485 beach:
486   /* Sync to ensure we swallow any errors we caused and reset error_caught */
487   XSync (xcontext->disp, FALSE);
488
489   error_caught = FALSE;
490   XSetErrorHandler (handler);
491
492   if (did_attach) {
493     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
494         SHMInfo.shmid, SHMInfo.shmseg);
495     XShmDetach (xcontext->disp, &SHMInfo);
496     XSync (xcontext->disp, FALSE);
497   }
498   if (SHMInfo.shmaddr != ((void *) -1))
499     shmdt (SHMInfo.shmaddr);
500   if (xvimage)
501     XFree (xvimage);
502   return result;
503 }
504 #endif /* HAVE_XSHM */
505
506 /* This function handles GstXvImage creation depending on XShm availability */
507 static GstXvImageBuffer *
508 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
509 {
510   GstXvImageBuffer *xvimage = NULL;
511   GstStructure *structure = NULL;
512   gboolean succeeded = FALSE;
513   int (*handler) (Display *, XErrorEvent *);
514
515   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
516
517   xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
518   GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
519
520   structure = gst_caps_get_structure (caps, 0);
521
522   if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
523       !gst_structure_get_int (structure, "height", &xvimage->height)) {
524     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
525   }
526
527   GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
528       xvimage->height);
529
530   xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
531   if (xvimage->im_format == -1) {
532     GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
533         GST_PTR_FORMAT, caps);
534     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
535         ("Failed to create output image buffer of %dx%d pixels",
536             xvimage->width, xvimage->height), ("Invalid input caps"));
537     goto beach_unlocked;
538   }
539   xvimage->xvimagesink = gst_object_ref (xvimagesink);
540
541   g_mutex_lock (xvimagesink->x_lock);
542
543   /* Setting an error handler to catch failure */
544   error_caught = FALSE;
545   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
546
547 #ifdef HAVE_XSHM
548   if (xvimagesink->xcontext->use_xshm) {
549     int expected_size;
550
551     xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
552         xvimagesink->xcontext->xv_port_id,
553         xvimage->im_format, NULL,
554         xvimage->width, xvimage->height, &xvimage->SHMInfo);
555     if (!xvimage->xvimage || error_caught) {
556       g_mutex_unlock (xvimagesink->x_lock);
557       /* Reset error handler */
558       error_caught = FALSE;
559       XSetErrorHandler (handler);
560       /* Push an error */
561       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
562           ("Failed to create output image buffer of %dx%d pixels",
563               xvimage->width, xvimage->height),
564           ("could not XvShmCreateImage a %dx%d image",
565               xvimage->width, xvimage->height));
566       goto beach_unlocked;
567     }
568
569     /* we have to use the returned data_size for our shm size */
570     xvimage->size = xvimage->xvimage->data_size;
571     GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
572         xvimage->size);
573
574     /* calculate the expected size.  This is only for sanity checking the
575      * number we get from X. */
576     switch (xvimage->im_format) {
577       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
578       case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
579         expected_size =
580             GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_4 (xvimage->width);
581         expected_size +=
582             GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_8 (xvimage->width) /
583             2;
584         break;
585       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
586       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
587         expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
588         break;
589       default:
590         expected_size = 0;
591         break;
592     }
593     if (expected_size != 0 && xvimage->size != expected_size) {
594       GST_WARNING_OBJECT (xvimagesink,
595           "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
596           xvimage->size, expected_size);
597     }
598
599     /* Be verbose about our XvImage stride */
600     {
601       guint plane;
602
603       for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
604         GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
605             "offset of %d", plane, xvimage->xvimage->pitches[plane],
606             xvimage->xvimage->offsets[plane]);
607       }
608     }
609
610     xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
611         IPC_CREAT | 0777);
612     if (xvimage->SHMInfo.shmid == -1) {
613       g_mutex_unlock (xvimagesink->x_lock);
614       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
615           ("Failed to create output image buffer of %dx%d pixels",
616               xvimage->width, xvimage->height),
617           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
618               xvimage->size));
619       goto beach_unlocked;
620     }
621
622     xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
623     if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
624       g_mutex_unlock (xvimagesink->x_lock);
625       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
626           ("Failed to create output image buffer of %dx%d pixels",
627               xvimage->width, xvimage->height),
628           ("Failed to shmat: %s", g_strerror (errno)));
629       /* Clean up the shared memory segment */
630       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
631       goto beach_unlocked;
632     }
633
634     xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
635     xvimage->SHMInfo.readOnly = FALSE;
636
637     if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
638       /* Clean up the shared memory segment */
639       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
640
641       g_mutex_unlock (xvimagesink->x_lock);
642       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
643           ("Failed to create output image buffer of %dx%d pixels",
644               xvimage->width, xvimage->height), ("Failed to XShmAttach"));
645       goto beach_unlocked;
646     }
647
648     XSync (xvimagesink->xcontext->disp, FALSE);
649
650     /* Delete the shared memory segment as soon as we everyone is attached.
651      * This way, it will be deleted as soon as we detach later, and not
652      * leaked if we crash. */
653     shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
654
655     GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
656         xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
657   } else
658 #endif /* HAVE_XSHM */
659   {
660     xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
661         xvimagesink->xcontext->xv_port_id,
662         xvimage->im_format, NULL, xvimage->width, xvimage->height);
663     if (!xvimage->xvimage || error_caught) {
664       g_mutex_unlock (xvimagesink->x_lock);
665       /* Reset error handler */
666       error_caught = FALSE;
667       XSetErrorHandler (handler);
668       /* Push an error */
669       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
670           ("Failed to create outputimage buffer of %dx%d pixels",
671               xvimage->width, xvimage->height),
672           ("could not XvCreateImage a %dx%d image",
673               xvimage->width, xvimage->height));
674       goto beach_unlocked;
675     }
676
677     /* we have to use the returned data_size for our image size */
678     xvimage->size = xvimage->xvimage->data_size;
679     xvimage->xvimage->data = g_malloc (xvimage->size);
680
681     XSync (xvimagesink->xcontext->disp, FALSE);
682   }
683
684   /* Reset error handler */
685   error_caught = FALSE;
686   XSetErrorHandler (handler);
687
688   succeeded = TRUE;
689
690   GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
691   GST_BUFFER_SIZE (xvimage) = xvimage->size;
692
693   g_mutex_unlock (xvimagesink->x_lock);
694
695 beach_unlocked:
696   if (!succeeded) {
697     gst_xvimage_buffer_free (xvimage);
698     xvimage = NULL;
699   }
700
701   return xvimage;
702 }
703
704 /* We are called with the x_lock taken */
705 static void
706 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
707     GstXWindow * xwindow, GstVideoRectangle rect)
708 {
709   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
710   g_return_if_fail (xwindow != NULL);
711
712   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
713       xvimagesink->xcontext->black);
714
715   /* Left border */
716   if (rect.x > 0) {
717     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
718         0, 0, rect.x, xwindow->height);
719   }
720
721   /* Right border */
722   if ((rect.x + rect.w) < xwindow->width) {
723     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
724         rect.x + rect.w, 0, xwindow->width, xwindow->height);
725   }
726
727   /* Top border */
728   if (rect.y > 0) {
729     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
730         0, 0, xwindow->width, rect.y);
731   }
732
733   /* Bottom border */
734   if ((rect.y + rect.h) < xwindow->height) {
735     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
736         0, rect.y + rect.h, xwindow->width, xwindow->height);
737   }
738 }
739
740 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
741  * if no window was available  */
742 static gboolean
743 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
744     GstXvImageBuffer * xvimage)
745 {
746   GstVideoRectangle src, dst, result;
747   gboolean draw_border = FALSE;
748
749   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
750
751   /* We take the flow_lock. If expose is in there we don't want to run
752      concurrently from the data flow thread */
753   g_mutex_lock (xvimagesink->flow_lock);
754
755   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
756     g_mutex_unlock (xvimagesink->flow_lock);
757     return FALSE;
758   }
759
760   /* Draw borders when displaying the first frame. After this
761      draw borders only on expose event or after a size change. */
762   if (!xvimagesink->cur_image || xvimagesink->draw_border) {
763     draw_border = TRUE;
764   }
765
766   /* Store a reference to the last image we put, lose the previous one */
767   if (xvimage && xvimagesink->cur_image != xvimage) {
768     if (xvimagesink->cur_image) {
769       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
770       gst_buffer_unref (xvimagesink->cur_image);
771     }
772     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
773     xvimagesink->cur_image =
774         GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
775   }
776
777   /* Expose sends a NULL image, we take the latest frame */
778   if (!xvimage) {
779     draw_border = TRUE;
780     if (xvimagesink->cur_image) {
781       xvimage = xvimagesink->cur_image;
782     } else {
783       g_mutex_unlock (xvimagesink->flow_lock);
784       return TRUE;
785     }
786   }
787
788   gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);
789
790   /* We use the calculated geometry from _setcaps as a source to respect
791      source and screen pixel aspect ratios. */
792   src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
793   src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
794   dst.w = xvimagesink->xwindow->width;
795   dst.h = xvimagesink->xwindow->height;
796
797   if (xvimagesink->keep_aspect) {
798     gst_video_sink_center_rect (src, dst, &result, TRUE);
799   } else {
800     result.x = result.y = 0;
801     result.w = dst.w;
802     result.h = dst.h;
803   }
804
805   g_mutex_lock (xvimagesink->x_lock);
806
807   if (draw_border) {
808     gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
809         result);
810     xvimagesink->draw_border = FALSE;
811   }
812
813   /* We scale to the window's geometry */
814 #ifdef HAVE_XSHM
815   if (xvimagesink->xcontext->use_xshm) {
816     GST_LOG_OBJECT (xvimagesink,
817         "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
818         GST_PTR_FORMAT,
819         xvimage->width, xvimage->height,
820         xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);
821
822     XvShmPutImage (xvimagesink->xcontext->disp,
823         xvimagesink->xcontext->xv_port_id,
824         xvimagesink->xwindow->win,
825         xvimagesink->xwindow->gc, xvimage->xvimage,
826         0, 0, xvimage->width, xvimage->height,
827         result.x, result.y, result.w, result.h, FALSE);
828   } else
829 #endif /* HAVE_XSHM */
830   {
831     XvPutImage (xvimagesink->xcontext->disp,
832         xvimagesink->xcontext->xv_port_id,
833         xvimagesink->xwindow->win,
834         xvimagesink->xwindow->gc, xvimage->xvimage,
835         0, 0, xvimage->width, xvimage->height,
836         result.x, result.y, result.w, result.h);
837   }
838
839   XSync (xvimagesink->xcontext->disp, FALSE);
840
841   g_mutex_unlock (xvimagesink->x_lock);
842
843   g_mutex_unlock (xvimagesink->flow_lock);
844
845   return TRUE;
846 }
847
848 static gboolean
849 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
850     GstXWindow * window)
851 {
852   Atom hints_atom = None;
853   MotifWmHints *hints;
854
855   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
856   g_return_val_if_fail (window != NULL, FALSE);
857
858   g_mutex_lock (xvimagesink->x_lock);
859
860   hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
861       True);
862   if (hints_atom == None) {
863     g_mutex_unlock (xvimagesink->x_lock);
864     return FALSE;
865   }
866
867   hints = g_malloc0 (sizeof (MotifWmHints));
868
869   hints->flags |= MWM_HINTS_DECORATIONS;
870   hints->decorations = 1 << 0;
871
872   XChangeProperty (xvimagesink->xcontext->disp, window->win,
873       hints_atom, hints_atom, 32, PropModeReplace,
874       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
875
876   XSync (xvimagesink->xcontext->disp, FALSE);
877
878   g_mutex_unlock (xvimagesink->x_lock);
879
880   g_free (hints);
881
882   return TRUE;
883 }
884
885 /* This function handles a GstXWindow creation
886  * The width and height are the actual pixel size on the display */
887 static GstXWindow *
888 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
889     gint width, gint height)
890 {
891   GstXWindow *xwindow = NULL;
892   XGCValues values;
893
894   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
895
896   xwindow = g_new0 (GstXWindow, 1);
897
898   xwindow->width = width;
899   xwindow->height = height;
900   xwindow->internal = TRUE;
901
902   g_mutex_lock (xvimagesink->x_lock);
903
904   xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
905       xvimagesink->xcontext->root,
906       0, 0, xwindow->width, xwindow->height,
907       0, 0, xvimagesink->xcontext->black);
908
909   /* We have to do that to prevent X from redrawing the background on
910    * ConfigureNotify. This takes away flickering of video when resizing. */
911   XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
912
913   if (xvimagesink->handle_events) {
914     Atom wm_delete;
915
916     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
917         StructureNotifyMask | PointerMotionMask | KeyPressMask |
918         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
919
920     /* Tell the window manager we'd like delete client messages instead of
921      * being killed */
922     wm_delete = XInternAtom (xvimagesink->xcontext->disp,
923         "WM_DELETE_WINDOW", True);
924     if (wm_delete != None) {
925       (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
926           &wm_delete, 1);
927     }
928   }
929
930   xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
931       xwindow->win, 0, &values);
932
933   XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
934
935   XSync (xvimagesink->xcontext->disp, FALSE);
936
937   g_mutex_unlock (xvimagesink->x_lock);
938
939   gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
940
941   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win);
942
943   return xwindow;
944 }
945
946 /* This function destroys a GstXWindow */
947 static void
948 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
949     GstXWindow * xwindow)
950 {
951   g_return_if_fail (xwindow != NULL);
952   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
953
954   g_mutex_lock (xvimagesink->x_lock);
955
956   /* If we did not create that window we just free the GC and let it live */
957   if (xwindow->internal)
958     XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
959   else
960     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
961
962   XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
963
964   XSync (xvimagesink->xcontext->disp, FALSE);
965
966   g_mutex_unlock (xvimagesink->x_lock);
967
968   g_free (xwindow);
969 }
970
971 static void
972 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
973     GstXWindow * xwindow)
974 {
975   XWindowAttributes attr;
976
977   g_return_if_fail (xwindow != NULL);
978   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
979
980   /* Update the window geometry */
981   g_mutex_lock (xvimagesink->x_lock);
982
983   XGetWindowAttributes (xvimagesink->xcontext->disp,
984       xvimagesink->xwindow->win, &attr);
985
986   xvimagesink->xwindow->width = attr.width;
987   xvimagesink->xwindow->height = attr.height;
988
989   g_mutex_unlock (xvimagesink->x_lock);
990 }
991
992 static void
993 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
994     GstXWindow * xwindow)
995 {
996   g_return_if_fail (xwindow != NULL);
997   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
998
999   g_mutex_lock (xvimagesink->x_lock);
1000
1001   XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1002       xwindow->win);
1003
1004   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
1005       xvimagesink->xcontext->black);
1006
1007   XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1008       0, 0, xwindow->width, xwindow->height);
1009
1010   XSync (xvimagesink->xcontext->disp, FALSE);
1011
1012   g_mutex_unlock (xvimagesink->x_lock);
1013 }
1014
1015 /* This function commits our internal colorbalance settings to our grabbed Xv
1016    port. If the xcontext is not initialized yet it simply returns */
1017 static void
1018 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1019 {
1020   GList *channels = NULL;
1021
1022   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1023
1024   /* If we haven't initialized the X context we can't update anything */
1025   if (xvimagesink->xcontext == NULL)
1026     return;
1027
1028   /* Don't set the attributes if they haven't been changed, to avoid
1029    * rounding errors changing the values */
1030   if (!xvimagesink->cb_changed)
1031     return;
1032
1033   /* For each channel of the colorbalance we calculate the correct value
1034      doing range conversion and then set the Xv port attribute to match our
1035      values. */
1036   channels = xvimagesink->xcontext->channels_list;
1037
1038   while (channels) {
1039     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1040       GstColorBalanceChannel *channel = NULL;
1041       Atom prop_atom;
1042       gint value = 0;
1043       gdouble convert_coef;
1044
1045       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1046       g_object_ref (channel);
1047
1048       /* Our range conversion coef */
1049       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1050
1051       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1052         value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
1053       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1054         value = (xvimagesink->saturation + 1000) * convert_coef +
1055             channel->min_value;
1056       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1057         value = (xvimagesink->contrast + 1000) * convert_coef +
1058             channel->min_value;
1059       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1060         value = (xvimagesink->brightness + 1000) * convert_coef +
1061             channel->min_value;
1062       } else {
1063         g_warning ("got an unknown channel %s", channel->label);
1064         g_object_unref (channel);
1065         return;
1066       }
1067
1068       /* Committing to Xv port */
1069       g_mutex_lock (xvimagesink->x_lock);
1070       prop_atom =
1071           XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1072       if (prop_atom != None) {
1073         XvSetPortAttribute (xvimagesink->xcontext->disp,
1074             xvimagesink->xcontext->xv_port_id, prop_atom, value);
1075       }
1076       g_mutex_unlock (xvimagesink->x_lock);
1077
1078       g_object_unref (channel);
1079     }
1080     channels = g_list_next (channels);
1081   }
1082 }
1083
1084 /* This function handles XEvents that might be in the queue. It generates
1085    GstEvent that will be sent upstream in the pipeline to handle interactivity
1086    and navigation. It will also listen for configure events on the window to
1087    trigger caps renegotiation so on the fly software scaling can work. */
1088 static void
1089 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1090 {
1091   XEvent e;
1092   guint pointer_x = 0, pointer_y = 0;
1093   gboolean pointer_moved = FALSE;
1094   gboolean exposed = FALSE, configured = FALSE;
1095
1096   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1097
1098   /* We get all pointer motion events, only the last position is
1099      interesting. */
1100   g_mutex_lock (xvimagesink->flow_lock);
1101   g_mutex_lock (xvimagesink->x_lock);
1102   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1103           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1104     g_mutex_unlock (xvimagesink->x_lock);
1105     g_mutex_unlock (xvimagesink->flow_lock);
1106
1107     switch (e.type) {
1108       case MotionNotify:
1109         pointer_x = e.xmotion.x;
1110         pointer_y = e.xmotion.y;
1111         pointer_moved = TRUE;
1112         break;
1113       default:
1114         break;
1115     }
1116     g_mutex_lock (xvimagesink->flow_lock);
1117     g_mutex_lock (xvimagesink->x_lock);
1118   }
1119   if (pointer_moved) {
1120     g_mutex_unlock (xvimagesink->x_lock);
1121     g_mutex_unlock (xvimagesink->flow_lock);
1122
1123     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1124         pointer_x, pointer_y);
1125     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1126         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1127
1128     g_mutex_lock (xvimagesink->flow_lock);
1129     g_mutex_lock (xvimagesink->x_lock);
1130   }
1131
1132   /* We get all events on our window to throw them upstream */
1133   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1134           xvimagesink->xwindow->win,
1135           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1136           &e)) {
1137     KeySym keysym;
1138
1139     /* We lock only for the X function call */
1140     g_mutex_unlock (xvimagesink->x_lock);
1141     g_mutex_unlock (xvimagesink->flow_lock);
1142
1143     switch (e.type) {
1144       case ButtonPress:
1145         /* Mouse button pressed over our window. We send upstream
1146            events for interactivity/navigation */
1147         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1148             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1149         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1150             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1151         break;
1152       case ButtonRelease:
1153         /* Mouse button released over our window. We send upstream
1154            events for interactivity/navigation */
1155         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1156             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1157         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1158             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1159         break;
1160       case KeyPress:
1161       case KeyRelease:
1162         /* Key pressed/released over our window. We send upstream
1163            events for interactivity/navigation */
1164         GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1165             e.xkey.keycode, e.xkey.x, e.xkey.y);
1166         g_mutex_lock (xvimagesink->x_lock);
1167         keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1168             e.xkey.keycode, 0);
1169         g_mutex_unlock (xvimagesink->x_lock);
1170         if (keysym != NoSymbol) {
1171           char *key_str = NULL;
1172
1173           g_mutex_lock (xvimagesink->x_lock);
1174           key_str = XKeysymToString (keysym);
1175           g_mutex_unlock (xvimagesink->x_lock);
1176           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1177               e.type == KeyPress ? "key-press" : "key-release", key_str);
1178         } else {
1179           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1180               e.type == KeyPress ? "key-press" : "key-release", "unknown");
1181         }
1182         break;
1183       default:
1184         GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1185     }
1186     g_mutex_lock (xvimagesink->flow_lock);
1187     g_mutex_lock (xvimagesink->x_lock);
1188   }
1189
1190   /* Handle Expose */
1191   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1192           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1193     switch (e.type) {
1194       case Expose:
1195         exposed = TRUE;
1196         break;
1197       case ConfigureNotify:
1198         configured = TRUE;
1199         break;
1200       default:
1201         break;
1202     }
1203   }
1204
1205   if (xvimagesink->handle_expose && (exposed || configured)) {
1206     g_mutex_unlock (xvimagesink->x_lock);
1207     g_mutex_unlock (xvimagesink->flow_lock);
1208
1209     gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1210
1211     g_mutex_lock (xvimagesink->flow_lock);
1212     g_mutex_lock (xvimagesink->x_lock);
1213   }
1214
1215   /* Handle Display events */
1216   while (XPending (xvimagesink->xcontext->disp)) {
1217     XNextEvent (xvimagesink->xcontext->disp, &e);
1218
1219     switch (e.type) {
1220       case ClientMessage:{
1221         Atom wm_delete;
1222
1223         wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1224             "WM_DELETE_WINDOW", True);
1225         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1226           /* Handle window deletion by posting an error on the bus */
1227           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1228               ("Output window was closed"), (NULL));
1229
1230           g_mutex_unlock (xvimagesink->x_lock);
1231           gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1232           xvimagesink->xwindow = NULL;
1233           g_mutex_lock (xvimagesink->x_lock);
1234         }
1235         break;
1236       }
1237       default:
1238         break;
1239     }
1240   }
1241
1242   g_mutex_unlock (xvimagesink->x_lock);
1243   g_mutex_unlock (xvimagesink->flow_lock);
1244 }
1245
1246 static void
1247 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1248     XvAdaptorInfo * adaptors, int adaptor_no)
1249 {
1250   gint j;
1251   gint res;
1252
1253   /* Do we support XvImageMask ? */
1254   if (!(adaptors[adaptor_no].type & XvImageMask)) {
1255     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1256         adaptors[adaptor_no].name);
1257     return;
1258   }
1259
1260   /* We found such an adaptor, looking for an available port */
1261   for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1262     /* We try to grab the port */
1263     res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1264     if (Success == res) {
1265       xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1266       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1267           adaptors[adaptor_no].num_ports);
1268     } else {
1269       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1270           adaptors[adaptor_no].name, res);
1271     }
1272   }
1273 }
1274
1275 /* This function generates a caps with all supported format by the first
1276    Xv grabable port we find. We store each one of the supported formats in a
1277    format list and append the format to a newly created caps that we return
1278    If this function does not return NULL because of an error, it also grabs
1279    the port via XvGrabPort */
1280 static GstCaps *
1281 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1282     GstXContext * xcontext)
1283 {
1284   gint i;
1285   XvAdaptorInfo *adaptors;
1286   gint nb_formats;
1287   XvImageFormatValues *formats = NULL;
1288   guint nb_encodings;
1289   XvEncodingInfo *encodings = NULL;
1290   gulong max_w = G_MAXINT, max_h = G_MAXINT;
1291   GstCaps *caps = NULL;
1292   GstCaps *rgb_caps = NULL;
1293
1294   g_return_val_if_fail (xcontext != NULL, NULL);
1295
1296   /* First let's check that XVideo extension is available */
1297   if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1298     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1299         ("Could not initialise Xv output"),
1300         ("XVideo extension is not available"));
1301     return NULL;
1302   }
1303
1304   /* Then we get adaptors list */
1305   if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1306           &xcontext->nb_adaptors, &adaptors)) {
1307     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1308         ("Could not initialise Xv output"),
1309         ("Failed getting XV adaptors list"));
1310     return NULL;
1311   }
1312
1313   xcontext->xv_port_id = 0;
1314
1315   GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1316
1317   xcontext->adaptors =
1318       (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1319
1320   /* Now fill up our adaptor name array */
1321   for (i = 0; i < xcontext->nb_adaptors; i++) {
1322     xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1323   }
1324
1325   if (xvimagesink->adaptor_no >= 0 &&
1326       xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1327     /* Find xv port from user defined adaptor */
1328     gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1329         xvimagesink->adaptor_no);
1330   }
1331
1332   if (!xcontext->xv_port_id) {
1333     /* Now search for an adaptor that supports XvImageMask */
1334     for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1335       gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1336       xvimagesink->adaptor_no = i;
1337     }
1338   }
1339
1340   XvFreeAdaptorInfo (adaptors);
1341
1342   if (!xcontext->xv_port_id) {
1343     xvimagesink->adaptor_no = -1;
1344     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1345         ("Could not initialise Xv output"), ("No port available"));
1346     return NULL;
1347   }
1348
1349   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1350   {
1351     int count, todo = 3;
1352     XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1353         xcontext->xv_port_id, &count);
1354     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1355     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1356     static const char colorkey[] = "XV_COLORKEY";
1357
1358     GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1359
1360     for (i = 0; ((i < count) && todo); i++)
1361       if (!strcmp (attr[i].name, autopaint)) {
1362         const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1363
1364         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1);
1365         todo--;
1366       } else if (!strcmp (attr[i].name, dbl_buffer)) {
1367         const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1368
1369         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1370             (xvimagesink->double_buffer ? 1 : 0));
1371         todo--;
1372       } else if (!strcmp (attr[i].name, colorkey)) {
1373         /* Set the colorkey to something that is dark but hopefully won't randomly
1374          * appear on the screen elsewhere (ie not black or greys) */
1375         const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1376         guint32 ckey;
1377         guint32 keymask;
1378         gint bits;
1379         gboolean set_attr = TRUE;
1380
1381         /* Count the bits in the colorkey mask 'max' value */
1382         bits = 0;
1383         for (keymask = (guint32) (attr[i].max_value);
1384             keymask != 0; keymask >>= 1)
1385           bits++;
1386
1387         /* set a colorkey in the right format RGB565/RGB888
1388          * note that the colorkey is independent from the display
1389          * depth (xcontext->depth). We only handle these 2 cases, because
1390          * they're the only types of devices we've encountered. If we don't
1391          * recognise it, leave it alone  */
1392         if (bits == 16)
1393           ckey = (1 << 11) | (2 << 5) | 3;
1394         else if (bits == 24 || bits == 32)
1395           ckey = (1 << 16) | (2 << 8) | 3;
1396         else
1397           set_attr = FALSE;
1398
1399         if (set_attr) {
1400           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1401               (guint32) attr[i].max_value);
1402           GST_LOG_OBJECT (xvimagesink,
1403               "Setting color key for display depth %d to 0x%x",
1404               xcontext->depth, ckey);
1405
1406           XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1407               (gint) ckey);
1408         } else {
1409           GST_DEBUG_OBJECT (xvimagesink,
1410               "Unknown bit depth %d for Xv Colorkey - not adjusting", bits);
1411         }
1412         todo--;
1413       }
1414
1415     XFree (attr);
1416   }
1417
1418   /* Get the list of encodings supported by the adapter and look for the
1419    * XV_IMAGE encoding so we can determine the maximum width and height
1420    * supported */
1421   XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1422       &encodings);
1423
1424   for (i = 0; i < nb_encodings; i++) {
1425     GST_LOG_OBJECT (xvimagesink,
1426         "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1427         i, encodings[i].name, encodings[i].width, encodings[i].height,
1428         encodings[i].rate.numerator, encodings[i].rate.denominator);
1429     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1430       max_w = encodings[i].width;
1431       max_h = encodings[i].height;
1432     }
1433   }
1434
1435   XvFreeEncodingInfo (encodings);
1436
1437   /* We get all image formats supported by our port */
1438   formats = XvListImageFormats (xcontext->disp,
1439       xcontext->xv_port_id, &nb_formats);
1440   caps = gst_caps_new_empty ();
1441   for (i = 0; i < nb_formats; i++) {
1442     GstCaps *format_caps = NULL;
1443     gboolean is_rgb_format = FALSE;
1444
1445     /* We set the image format of the xcontext to an existing one. This
1446        is just some valid image format for making our xshm calls check before
1447        caps negotiation really happens. */
1448     xcontext->im_format = formats[i].id;
1449
1450     switch (formats[i].type) {
1451       case XvRGB:
1452       {
1453         XvImageFormatValues *fmt = &(formats[i]);
1454         gint endianness = G_BIG_ENDIAN;
1455
1456         if (fmt->byte_order == LSBFirst) {
1457           /* our caps system handles 24/32bpp RGB as big-endian. */
1458           if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1459             fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1460             fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1461             fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1462
1463             if (fmt->bits_per_pixel == 24) {
1464               fmt->red_mask >>= 8;
1465               fmt->green_mask >>= 8;
1466               fmt->blue_mask >>= 8;
1467             }
1468           } else
1469             endianness = G_LITTLE_ENDIAN;
1470         }
1471
1472         format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1473             "endianness", G_TYPE_INT, endianness,
1474             "depth", G_TYPE_INT, fmt->depth,
1475             "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1476             "red_mask", G_TYPE_INT, fmt->red_mask,
1477             "green_mask", G_TYPE_INT, fmt->green_mask,
1478             "blue_mask", G_TYPE_INT, fmt->blue_mask,
1479             "width", GST_TYPE_INT_RANGE, 1, max_w,
1480             "height", GST_TYPE_INT_RANGE, 1, max_h,
1481             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1482
1483         is_rgb_format = TRUE;
1484         break;
1485       }
1486       case XvYUV:
1487         format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1488             "format", GST_TYPE_FOURCC, formats[i].id,
1489             "width", GST_TYPE_INT_RANGE, 1, max_w,
1490             "height", GST_TYPE_INT_RANGE, 1, max_h,
1491             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1492         break;
1493       default:
1494         g_assert_not_reached ();
1495         break;
1496     }
1497
1498     if (format_caps) {
1499       GstXvImageFormat *format = NULL;
1500
1501       format = g_new0 (GstXvImageFormat, 1);
1502       if (format) {
1503         format->format = formats[i].id;
1504         format->caps = gst_caps_copy (format_caps);
1505         xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1506       }
1507
1508       if (is_rgb_format) {
1509         if (rgb_caps == NULL)
1510           rgb_caps = format_caps;
1511         else
1512           gst_caps_append (rgb_caps, format_caps);
1513       } else
1514         gst_caps_append (caps, format_caps);
1515     }
1516   }
1517
1518   /* Collected all caps into either the caps or rgb_caps structures.
1519    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1520   if (rgb_caps)
1521     gst_caps_append (caps, rgb_caps);
1522
1523   if (formats)
1524     XFree (formats);
1525
1526   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1527
1528   if (gst_caps_is_empty (caps)) {
1529     gst_caps_unref (caps);
1530     XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1531     GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1532         ("No supported format found"));
1533     return NULL;
1534   }
1535
1536   return caps;
1537 }
1538
1539 static gpointer
1540 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1541 {
1542   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1543
1544   GST_OBJECT_LOCK (xvimagesink);
1545   while (xvimagesink->running) {
1546     GST_OBJECT_UNLOCK (xvimagesink);
1547
1548     if (xvimagesink->xwindow) {
1549       gst_xvimagesink_handle_xevents (xvimagesink);
1550     }
1551     g_usleep (50000);
1552
1553     GST_OBJECT_LOCK (xvimagesink);
1554   }
1555   GST_OBJECT_UNLOCK (xvimagesink);
1556
1557   return NULL;
1558 }
1559
1560 /* This function calculates the pixel aspect ratio based on the properties
1561  * in the xcontext structure and stores it there. */
1562 static void
1563 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1564 {
1565   static const gint par[][2] = {
1566     {1, 1},                     /* regular screen */
1567     {16, 15},                   /* PAL TV */
1568     {11, 10},                   /* 525 line Rec.601 video */
1569     {54, 59},                   /* 625 line Rec.601 video */
1570     {64, 45},                   /* 1280x1024 on 16:9 display */
1571     {5, 3},                     /* 1280x1024 on 4:3 display */
1572     {4, 3}                      /*  800x600 on 16:9 display */
1573   };
1574   gint i;
1575   gint index;
1576   gdouble ratio;
1577   gdouble delta;
1578
1579 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1580
1581   /* first calculate the "real" ratio based on the X values;
1582    * which is the "physical" w/h divided by the w/h in pixels of the display */
1583   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1584       / (xcontext->heightmm * xcontext->width);
1585
1586   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1587    * override here */
1588   if (xcontext->width == 720 && xcontext->height == 576) {
1589     ratio = 4.0 * 576 / (3.0 * 720);
1590   }
1591   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1592   /* now find the one from par[][2] with the lowest delta to the real one */
1593   delta = DELTA (0);
1594   index = 0;
1595
1596   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1597     gdouble this_delta = DELTA (i);
1598
1599     if (this_delta < delta) {
1600       index = i;
1601       delta = this_delta;
1602     }
1603   }
1604
1605   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1606       par[index][0], par[index][1]);
1607
1608   g_free (xcontext->par);
1609   xcontext->par = g_new0 (GValue, 1);
1610   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1611   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1612   GST_DEBUG ("set xcontext PAR to %d/%d",
1613       gst_value_get_fraction_numerator (xcontext->par),
1614       gst_value_get_fraction_denominator (xcontext->par));
1615 }
1616
1617 /* This function gets the X Display and global info about it. Everything is
1618    stored in our object and will be cleaned when the object is disposed. Note
1619    here that caps for supported format are generated without any window or
1620    image creation */
1621 static GstXContext *
1622 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1623 {
1624   GstXContext *xcontext = NULL;
1625   XPixmapFormatValues *px_formats = NULL;
1626   gint nb_formats = 0, i, j, N_attr;
1627   XvAttribute *xv_attr;
1628   Atom prop_atom;
1629   char *channels[4] = { "XV_HUE", "XV_SATURATION",
1630     "XV_BRIGHTNESS", "XV_CONTRAST"
1631   };
1632
1633   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1634
1635   xcontext = g_new0 (GstXContext, 1);
1636   xcontext->im_format = 0;
1637
1638   g_mutex_lock (xvimagesink->x_lock);
1639
1640   xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1641
1642   if (!xcontext->disp) {
1643     g_mutex_unlock (xvimagesink->x_lock);
1644     g_free (xcontext);
1645     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1646         ("Could not initialise Xv output"), ("Could not open display"));
1647     return NULL;
1648   }
1649
1650   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1651   xcontext->screen_num = DefaultScreen (xcontext->disp);
1652   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1653   xcontext->root = DefaultRootWindow (xcontext->disp);
1654   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1655   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1656   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1657
1658   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1659   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1660   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1661   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1662
1663   GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1664       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1665
1666   gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1667   /* We get supported pixmap formats at supported depth */
1668   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1669
1670   if (!px_formats) {
1671     XCloseDisplay (xcontext->disp);
1672     g_mutex_unlock (xvimagesink->x_lock);
1673     g_free (xcontext->par);
1674     g_free (xcontext);
1675     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1676         ("Could not initialise Xv output"), ("Could not get pixel formats"));
1677     return NULL;
1678   }
1679
1680   /* We get bpp value corresponding to our running depth */
1681   for (i = 0; i < nb_formats; i++) {
1682     if (px_formats[i].depth == xcontext->depth)
1683       xcontext->bpp = px_formats[i].bits_per_pixel;
1684   }
1685
1686   XFree (px_formats);
1687
1688   xcontext->endianness =
1689       (ImageByteOrder (xcontext->disp) ==
1690       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1691
1692   /* our caps system handles 24/32bpp RGB as big-endian. */
1693   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1694       xcontext->endianness == G_LITTLE_ENDIAN) {
1695     xcontext->endianness = G_BIG_ENDIAN;
1696     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1697     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1698     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1699     if (xcontext->bpp == 24) {
1700       xcontext->visual->red_mask >>= 8;
1701       xcontext->visual->green_mask >>= 8;
1702       xcontext->visual->blue_mask >>= 8;
1703     }
1704   }
1705
1706   xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1707
1708   if (!xcontext->caps) {
1709     XCloseDisplay (xcontext->disp);
1710     g_mutex_unlock (xvimagesink->x_lock);
1711     g_free (xcontext->par);
1712     g_free (xcontext);
1713     /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1714     return NULL;
1715   }
1716 #ifdef HAVE_XSHM
1717   /* Search for XShm extension support */
1718   if (XShmQueryExtension (xcontext->disp) &&
1719       gst_xvimagesink_check_xshm_calls (xcontext)) {
1720     xcontext->use_xshm = TRUE;
1721     GST_DEBUG ("xvimagesink is using XShm extension");
1722   } else
1723 #endif /* HAVE_XSHM */
1724   {
1725     xcontext->use_xshm = FALSE;
1726     GST_DEBUG ("xvimagesink is not using XShm extension");
1727   }
1728
1729   xv_attr = XvQueryPortAttributes (xcontext->disp,
1730       xcontext->xv_port_id, &N_attr);
1731
1732
1733   /* Generate the channels list */
1734   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1735     XvAttribute *matching_attr = NULL;
1736
1737     /* Retrieve the property atom if it exists. If it doesn't exist, 
1738      * the attribute itself must not either, so we can skip */
1739     prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1740     if (prop_atom == None)
1741       continue;
1742
1743     if (xv_attr != NULL) {
1744       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1745         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1746           matching_attr = xv_attr + j;
1747     }
1748
1749     if (matching_attr) {
1750       GstColorBalanceChannel *channel;
1751
1752       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1753       channel->label = g_strdup (channels[i]);
1754       channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1755       channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1756
1757       xcontext->channels_list = g_list_append (xcontext->channels_list,
1758           channel);
1759
1760       /* If the colorbalance settings have not been touched we get Xv values
1761          as defaults and update our internal variables */
1762       if (!xvimagesink->cb_changed) {
1763         gint val;
1764
1765         XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1766             prop_atom, &val);
1767         /* Normalize val to [-1000, 1000] */
1768         val = -1000 + 2000 * (val - channel->min_value) /
1769             (channel->max_value - channel->min_value);
1770
1771         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1772           xvimagesink->hue = val;
1773         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1774           xvimagesink->saturation = val;
1775         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1776           xvimagesink->brightness = val;
1777         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1778           xvimagesink->contrast = val;
1779       }
1780     }
1781   }
1782
1783   if (xv_attr)
1784     XFree (xv_attr);
1785
1786   g_mutex_unlock (xvimagesink->x_lock);
1787
1788   /* Setup our event listening thread */
1789   GST_OBJECT_LOCK (xvimagesink);
1790   xvimagesink->running = TRUE;
1791   xvimagesink->event_thread = g_thread_create (
1792       (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1793   GST_OBJECT_UNLOCK (xvimagesink);
1794
1795   return xcontext;
1796 }
1797
1798 /* This function cleans the X context. Closing the Display, releasing the XV
1799    port and unrefing the caps for supported formats. */
1800 static void
1801 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1802 {
1803   GList *formats_list, *channels_list;
1804   GstXContext *xcontext;
1805   gint i = 0;
1806
1807   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1808
1809   GST_OBJECT_LOCK (xvimagesink);
1810   if (xvimagesink->xcontext == NULL) {
1811     GST_OBJECT_UNLOCK (xvimagesink);
1812     return;
1813   }
1814
1815   /* Take the XContext from the sink and clean it up */
1816   xcontext = xvimagesink->xcontext;
1817   xvimagesink->xcontext = NULL;
1818
1819   GST_OBJECT_UNLOCK (xvimagesink);
1820
1821
1822   formats_list = xcontext->formats_list;
1823
1824   while (formats_list) {
1825     GstXvImageFormat *format = formats_list->data;
1826
1827     gst_caps_unref (format->caps);
1828     g_free (format);
1829     formats_list = g_list_next (formats_list);
1830   }
1831
1832   if (xcontext->formats_list)
1833     g_list_free (xcontext->formats_list);
1834
1835   channels_list = xcontext->channels_list;
1836
1837   while (channels_list) {
1838     GstColorBalanceChannel *channel = channels_list->data;
1839
1840     g_object_unref (channel);
1841     channels_list = g_list_next (channels_list);
1842   }
1843
1844   if (xcontext->channels_list)
1845     g_list_free (xcontext->channels_list);
1846
1847   gst_caps_unref (xcontext->caps);
1848   if (xcontext->last_caps)
1849     gst_caps_replace (&xcontext->last_caps, NULL);
1850
1851   for (i = 0; i < xcontext->nb_adaptors; i++) {
1852     g_free (xcontext->adaptors[i]);
1853   }
1854
1855   g_free (xcontext->adaptors);
1856
1857   g_free (xcontext->par);
1858
1859   g_mutex_lock (xvimagesink->x_lock);
1860
1861   GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1862
1863   XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1864
1865   XCloseDisplay (xcontext->disp);
1866
1867   g_mutex_unlock (xvimagesink->x_lock);
1868
1869   g_free (xcontext);
1870 }
1871
1872 static void
1873 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
1874 {
1875   g_mutex_lock (xvimagesink->pool_lock);
1876
1877   while (xvimagesink->image_pool) {
1878     GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
1879
1880     xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
1881         xvimagesink->image_pool);
1882     gst_xvimage_buffer_free (xvimage);
1883   }
1884
1885   g_mutex_unlock (xvimagesink->pool_lock);
1886 }
1887
1888 /* Element stuff */
1889
1890 /* This function tries to get a format matching with a given caps in the
1891    supported list of formats we generated in gst_xvimagesink_get_xv_support */
1892 static gint
1893 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
1894     GstCaps * caps)
1895 {
1896   GList *list = NULL;
1897
1898   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1899
1900   list = xvimagesink->xcontext->formats_list;
1901
1902   while (list) {
1903     GstXvImageFormat *format = list->data;
1904
1905     if (format) {
1906       GstCaps *icaps = NULL;
1907
1908       icaps = gst_caps_intersect (caps, format->caps);
1909       if (!gst_caps_is_empty (icaps)) {
1910         gst_caps_unref (icaps);
1911         return format->format;
1912       }
1913       gst_caps_unref (icaps);
1914     }
1915     list = g_list_next (list);
1916   }
1917
1918   return -1;
1919 }
1920
1921 static GstCaps *
1922 gst_xvimagesink_getcaps (GstBaseSink * bsink)
1923 {
1924   GstXvImageSink *xvimagesink;
1925
1926   xvimagesink = GST_XVIMAGESINK (bsink);
1927
1928   if (xvimagesink->xcontext)
1929     return gst_caps_ref (xvimagesink->xcontext->caps);
1930
1931   return
1932       gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
1933           (xvimagesink)));
1934 }
1935
1936 static gboolean
1937 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1938 {
1939   GstXvImageSink *xvimagesink;
1940   GstStructure *structure;
1941   GstCaps *intersection;
1942   guint32 im_format = 0;
1943   gboolean ret;
1944   gint video_width, video_height;
1945   gint video_par_n, video_par_d;        /* video's PAR */
1946   gint display_par_n, display_par_d;    /* display's PAR */
1947   const GValue *caps_par;
1948   const GValue *fps;
1949   guint num, den;
1950
1951   xvimagesink = GST_XVIMAGESINK (bsink);
1952
1953   GST_DEBUG_OBJECT (xvimagesink,
1954       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1955       GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1956
1957   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
1958   GST_DEBUG_OBJECT (xvimagesink, "intersection returned %" GST_PTR_FORMAT,
1959       intersection);
1960   if (gst_caps_is_empty (intersection)) {
1961     gst_caps_unref (intersection);
1962     return FALSE;
1963   }
1964
1965   gst_caps_unref (intersection);
1966
1967   structure = gst_caps_get_structure (caps, 0);
1968   ret = gst_structure_get_int (structure, "width", &video_width);
1969   ret &= gst_structure_get_int (structure, "height", &video_height);
1970   fps = gst_structure_get_value (structure, "framerate");
1971   ret &= (fps != NULL);
1972
1973   if (!ret) {
1974     GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
1975         "height or framerate from intersected caps");
1976     return FALSE;
1977   }
1978
1979   xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
1980   xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
1981
1982   xvimagesink->video_width = video_width;
1983   xvimagesink->video_height = video_height;
1984   im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
1985   if (im_format == -1) {
1986     GST_DEBUG_OBJECT (xvimagesink,
1987         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1988     return FALSE;
1989   }
1990
1991   /* get aspect ratio from caps if it's present, and
1992    * convert video width and height to a display width and height
1993    * using wd / hd = wv / hv * PARv / PARd */
1994
1995   /* get video's PAR */
1996   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1997   if (caps_par) {
1998     video_par_n = gst_value_get_fraction_numerator (caps_par);
1999     video_par_d = gst_value_get_fraction_denominator (caps_par);
2000   } else {
2001     video_par_n = 1;
2002     video_par_d = 1;
2003   }
2004   /* get display's PAR */
2005   if (xvimagesink->par) {
2006     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2007     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2008   } else {
2009     display_par_n = 1;
2010     display_par_d = 1;
2011   }
2012
2013   if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2014           video_height, video_par_n, video_par_d, display_par_n,
2015           display_par_d)) {
2016     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2017         ("Error calculating the output display ratio of the video."));
2018     return FALSE;
2019   }
2020
2021   GST_DEBUG_OBJECT (xvimagesink,
2022       "video width/height: %dx%d, calculated display ratio: %d/%d",
2023       video_width, video_height, num, den);
2024
2025   /* now find a width x height that respects this display ratio.
2026    * prefer those that have one of w/h the same as the incoming video
2027    * using wd / hd = num / den */
2028
2029   /* start with same height, because of interlaced video */
2030   /* check hd / den is an integer scale factor, and scale wd with the PAR */
2031   if (video_height % den == 0) {
2032     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2033     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2034         gst_util_uint64_scale_int (video_height, num, den);
2035     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2036   } else if (video_width % num == 0) {
2037     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2038     GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2039     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2040         gst_util_uint64_scale_int (video_width, den, num);
2041   } else {
2042     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2043     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2044         gst_util_uint64_scale_int (video_height, num, den);
2045     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2046   }
2047   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2048       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2049
2050   /* Notify application to set xwindow id now */
2051   g_mutex_lock (xvimagesink->flow_lock);
2052   if (!xvimagesink->xwindow) {
2053     g_mutex_unlock (xvimagesink->flow_lock);
2054     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2055   } else {
2056     g_mutex_unlock (xvimagesink->flow_lock);
2057   }
2058
2059   /* Creating our window and our image with the display size in pixels */
2060   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2061       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0) {
2062     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2063         ("Error calculating the output display ratio of the video."));
2064     return FALSE;
2065   }
2066
2067   g_mutex_lock (xvimagesink->flow_lock);
2068   if (!xvimagesink->xwindow) {
2069     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2070         GST_VIDEO_SINK_WIDTH (xvimagesink),
2071         GST_VIDEO_SINK_HEIGHT (xvimagesink));
2072   }
2073
2074   /* After a resize, we want to redraw the borders in case the new frame size 
2075    * doesn't cover the same area */
2076   xvimagesink->draw_border = TRUE;
2077
2078   /* We renew our xvimage only if size or format changed;
2079    * the xvimage is the same size as the video pixel size */
2080   if ((xvimagesink->xvimage) &&
2081       ((im_format != xvimagesink->xvimage->im_format) ||
2082           (video_width != xvimagesink->xvimage->width) ||
2083           (video_height != xvimagesink->xvimage->height))) {
2084     GST_DEBUG_OBJECT (xvimagesink,
2085         "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2086         GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2087         GST_FOURCC_ARGS (im_format));
2088     GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2089     gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2090     xvimagesink->xvimage = NULL;
2091   }
2092
2093   g_mutex_unlock (xvimagesink->flow_lock);
2094
2095   return TRUE;
2096 }
2097
2098 static GstStateChangeReturn
2099 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2100 {
2101   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2102   GstXvImageSink *xvimagesink;
2103   GstXContext *xcontext = NULL;
2104
2105   xvimagesink = GST_XVIMAGESINK (element);
2106
2107   switch (transition) {
2108     case GST_STATE_CHANGE_NULL_TO_READY:
2109       /* Initializing the XContext */
2110       if (xvimagesink->xcontext == NULL) {
2111         xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2112         if (xcontext == NULL)
2113           return GST_STATE_CHANGE_FAILURE;
2114         GST_OBJECT_LOCK (xvimagesink);
2115         if (xcontext)
2116           xvimagesink->xcontext = xcontext;
2117         GST_OBJECT_UNLOCK (xvimagesink);
2118       }
2119
2120       /* update object's par with calculated one if not set yet */
2121       if (!xvimagesink->par) {
2122         xvimagesink->par = g_new0 (GValue, 1);
2123         gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2124         GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2125       }
2126       /* call XSynchronize with the current value of synchronous */
2127       GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2128           xvimagesink->synchronous ? "TRUE" : "FALSE");
2129       XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2130       gst_xvimagesink_update_colorbalance (xvimagesink);
2131       break;
2132     case GST_STATE_CHANGE_READY_TO_PAUSED:
2133       g_mutex_lock (xvimagesink->flow_lock);
2134       if (xvimagesink->xwindow)
2135         gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2136       g_mutex_unlock (xvimagesink->flow_lock);
2137       break;
2138     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2139       break;
2140     default:
2141       break;
2142   }
2143
2144   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2145
2146   switch (transition) {
2147     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2148       break;
2149     case GST_STATE_CHANGE_PAUSED_TO_READY:
2150       xvimagesink->fps_n = 0;
2151       xvimagesink->fps_d = 1;
2152       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2153       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2154       break;
2155     case GST_STATE_CHANGE_READY_TO_NULL:
2156       gst_xvimagesink_reset (xvimagesink);
2157       break;
2158     default:
2159       break;
2160   }
2161
2162   return ret;
2163 }
2164
2165 static void
2166 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2167     GstClockTime * start, GstClockTime * end)
2168 {
2169   GstXvImageSink *xvimagesink;
2170
2171   xvimagesink = GST_XVIMAGESINK (bsink);
2172
2173   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2174     *start = GST_BUFFER_TIMESTAMP (buf);
2175     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2176       *end = *start + GST_BUFFER_DURATION (buf);
2177     } else {
2178       if (xvimagesink->fps_n > 0) {
2179         *end = *start +
2180             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2181             xvimagesink->fps_n);
2182       }
2183     }
2184   }
2185 }
2186
2187 static GstFlowReturn
2188 gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
2189 {
2190   GstXvImageSink *xvimagesink;
2191
2192   xvimagesink = GST_XVIMAGESINK (bsink);
2193
2194   /* If this buffer has been allocated using our buffer management we simply
2195      put the ximage which is in the PRIVATE pointer */
2196   if (GST_IS_XVIMAGE_BUFFER (buf)) {
2197     GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2198     if (!gst_xvimagesink_xvimage_put (xvimagesink, GST_XVIMAGE_BUFFER (buf)))
2199       goto no_window;
2200   } else {
2201     GST_LOG_OBJECT (xvimagesink, "slow copy into bufferpool buffer %p", buf);
2202     /* Else we have to copy the data into our private image, */
2203     /* if we have one... */
2204     if (!xvimagesink->xvimage) {
2205       GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2206
2207       xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2208           GST_BUFFER_CAPS (buf));
2209
2210       if (!xvimagesink->xvimage)
2211         /* The create method should have posted an informative error */
2212         goto no_image;
2213
2214       if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2215         GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2216             ("Failed to create output image buffer of %dx%d pixels",
2217                 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2218             ("XServer allocated buffer size did not match input buffer"));
2219
2220         gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2221         xvimagesink->xvimage = NULL;
2222         goto no_image;
2223       }
2224     }
2225
2226     memcpy (xvimagesink->xvimage->xvimage->data,
2227         GST_BUFFER_DATA (buf),
2228         MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2229
2230     if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2231       goto no_window;
2232   }
2233
2234   return GST_FLOW_OK;
2235
2236   /* ERRORS */
2237 no_image:
2238   {
2239     /* No image available. That's very bad ! */
2240     GST_WARNING_OBJECT (xvimagesink, "could not create image");
2241     return GST_FLOW_ERROR;
2242   }
2243 no_window:
2244   {
2245     /* No Window available to put our image into */
2246     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2247     return GST_FLOW_ERROR;
2248   }
2249 }
2250
2251 /* Buffer management */
2252
2253 static GstFlowReturn
2254 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2255     GstCaps * caps, GstBuffer ** buf)
2256 {
2257   GstFlowReturn ret = GST_FLOW_OK;
2258   GstXvImageSink *xvimagesink;
2259   GstXvImageBuffer *xvimage = NULL;
2260   GstCaps *intersection = NULL;
2261   GstStructure *structure = NULL;
2262   gint width, height, image_format;
2263
2264   xvimagesink = GST_XVIMAGESINK (bsink);
2265
2266   if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2267           gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2268     GST_DEBUG_OBJECT (xvimagesink,
2269         "buffer alloc for same last_caps, reusing caps");
2270     intersection = gst_caps_ref (caps);
2271     image_format = xvimagesink->xcontext->last_format;
2272
2273     goto reuse_last_caps;
2274   }
2275
2276   GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2277       GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2278       caps, xvimagesink->xcontext->caps);
2279
2280   /* Check the caps against our xcontext */
2281   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2282
2283   /* Ensure the returned caps are fixed */
2284   gst_caps_truncate (intersection);
2285
2286   GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2287       GST_PTR_FORMAT, intersection);
2288
2289   if (gst_caps_is_empty (intersection)) {
2290     /* So we don't support this kind of buffer, let's define one we'd like */
2291     GstCaps *new_caps = gst_caps_copy (caps);
2292
2293     structure = gst_caps_get_structure (new_caps, 0);
2294
2295     /* Try with YUV first */
2296     gst_structure_set_name (structure, "video/x-raw-yuv");
2297     gst_structure_remove_field (structure, "format");
2298     gst_structure_remove_field (structure, "endianness");
2299     gst_structure_remove_field (structure, "depth");
2300     gst_structure_remove_field (structure, "bpp");
2301     gst_structure_remove_field (structure, "red_mask");
2302     gst_structure_remove_field (structure, "green_mask");
2303     gst_structure_remove_field (structure, "blue_mask");
2304     gst_structure_remove_field (structure, "alpha_mask");
2305
2306     /* Reuse intersection with Xcontext */
2307     gst_caps_unref (intersection);
2308     intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2309
2310     if (gst_caps_is_empty (intersection)) {
2311       /* Now try with RGB */
2312       gst_structure_set_name (structure, "video/x-raw-rgb");
2313       /* And interset again */
2314       gst_caps_unref (intersection);
2315       intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2316
2317       if (gst_caps_is_empty (intersection)) {
2318         GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2319             "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2320             " are completely incompatible with those caps", new_caps,
2321             xvimagesink->xcontext->caps);
2322         gst_caps_unref (new_caps);
2323         ret = GST_FLOW_UNEXPECTED;
2324         goto beach;
2325       }
2326     }
2327
2328     /* Clean this copy */
2329     gst_caps_unref (new_caps);
2330     /* We want fixed caps */
2331     gst_caps_truncate (intersection);
2332
2333     GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2334         GST_PTR_FORMAT, intersection);
2335   } else if (gst_caps_is_equal (intersection, caps)) {
2336     /* Things work better if we return a buffer with the same caps ptr
2337      * as was asked for when we can */
2338     gst_caps_replace (&intersection, caps);
2339   }
2340
2341   /* Get image format from caps */
2342   image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2343       intersection);
2344
2345   /* Store our caps and format as the last_caps to avoid expensive
2346    * caps intersection next time */
2347   gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2348   xvimagesink->xcontext->last_format = image_format;
2349
2350 reuse_last_caps:
2351
2352   /* Get geometry from caps */
2353   structure = gst_caps_get_structure (intersection, 0);
2354   if (!gst_structure_get_int (structure, "width", &width) ||
2355       !gst_structure_get_int (structure, "height", &height) ||
2356       image_format == -1) {
2357     GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2358         GST_PTR_FORMAT, intersection);
2359     ret = GST_FLOW_UNEXPECTED;
2360     goto beach;
2361   }
2362
2363   g_mutex_lock (xvimagesink->pool_lock);
2364
2365   /* Walking through the pool cleaning unusable images and searching for a
2366      suitable one */
2367   while (xvimagesink->image_pool) {
2368     xvimage = xvimagesink->image_pool->data;
2369
2370     if (xvimage) {
2371       /* Removing from the pool */
2372       xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2373           xvimagesink->image_pool);
2374
2375       /* We check for geometry or image format changes */
2376       if ((xvimage->width != width) ||
2377           (xvimage->height != height) || (xvimage->im_format != image_format)) {
2378         /* This image is unusable. Destroying... */
2379         gst_xvimage_buffer_free (xvimage);
2380         xvimage = NULL;
2381       } else {
2382         /* We found a suitable image */
2383         GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2384         break;
2385       }
2386     }
2387   }
2388
2389   g_mutex_unlock (xvimagesink->pool_lock);
2390
2391   if (!xvimage) {
2392     /* We found no suitable image in the pool. Creating... */
2393     GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2394     xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2395     if (xvimage && xvimage->size < size) {
2396       /* This image is unusable. Destroying... */
2397       GST_LOG_OBJECT (xvimagesink, "Discarding allocated buffer as unsuitable. "
2398           "Falling back to normal buffer");
2399       gst_xvimage_buffer_free (xvimage);
2400       xvimage = NULL;
2401     }
2402   }
2403
2404   if (xvimage) {
2405     gst_buffer_set_caps (GST_BUFFER (xvimage), intersection);
2406   }
2407
2408   *buf = GST_BUFFER (xvimage);
2409
2410 beach:
2411   if (intersection) {
2412     gst_caps_unref (intersection);
2413   }
2414
2415   return ret;
2416 }
2417
2418 /* Interfaces stuff */
2419
2420 static gboolean
2421 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2422 {
2423   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2424       type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
2425   return TRUE;
2426 }
2427
2428 static void
2429 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2430 {
2431   klass->supported = gst_xvimagesink_interface_supported;
2432 }
2433
2434 static void
2435 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2436     GstStructure * structure)
2437 {
2438   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2439   GstPad *peer;
2440
2441   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2442     GstEvent *event;
2443     GstVideoRectangle src, dst, result;
2444     gdouble x, y, xscale = 1.0, yscale = 1.0;
2445
2446     event = gst_event_new_navigation (structure);
2447
2448     /* We take the flow_lock while we look at the window */
2449     g_mutex_lock (xvimagesink->flow_lock);
2450
2451     if (!xvimagesink->xwindow) {
2452       g_mutex_unlock (xvimagesink->flow_lock);
2453       return;
2454     }
2455
2456     /* We get the frame position using the calculated geometry from _setcaps
2457        that respect pixel aspect ratios */
2458     src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2459     src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2460     dst.w = xvimagesink->xwindow->width;
2461     dst.h = xvimagesink->xwindow->height;
2462
2463     g_mutex_unlock (xvimagesink->flow_lock);
2464
2465     if (xvimagesink->keep_aspect) {
2466       gst_video_sink_center_rect (src, dst, &result, TRUE);
2467     } else {
2468       result.x = result.y = 0;
2469       result.w = dst.w;
2470       result.h = dst.h;
2471     }
2472
2473     /* We calculate scaling using the original video frames geometry to include
2474        pixel aspect ratio scaling. */
2475     xscale = (gdouble) xvimagesink->video_width / result.w;
2476     yscale = (gdouble) xvimagesink->video_height / result.h;
2477
2478     /* Converting pointer coordinates to the non scaled geometry */
2479     if (gst_structure_get_double (structure, "pointer_x", &x)) {
2480       x = MIN (x, result.x + result.w);
2481       x = MAX (x - result.x, 0);
2482       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2483           (gdouble) x * xscale, NULL);
2484     }
2485     if (gst_structure_get_double (structure, "pointer_y", &y)) {
2486       y = MIN (y, result.y + result.h);
2487       y = MAX (y - result.y, 0);
2488       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2489           (gdouble) y * yscale, NULL);
2490     }
2491
2492     gst_pad_send_event (peer, event);
2493     gst_object_unref (peer);
2494   }
2495 }
2496
2497 static void
2498 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2499 {
2500   iface->send_event = gst_xvimagesink_navigation_send_event;
2501 }
2502
2503 static void
2504 gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
2505 {
2506   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2507   GstXWindow *xwindow = NULL;
2508   XWindowAttributes attr;
2509
2510   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2511
2512   g_mutex_lock (xvimagesink->flow_lock);
2513
2514   /* If we already use that window return */
2515   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2516     g_mutex_unlock (xvimagesink->flow_lock);
2517     return;
2518   }
2519
2520   /* If the element has not initialized the X11 context try to do so */
2521   if (!xvimagesink->xcontext &&
2522       !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2523     g_mutex_unlock (xvimagesink->flow_lock);
2524     /* we have thrown a GST_ELEMENT_ERROR now */
2525     return;
2526   }
2527
2528   gst_xvimagesink_update_colorbalance (xvimagesink);
2529
2530   /* Clear image pool as the images are unusable anyway */
2531   gst_xvimagesink_imagepool_clear (xvimagesink);
2532
2533   /* Clear the xvimage */
2534   if (xvimagesink->xvimage) {
2535     gst_xvimage_buffer_free (xvimagesink->xvimage);
2536     xvimagesink->xvimage = NULL;
2537   }
2538
2539   /* If a window is there already we destroy it */
2540   if (xvimagesink->xwindow) {
2541     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2542     xvimagesink->xwindow = NULL;
2543   }
2544
2545   /* If the xid is 0 we go back to an internal window */
2546   if (xwindow_id == 0) {
2547     /* If no width/height caps nego did not happen window will be created
2548        during caps nego then */
2549     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2550         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2551       xwindow =
2552           gst_xvimagesink_xwindow_new (xvimagesink,
2553           GST_VIDEO_SINK_WIDTH (xvimagesink),
2554           GST_VIDEO_SINK_HEIGHT (xvimagesink));
2555     }
2556   } else {
2557     xwindow = g_new0 (GstXWindow, 1);
2558
2559     xwindow->win = xwindow_id;
2560
2561     /* We get window geometry, set the event we want to receive,
2562        and create a GC */
2563     g_mutex_lock (xvimagesink->x_lock);
2564     XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2565     xwindow->width = attr.width;
2566     xwindow->height = attr.height;
2567     xwindow->internal = FALSE;
2568     if (xvimagesink->handle_events) {
2569       XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2570           StructureNotifyMask | PointerMotionMask | KeyPressMask |
2571           KeyReleaseMask);
2572     }
2573
2574     xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2575         xwindow->win, 0, NULL);
2576     g_mutex_unlock (xvimagesink->x_lock);
2577   }
2578
2579   if (xwindow)
2580     xvimagesink->xwindow = xwindow;
2581
2582   g_mutex_unlock (xvimagesink->flow_lock);
2583 }
2584
2585 static void
2586 gst_xvimagesink_expose (GstXOverlay * overlay)
2587 {
2588   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2589
2590   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2591 }
2592
2593 static void
2594 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2595     gboolean handle_events)
2596 {
2597   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2598
2599   xvimagesink->handle_events = handle_events;
2600
2601   g_mutex_lock (xvimagesink->flow_lock);
2602
2603   if (G_UNLIKELY (!xvimagesink->xwindow)) {
2604     g_mutex_unlock (xvimagesink->flow_lock);
2605     return;
2606   }
2607
2608   g_mutex_lock (xvimagesink->x_lock);
2609
2610   if (handle_events) {
2611     if (xvimagesink->xwindow->internal) {
2612       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2613           ExposureMask | StructureNotifyMask | PointerMotionMask |
2614           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2615     } else {
2616       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2617           ExposureMask | StructureNotifyMask | PointerMotionMask |
2618           KeyPressMask | KeyReleaseMask);
2619     }
2620   } else {
2621     XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2622   }
2623
2624   g_mutex_unlock (xvimagesink->x_lock);
2625
2626   g_mutex_unlock (xvimagesink->flow_lock);
2627 }
2628
2629 static void
2630 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2631 {
2632   iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id;
2633   iface->expose = gst_xvimagesink_expose;
2634   iface->handle_events = gst_xvimagesink_set_event_handling;
2635 }
2636
2637 static const GList *
2638 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2639 {
2640   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2641
2642   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2643
2644   if (xvimagesink->xcontext)
2645     return xvimagesink->xcontext->channels_list;
2646   else
2647     return NULL;
2648 }
2649
2650 static void
2651 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2652     GstColorBalanceChannel * channel, gint value)
2653 {
2654   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2655
2656   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2657   g_return_if_fail (channel->label != NULL);
2658
2659   xvimagesink->cb_changed = TRUE;
2660
2661   /* Normalize val to [-1000, 1000] */
2662   value = -1000 + 2000 * (value - channel->min_value) /
2663       (channel->max_value - channel->min_value);
2664
2665   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2666     xvimagesink->hue = value;
2667   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2668     xvimagesink->saturation = value;
2669   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2670     xvimagesink->contrast = value;
2671   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2672     xvimagesink->brightness = value;
2673   } else {
2674     g_warning ("got an unknown channel %s", channel->label);
2675     return;
2676   }
2677
2678   gst_xvimagesink_update_colorbalance (xvimagesink);
2679 }
2680
2681 static gint
2682 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2683     GstColorBalanceChannel * channel)
2684 {
2685   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2686   gint value = 0;
2687
2688   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2689   g_return_val_if_fail (channel->label != NULL, 0);
2690
2691   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2692     value = xvimagesink->hue;
2693   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2694     value = xvimagesink->saturation;
2695   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2696     value = xvimagesink->contrast;
2697   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2698     value = xvimagesink->brightness;
2699   } else {
2700     g_warning ("got an unknown channel %s", channel->label);
2701   }
2702
2703   /* Normalize val to [channel->min_value, channel->max_value] */
2704   value = channel->min_value + (channel->max_value - channel->min_value) *
2705       (value + 1000) / 2000;
2706
2707   return value;
2708 }
2709
2710 static void
2711 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2712 {
2713   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2714   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2715   iface->set_value = gst_xvimagesink_colorbalance_set_value;
2716   iface->get_value = gst_xvimagesink_colorbalance_get_value;
2717 }
2718
2719 static const GList *
2720 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2721 {
2722   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2723   static GList *list = NULL;
2724
2725   if (!list) {
2726     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2727   }
2728
2729   return list;
2730 }
2731
2732 static void
2733 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2734     guint prop_id, const GParamSpec * pspec)
2735 {
2736   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2737
2738   switch (prop_id) {
2739     case ARG_DEVICE:
2740       GST_DEBUG_OBJECT (xvimagesink, "probing device list");
2741       if (!xvimagesink->xcontext) {
2742         GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2743         xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2744       }
2745       break;
2746     default:
2747       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2748       break;
2749   }
2750 }
2751
2752 static gboolean
2753 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2754     guint prop_id, const GParamSpec * pspec)
2755 {
2756   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2757   gboolean ret = FALSE;
2758
2759   switch (prop_id) {
2760     case ARG_DEVICE:
2761       if (xvimagesink->xcontext != NULL) {
2762         ret = FALSE;
2763       } else {
2764         ret = TRUE;
2765       }
2766       break;
2767     default:
2768       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2769       break;
2770   }
2771
2772   return ret;
2773 }
2774
2775 static GValueArray *
2776 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2777     guint prop_id, const GParamSpec * pspec)
2778 {
2779   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2780   GValueArray *array = NULL;
2781
2782   if (G_UNLIKELY (!xvimagesink->xcontext)) {
2783     GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2784         "get values");
2785     goto beach;
2786   }
2787
2788   switch (prop_id) {
2789     case ARG_DEVICE:
2790     {
2791       guint i;
2792       GValue value = { 0 };
2793
2794       array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2795       g_value_init (&value, G_TYPE_STRING);
2796
2797       for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2798         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2799
2800         g_value_set_string (&value, adaptor_id_s);
2801         g_value_array_append (array, &value);
2802         g_free (adaptor_id_s);
2803       }
2804       g_value_unset (&value);
2805       break;
2806     }
2807     default:
2808       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2809       break;
2810   }
2811
2812 beach:
2813   return array;
2814 }
2815
2816 static void
2817 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2818     iface)
2819 {
2820   iface->get_properties = gst_xvimagesink_probe_get_properties;
2821   iface->probe_property = gst_xvimagesink_probe_probe_property;
2822   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2823   iface->get_values = gst_xvimagesink_probe_get_values;
2824 }
2825
2826 /* =========================================== */
2827 /*                                             */
2828 /*              Init & Class init              */
2829 /*                                             */
2830 /* =========================================== */
2831
2832 static void
2833 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2834     const GValue * value, GParamSpec * pspec)
2835 {
2836   GstXvImageSink *xvimagesink;
2837
2838   g_return_if_fail (GST_IS_XVIMAGESINK (object));
2839
2840   xvimagesink = GST_XVIMAGESINK (object);
2841
2842   switch (prop_id) {
2843     case ARG_HUE:
2844       xvimagesink->hue = g_value_get_int (value);
2845       xvimagesink->cb_changed = TRUE;
2846       gst_xvimagesink_update_colorbalance (xvimagesink);
2847       break;
2848     case ARG_CONTRAST:
2849       xvimagesink->contrast = g_value_get_int (value);
2850       xvimagesink->cb_changed = TRUE;
2851       gst_xvimagesink_update_colorbalance (xvimagesink);
2852       break;
2853     case ARG_BRIGHTNESS:
2854       xvimagesink->brightness = g_value_get_int (value);
2855       xvimagesink->cb_changed = TRUE;
2856       gst_xvimagesink_update_colorbalance (xvimagesink);
2857       break;
2858     case ARG_SATURATION:
2859       xvimagesink->saturation = g_value_get_int (value);
2860       xvimagesink->cb_changed = TRUE;
2861       gst_xvimagesink_update_colorbalance (xvimagesink);
2862       break;
2863     case ARG_DISPLAY:
2864       xvimagesink->display_name = g_strdup (g_value_get_string (value));
2865       break;
2866     case ARG_SYNCHRONOUS:
2867       xvimagesink->synchronous = g_value_get_boolean (value);
2868       if (xvimagesink->xcontext) {
2869         XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2870         GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2871             xvimagesink->synchronous ? "TRUE" : "FALSE");
2872       }
2873       break;
2874     case ARG_PIXEL_ASPECT_RATIO:
2875       g_free (xvimagesink->par);
2876       xvimagesink->par = g_new0 (GValue, 1);
2877       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2878       if (!g_value_transform (value, xvimagesink->par)) {
2879         g_warning ("Could not transform string to aspect ratio");
2880         gst_value_set_fraction (xvimagesink->par, 1, 1);
2881       }
2882       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2883           gst_value_get_fraction_numerator (xvimagesink->par),
2884           gst_value_get_fraction_denominator (xvimagesink->par));
2885       break;
2886     case ARG_FORCE_ASPECT_RATIO:
2887       xvimagesink->keep_aspect = g_value_get_boolean (value);
2888       break;
2889     case ARG_HANDLE_EVENTS:
2890       gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
2891           g_value_get_boolean (value));
2892       break;
2893     case ARG_DEVICE:
2894       xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2895       break;
2896     case ARG_HANDLE_EXPOSE:
2897       xvimagesink->handle_expose = g_value_get_boolean (value);
2898       break;
2899     case ARG_DOUBLE_BUFFER:
2900       xvimagesink->double_buffer = g_value_get_boolean (value);
2901       break;
2902     default:
2903       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2904       break;
2905   }
2906 }
2907
2908 static void
2909 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2910     GValue * value, GParamSpec * pspec)
2911 {
2912   GstXvImageSink *xvimagesink;
2913
2914   g_return_if_fail (GST_IS_XVIMAGESINK (object));
2915
2916   xvimagesink = GST_XVIMAGESINK (object);
2917
2918   switch (prop_id) {
2919     case ARG_HUE:
2920       g_value_set_int (value, xvimagesink->hue);
2921       break;
2922     case ARG_CONTRAST:
2923       g_value_set_int (value, xvimagesink->contrast);
2924       break;
2925     case ARG_BRIGHTNESS:
2926       g_value_set_int (value, xvimagesink->brightness);
2927       break;
2928     case ARG_SATURATION:
2929       g_value_set_int (value, xvimagesink->saturation);
2930       break;
2931     case ARG_DISPLAY:
2932       g_value_set_string (value, xvimagesink->display_name);
2933       break;
2934     case ARG_SYNCHRONOUS:
2935       g_value_set_boolean (value, xvimagesink->synchronous);
2936       break;
2937     case ARG_PIXEL_ASPECT_RATIO:
2938       if (xvimagesink->par)
2939         g_value_transform (xvimagesink->par, value);
2940       break;
2941     case ARG_FORCE_ASPECT_RATIO:
2942       g_value_set_boolean (value, xvimagesink->keep_aspect);
2943       break;
2944     case ARG_HANDLE_EVENTS:
2945       g_value_set_boolean (value, xvimagesink->handle_events);
2946       break;
2947     case ARG_DEVICE:
2948     {
2949       char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2950
2951       g_value_set_string (value, adaptor_no_s);
2952       g_free (adaptor_no_s);
2953       break;
2954     }
2955     case ARG_DEVICE_NAME:
2956       if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2957         g_value_set_string (value,
2958             xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2959       } else {
2960         g_value_set_string (value, NULL);
2961       }
2962       break;
2963     case ARG_HANDLE_EXPOSE:
2964       g_value_set_boolean (value, xvimagesink->handle_expose);
2965       break;
2966     case ARG_DOUBLE_BUFFER:
2967       g_value_set_boolean (value, xvimagesink->double_buffer);
2968       break;
2969     default:
2970       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2971       break;
2972   }
2973 }
2974
2975 static void
2976 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2977 {
2978   GThread *thread;
2979
2980   GST_OBJECT_LOCK (xvimagesink);
2981   xvimagesink->running = FALSE;
2982   /* grab thread and mark it as NULL */
2983   thread = xvimagesink->event_thread;
2984   xvimagesink->event_thread = NULL;
2985   GST_OBJECT_UNLOCK (xvimagesink);
2986
2987   /* Wait for our event thread to finish before we clean up our stuff. */
2988   if (thread)
2989     g_thread_join (thread);
2990
2991   if (xvimagesink->cur_image) {
2992     gst_buffer_unref (xvimagesink->cur_image);
2993     xvimagesink->cur_image = NULL;
2994   }
2995   if (xvimagesink->xvimage) {
2996     gst_buffer_unref (xvimagesink->xvimage);
2997     xvimagesink->xvimage = NULL;
2998   }
2999
3000   gst_xvimagesink_imagepool_clear (xvimagesink);
3001
3002   if (xvimagesink->xwindow) {
3003     gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3004     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3005     xvimagesink->xwindow = NULL;
3006   }
3007
3008   gst_xvimagesink_xcontext_clear (xvimagesink);
3009 }
3010
3011 /* Finalize is called only once, dispose can be called multiple times.
3012  * We use mutexes and don't reset stuff to NULL here so let's register
3013  * as a finalize. */
3014 static void
3015 gst_xvimagesink_finalize (GObject * object)
3016 {
3017   GstXvImageSink *xvimagesink;
3018
3019   xvimagesink = GST_XVIMAGESINK (object);
3020
3021   gst_xvimagesink_reset (xvimagesink);
3022
3023   if (xvimagesink->display_name) {
3024     g_free (xvimagesink->display_name);
3025     xvimagesink->display_name = NULL;
3026   }
3027
3028   if (xvimagesink->par) {
3029     g_free (xvimagesink->par);
3030     xvimagesink->par = NULL;
3031   }
3032   if (xvimagesink->x_lock) {
3033     g_mutex_free (xvimagesink->x_lock);
3034     xvimagesink->x_lock = NULL;
3035   }
3036   if (xvimagesink->flow_lock) {
3037     g_mutex_free (xvimagesink->flow_lock);
3038     xvimagesink->flow_lock = NULL;
3039   }
3040   if (xvimagesink->pool_lock) {
3041     g_mutex_free (xvimagesink->pool_lock);
3042     xvimagesink->pool_lock = NULL;
3043   }
3044
3045   G_OBJECT_CLASS (parent_class)->finalize (object);
3046 }
3047
3048 static void
3049 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
3050 {
3051   xvimagesink->display_name = NULL;
3052   xvimagesink->adaptor_no = 0;
3053   xvimagesink->xcontext = NULL;
3054   xvimagesink->xwindow = NULL;
3055   xvimagesink->xvimage = NULL;
3056   xvimagesink->cur_image = NULL;
3057
3058   xvimagesink->hue = xvimagesink->saturation = 0;
3059   xvimagesink->contrast = xvimagesink->brightness = 0;
3060   xvimagesink->cb_changed = FALSE;
3061
3062   xvimagesink->fps_n = 0;
3063   xvimagesink->fps_d = 0;
3064   xvimagesink->video_width = 0;
3065   xvimagesink->video_height = 0;
3066
3067   xvimagesink->x_lock = g_mutex_new ();
3068   xvimagesink->flow_lock = g_mutex_new ();
3069
3070   xvimagesink->image_pool = NULL;
3071   xvimagesink->pool_lock = g_mutex_new ();
3072
3073   xvimagesink->synchronous = FALSE;
3074   xvimagesink->double_buffer = TRUE;
3075   xvimagesink->running = FALSE;
3076   xvimagesink->keep_aspect = FALSE;
3077   xvimagesink->handle_events = TRUE;
3078   xvimagesink->par = NULL;
3079   xvimagesink->handle_expose = TRUE;
3080 }
3081
3082 static void
3083 gst_xvimagesink_base_init (gpointer g_class)
3084 {
3085   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3086
3087   gst_element_class_set_details (element_class, &gst_xvimagesink_details);
3088
3089   gst_element_class_add_pad_template (element_class,
3090       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
3091 }
3092
3093 static void
3094 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3095 {
3096   GObjectClass *gobject_class;
3097   GstElementClass *gstelement_class;
3098   GstBaseSinkClass *gstbasesink_class;
3099
3100   gobject_class = (GObjectClass *) klass;
3101   gstelement_class = (GstElementClass *) klass;
3102   gstbasesink_class = (GstBaseSinkClass *) klass;
3103
3104   parent_class = g_type_class_peek_parent (klass);
3105
3106   gobject_class->set_property = gst_xvimagesink_set_property;
3107   gobject_class->get_property = gst_xvimagesink_get_property;
3108
3109   g_object_class_install_property (gobject_class, ARG_CONTRAST,
3110       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3111           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3112   g_object_class_install_property (gobject_class, ARG_BRIGHTNESS,
3113       g_param_spec_int ("brightness", "Brightness",
3114           "The brightness of the video", -1000, 1000, 0,
3115           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3116   g_object_class_install_property (gobject_class, ARG_HUE,
3117       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3118           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3119   g_object_class_install_property (gobject_class, ARG_SATURATION,
3120       g_param_spec_int ("saturation", "Saturation",
3121           "The saturation of the video", -1000, 1000, 0,
3122           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3123   g_object_class_install_property (gobject_class, ARG_DISPLAY,
3124       g_param_spec_string ("display", "Display", "X Display name", NULL,
3125           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3126   g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS,
3127       g_param_spec_boolean ("synchronous", "Synchronous",
3128           "When enabled, runs "
3129           "the X display in synchronous mode. (used only for debugging)", FALSE,
3130           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3131   g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO,
3132       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3133           "The pixel aspect ratio of the device", "1/1",
3134           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3135   g_object_class_install_property (gobject_class, ARG_FORCE_ASPECT_RATIO,
3136       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3137           "When enabled, scaling will respect original aspect ratio", FALSE,
3138           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3139   g_object_class_install_property (gobject_class, ARG_HANDLE_EVENTS,
3140       g_param_spec_boolean ("handle-events", "Handle XEvents",
3141           "When enabled, XEvents will be selected and handled", TRUE,
3142           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3143   g_object_class_install_property (gobject_class, ARG_DEVICE,
3144       g_param_spec_string ("device", "Adaptor number",
3145           "The number of the video adaptor", "0",
3146           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3147   g_object_class_install_property (gobject_class, ARG_DEVICE_NAME,
3148       g_param_spec_string ("device-name", "Adaptor name",
3149           "The name of the video adaptor", NULL,
3150           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3151   g_object_class_install_property (gobject_class, ARG_HANDLE_EXPOSE,
3152       g_param_spec_boolean ("handle-expose", "Handle expose",
3153           "When enabled, "
3154           "the current frame will always be drawn in response to X Expose "
3155           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3156   g_object_class_install_property (gobject_class, ARG_DOUBLE_BUFFER,
3157       g_param_spec_boolean ("double-buffer", "Double-buffer",
3158           "Whether to double-buffer the output", TRUE,
3159           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3160
3161   gobject_class->finalize = gst_xvimagesink_finalize;
3162
3163   gstelement_class->change_state =
3164       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3165
3166   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3167   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3168   gstbasesink_class->buffer_alloc =
3169       GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3170   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3171   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3172   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3173 }
3174
3175 /* ============================================================= */
3176 /*                                                               */
3177 /*                       Public Methods                          */
3178 /*                                                               */
3179 /* ============================================================= */
3180
3181 /* =========================================== */
3182 /*                                             */
3183 /*          Object typing & Creation           */
3184 /*                                             */
3185 /* =========================================== */
3186
3187 GType
3188 gst_xvimagesink_get_type (void)
3189 {
3190   static GType xvimagesink_type = 0;
3191
3192   if (!xvimagesink_type) {
3193     static const GTypeInfo xvimagesink_info = {
3194       sizeof (GstXvImageSinkClass),
3195       gst_xvimagesink_base_init,
3196       NULL,
3197       (GClassInitFunc) gst_xvimagesink_class_init,
3198       NULL,
3199       NULL,
3200       sizeof (GstXvImageSink),
3201       0,
3202       (GInstanceInitFunc) gst_xvimagesink_init,
3203     };
3204     static const GInterfaceInfo iface_info = {
3205       (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3206       NULL,
3207       NULL,
3208     };
3209     static const GInterfaceInfo navigation_info = {
3210       (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3211       NULL,
3212       NULL,
3213     };
3214     static const GInterfaceInfo overlay_info = {
3215       (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3216       NULL,
3217       NULL,
3218     };
3219     static const GInterfaceInfo colorbalance_info = {
3220       (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3221       NULL,
3222       NULL,
3223     };
3224     static const GInterfaceInfo propertyprobe_info = {
3225       (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3226       NULL,
3227       NULL,
3228     };
3229     xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
3230         "GstXvImageSink", &xvimagesink_info, 0);
3231
3232     g_type_add_interface_static (xvimagesink_type,
3233         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3234     g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
3235         &navigation_info);
3236     g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
3237         &overlay_info);
3238     g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
3239         &colorbalance_info);
3240     g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE,
3241         &propertyprobe_info);
3242
3243
3244     /* register type and create class in a more safe place instead of at
3245      * runtime since the type registration and class creation is not
3246      * threadsafe. */
3247     g_type_class_ref (gst_xvimage_buffer_get_type ());
3248   }
3249
3250   return xvimagesink_type;
3251 }
3252
3253 static gboolean
3254 plugin_init (GstPlugin * plugin)
3255 {
3256   if (!gst_element_register (plugin, "xvimagesink",
3257           GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3258     return FALSE;
3259
3260   GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3261       "xvimagesink element");
3262
3263   return TRUE;
3264 }
3265
3266 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3267     GST_VERSION_MINOR,
3268     "xvimagesink",
3269     "XFree86 video output plugin using Xv extension",
3270     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)