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