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