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