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