ximagesink: clear src and dest rectangles
[platform/upstream/gst-plugins-base.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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-xvimagesink
23  *
24  * XvImageSink renders video frames to a drawable (XWindow) on a local display
25  * using the XVideo extension. Rendering to a remote display is theoretically
26  * possible but i doubt that the XVideo extension is actually available when
27  * connecting to a remote display. This element can receive a Window ID from the
28  * application through the #GstVideoOverlay interface and will then render
29  * video frames in this drawable. If no Window ID was provided by the
30  * application, the element will create its own internal window and render
31  * into it.
32  *
33  * <refsect2>
34  * <title>Scaling</title>
35  * <para>
36  * The XVideo extension, when it's available, handles hardware accelerated
37  * scaling of video frames. This means that the element will just accept
38  * incoming video frames no matter their geometry and will then put them to the
39  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40  * property it is possible to enforce scaling with a constant aspect ratio,
41  * which means drawing black borders around the video frame.
42  * </para>
43  * </refsect2>
44  * <refsect2>
45  * <title>Events</title>
46  * <para>
47  * XvImageSink creates a thread to handle events coming from the drawable. There
48  * are several kind of events that can be grouped in 2 big categories: input
49  * events and window state related events. Input events will be translated to
50  * navigation events and pushed upstream for other elements to react on them.
51  * This includes events such as pointer moves, key press/release, clicks etc...
52  * Other events are used to handle the drawable appearance even when the data
53  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
54  * paused, it will receive expose events from the drawable and draw the latest
55  * frame with correct borders/aspect-ratio.
56  * </para>
57  * </refsect2>
58  * <refsect2>
59  * <title>Pixel aspect ratio</title>
60  * <para>
61  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
62  * the display specified in the #GstXvImageSink:display property or the
63  * default display if nothing specified. Once this connection is open it will
64  * inspect the display configuration including the physical display geometry and
65  * then calculate the pixel aspect ratio. When receiving video frames with a
66  * different pixel aspect ratio, XvImageSink will use hardware scaling to
67  * display the video frames correctly on display's pixel aspect ratio.
68  * Sometimes the calculated pixel aspect ratio can be wrong, it is
69  * then possible to enforce a specific pixel aspect ratio using the
70  * #GstXvImageSink:pixel-aspect-ratio property.
71  * </para>
72  * </refsect2>
73  * <refsect2>
74  * <title>Examples</title>
75  * |[
76  * gst-launch -v videotestsrc ! xvimagesink
77  * ]| A pipeline to test hardware scaling.
78  * When the test video signal appears you can resize the window and see that
79  * video frames are scaled through hardware (no extra CPU cost).
80  * |[
81  * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
82  * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
83  * You can observe the borders drawn around the scaled image respecting aspect
84  * ratio.
85  * |[
86  * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
87  * ]| A pipeline to test navigation events.
88  * While moving the mouse pointer over the test signal you will see a black box
89  * following the mouse pointer. If you press the mouse button somewhere on the
90  * video and release it somewhere else a green box will appear where you pressed
91  * the button and a red one where you released it. (The navigationtest element
92  * is part of gst-plugins-good.) You can observe here that even if the images
93  * are scaled through hardware the pointer coordinates are converted back to the
94  * original video frame geometry so that the box can be drawn to the correct
95  * position. This also handles borders correctly, limiting coordinates to the
96  * image area
97  * |[
98  * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
99  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
100  * videotestsrc, in most cases the pixel aspect ratio of the display will be
101  * 1/1. This means that XvImageSink will have to do the scaling to convert
102  * incoming frames to a size that will match the display pixel aspect ratio
103  * (from 320x240 to 320x180 in this case). Note that you might have to escape
104  * some characters for your shell like '\(fraction\)'.
105  * |[
106  * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
107  * ]| Demonstrates how to use the colorbalance interface.
108  * </refsect2>
109  */
110
111 /* for developers: there are two useful tools : xvinfo and xvattr */
112
113 #ifdef HAVE_CONFIG_H
114 #include "config.h"
115 #endif
116
117 /* Our interfaces */
118 #include <gst/video/navigation.h>
119 #include <gst/video/videooverlay.h>
120 #include <gst/video/colorbalance.h>
121 /* Helper functions */
122 #include <gst/video/gstvideometa.h>
123
124 /* Object header */
125 #include "xvimagesink.h"
126 #include "xvimageallocator.h"
127
128 /* Debugging category */
129 #include <gst/gstinfo.h>
130
131 /* for XkbKeycodeToKeysym */
132 #include <X11/XKBlib.h>
133
134 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
135 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
136 #define GST_CAT_DEFAULT gst_debug_xvimagesink
137
138 typedef struct
139 {
140   unsigned long flags;
141   unsigned long functions;
142   unsigned long decorations;
143   long input_mode;
144   unsigned long status;
145 }
146 MotifWmHints, MwmHints;
147
148 #define MWM_HINTS_DECORATIONS   (1L << 1)
149
150 static gboolean gst_xvimagesink_open (GstXvImageSink * xvimagesink);
151 static void gst_xvimagesink_close (GstXvImageSink * xvimagesink);
152 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
153     xvimagesink);
154 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
155
156 /* Default template - initiated with class struct to allow gst-register to work
157    without X running */
158 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
159 GST_STATIC_PAD_TEMPLATE ("sink",
160     GST_PAD_SINK,
161     GST_PAD_ALWAYS,
162     GST_STATIC_CAPS ("video/x-raw, "
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_navigation_init (GstNavigationInterface * iface);
202 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
203     iface);
204 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
205     iface);
206 #define gst_xvimagesink_parent_class parent_class
207 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
208     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
209         gst_xvimagesink_navigation_init);
210     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
211         gst_xvimagesink_video_overlay_init);
212     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
213         gst_xvimagesink_colorbalance_init));
214
215
216 /* ============================================================= */
217 /*                                                               */
218 /*                       Private Methods                         */
219 /*                                                               */
220 /* ============================================================= */
221
222
223 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
224  * if no window was available  */
225 static gboolean
226 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
227 {
228   GstXvImageMemory *mem;
229   GstVideoCropMeta *crop;
230   GstVideoRectangle result;
231   gboolean draw_border = FALSE;
232   GstVideoRectangle src = { 0, };
233   GstVideoRectangle dst = { 0, };
234   GstVideoRectangle mem_crop;
235   GstXWindow *xwindow;
236
237   /* We take the flow_lock. If expose is in there we don't want to run
238      concurrently from the data flow thread */
239   g_mutex_lock (&xvimagesink->flow_lock);
240
241   if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
242     g_mutex_unlock (&xvimagesink->flow_lock);
243     return FALSE;
244   }
245
246   /* Draw borders when displaying the first frame. After this
247      draw borders only on expose event or after a size change. */
248   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
249     draw_border = xvimagesink->draw_borders;
250     xvimagesink->redraw_border = FALSE;
251   }
252
253   /* Store a reference to the last image we put, lose the previous one */
254   if (xvimage && xvimagesink->cur_image != xvimage) {
255     if (xvimagesink->cur_image) {
256       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
257       gst_buffer_unref (xvimagesink->cur_image);
258     }
259     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
260     xvimagesink->cur_image = gst_buffer_ref (xvimage);
261   }
262
263   /* Expose sends a NULL image, we take the latest frame */
264   if (!xvimage) {
265     if (xvimagesink->cur_image) {
266       draw_border = TRUE;
267       xvimage = xvimagesink->cur_image;
268     } else {
269       g_mutex_unlock (&xvimagesink->flow_lock);
270       return TRUE;
271     }
272   }
273
274   mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
275   gst_xvimage_memory_get_crop (mem, &mem_crop);
276
277   crop = gst_buffer_get_video_crop_meta (xvimage);
278
279   if (crop) {
280     src.x = crop->x + mem_crop.x;
281     src.y = crop->y + mem_crop.y;
282     src.w = crop->width;
283     src.h = crop->height;
284     GST_LOG_OBJECT (xvimagesink,
285         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
286   } else {
287     src = mem_crop;
288   }
289
290   if (xvimagesink->keep_aspect) {
291     GstVideoRectangle s;
292
293     /* We take the size of the source material as it was negotiated and
294      * corrected for DAR. This size can be different from the cropped size in
295      * which case the image will be scaled to fit the negotiated size. */
296     s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
297     s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
298     dst.w = xwindow->render_rect.w;
299     dst.h = xwindow->render_rect.h;
300
301     gst_video_sink_center_rect (s, dst, &result, TRUE);
302     result.x += xwindow->render_rect.x;
303     result.y += xwindow->render_rect.y;
304   } else {
305     memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
306   }
307
308   gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
309
310   g_mutex_unlock (&xvimagesink->flow_lock);
311
312   return TRUE;
313 }
314
315 static void
316 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
317     GstXWindow * xwindow, const gchar * media_title)
318 {
319   if (media_title) {
320     g_free (xvimagesink->media_title);
321     xvimagesink->media_title = g_strdup (media_title);
322   }
323   if (xwindow) {
324     /* we have a window */
325     const gchar *app_name;
326     const gchar *title = NULL;
327     gchar *title_mem = NULL;
328
329     /* set application name as a title */
330     app_name = g_get_application_name ();
331
332     if (app_name && xvimagesink->media_title) {
333       title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
334           app_name, NULL);
335     } else if (app_name) {
336       title = app_name;
337     } else if (xvimagesink->media_title) {
338       title = xvimagesink->media_title;
339     }
340
341     gst_xwindow_set_title (xwindow, title);
342     g_free (title_mem);
343   }
344 }
345
346 /* This function handles a GstXWindow creation
347  * The width and height are the actual pixel size on the display */
348 static GstXWindow *
349 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
350     gint width, gint height)
351 {
352   GstXWindow *xwindow = NULL;
353   GstXvContext *context;
354
355   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
356
357   context = xvimagesink->context;
358
359   xwindow = gst_xvcontext_create_xwindow (context, width, height);
360
361   /* set application name as a title */
362   gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
363
364   gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
365
366   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
367       xwindow->win);
368
369   return xwindow;
370 }
371
372 static void
373 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
374 {
375   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
376
377   /* Update the window geometry */
378   g_mutex_lock (&xvimagesink->flow_lock);
379   if (G_LIKELY (xvimagesink->xwindow))
380     gst_xwindow_update_geometry (xvimagesink->xwindow);
381   g_mutex_unlock (&xvimagesink->flow_lock);
382 }
383
384 /* This function commits our internal colorbalance settings to our grabbed Xv
385    port. If the context is not initialized yet it simply returns */
386 static void
387 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
388 {
389   GstXvContext *context;
390
391   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
392
393   /* If we haven't initialized the X context we can't update anything */
394   if ((context = xvimagesink->context) == NULL)
395     return;
396
397   gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
398 }
399
400 /* This function handles XEvents that might be in the queue. It generates
401    GstEvent that will be sent upstream in the pipeline to handle interactivity
402    and navigation. It will also listen for configure events on the window to
403    trigger caps renegotiation so on the fly software scaling can work. */
404 static void
405 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
406 {
407   XEvent e;
408   guint pointer_x = 0, pointer_y = 0;
409   gboolean pointer_moved = FALSE;
410   gboolean exposed = FALSE, configured = FALSE;
411
412   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
413
414   /* Handle Interaction, produces navigation events */
415
416   /* We get all pointer motion events, only the last position is
417      interesting. */
418   g_mutex_lock (&xvimagesink->flow_lock);
419   g_mutex_lock (&xvimagesink->context->lock);
420   while (XCheckWindowEvent (xvimagesink->context->disp,
421           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
422     g_mutex_unlock (&xvimagesink->context->lock);
423     g_mutex_unlock (&xvimagesink->flow_lock);
424
425     switch (e.type) {
426       case MotionNotify:
427         pointer_x = e.xmotion.x;
428         pointer_y = e.xmotion.y;
429         pointer_moved = TRUE;
430         break;
431       default:
432         break;
433     }
434     g_mutex_lock (&xvimagesink->flow_lock);
435     g_mutex_lock (&xvimagesink->context->lock);
436   }
437
438   if (pointer_moved) {
439     g_mutex_unlock (&xvimagesink->context->lock);
440     g_mutex_unlock (&xvimagesink->flow_lock);
441
442     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
443         pointer_x, pointer_y);
444     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
445         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
446
447     g_mutex_lock (&xvimagesink->flow_lock);
448     g_mutex_lock (&xvimagesink->context->lock);
449   }
450
451   /* We get all events on our window to throw them upstream */
452   while (XCheckWindowEvent (xvimagesink->context->disp,
453           xvimagesink->xwindow->win,
454           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
455           &e)) {
456     KeySym keysym;
457     const char *key_str = NULL;
458
459     /* We lock only for the X function call */
460     g_mutex_unlock (&xvimagesink->context->lock);
461     g_mutex_unlock (&xvimagesink->flow_lock);
462
463     switch (e.type) {
464       case ButtonPress:
465         /* Mouse button pressed over our window. We send upstream
466            events for interactivity/navigation */
467         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
468             e.xbutton.button, e.xbutton.x, e.xbutton.y);
469         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
470             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
471         break;
472       case ButtonRelease:
473         /* Mouse button released over our window. We send upstream
474            events for interactivity/navigation */
475         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
476             e.xbutton.button, e.xbutton.x, e.xbutton.y);
477         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
478             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
479         break;
480       case KeyPress:
481       case KeyRelease:
482         /* Key pressed/released over our window. We send upstream
483            events for interactivity/navigation */
484         g_mutex_lock (&xvimagesink->context->lock);
485         keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
486             e.xkey.keycode, 0, 0);
487         if (keysym != NoSymbol) {
488           key_str = XKeysymToString (keysym);
489         } else {
490           key_str = "unknown";
491         }
492         g_mutex_unlock (&xvimagesink->context->lock);
493         GST_DEBUG_OBJECT (xvimagesink,
494             "key %d pressed over window at %d,%d (%s)",
495             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
496         gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
497             e.type == KeyPress ? "key-press" : "key-release", key_str);
498         break;
499       default:
500         GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
501             e.type);
502     }
503     g_mutex_lock (&xvimagesink->flow_lock);
504     g_mutex_lock (&xvimagesink->context->lock);
505   }
506
507   /* Handle Expose */
508   while (XCheckWindowEvent (xvimagesink->context->disp,
509           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
510     switch (e.type) {
511       case Expose:
512         exposed = TRUE;
513         break;
514       case ConfigureNotify:
515         g_mutex_unlock (&xvimagesink->context->lock);
516         g_mutex_unlock (&xvimagesink->flow_lock);
517
518         gst_xvimagesink_xwindow_update_geometry (xvimagesink);
519
520         g_mutex_lock (&xvimagesink->flow_lock);
521         g_mutex_lock (&xvimagesink->context->lock);
522         configured = TRUE;
523         break;
524       default:
525         break;
526     }
527   }
528
529   if (xvimagesink->handle_expose && (exposed || configured)) {
530     g_mutex_unlock (&xvimagesink->context->lock);
531     g_mutex_unlock (&xvimagesink->flow_lock);
532
533     gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
534
535     g_mutex_lock (&xvimagesink->flow_lock);
536     g_mutex_lock (&xvimagesink->context->lock);
537   }
538
539   /* Handle Display events */
540   while (XPending (xvimagesink->context->disp)) {
541     XNextEvent (xvimagesink->context->disp, &e);
542
543     switch (e.type) {
544       case ClientMessage:{
545         Atom wm_delete;
546
547         wm_delete = XInternAtom (xvimagesink->context->disp,
548             "WM_DELETE_WINDOW", True);
549         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
550           /* Handle window deletion by posting an error on the bus */
551           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
552               ("Output window was closed"), (NULL));
553
554           g_mutex_unlock (&xvimagesink->context->lock);
555           gst_xwindow_destroy (xvimagesink->xwindow);
556           xvimagesink->xwindow = NULL;
557           g_mutex_lock (&xvimagesink->context->lock);
558         }
559         break;
560       }
561       default:
562         break;
563     }
564   }
565
566   g_mutex_unlock (&xvimagesink->context->lock);
567   g_mutex_unlock (&xvimagesink->flow_lock);
568 }
569
570 static gpointer
571 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
572 {
573   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
574
575   GST_OBJECT_LOCK (xvimagesink);
576   while (xvimagesink->running) {
577     GST_OBJECT_UNLOCK (xvimagesink);
578
579     if (xvimagesink->xwindow) {
580       gst_xvimagesink_handle_xevents (xvimagesink);
581     }
582     /* FIXME: do we want to align this with the framerate or anything else? */
583     g_usleep (G_USEC_PER_SEC / 20);
584
585     GST_OBJECT_LOCK (xvimagesink);
586   }
587   GST_OBJECT_UNLOCK (xvimagesink);
588
589   return NULL;
590 }
591
592 static void
593 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
594 {
595   GThread *thread = NULL;
596
597   /* don't start the thread too early */
598   if (xvimagesink->context == NULL) {
599     return;
600   }
601
602   GST_OBJECT_LOCK (xvimagesink);
603   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
604     if (!xvimagesink->event_thread) {
605       /* Setup our event listening thread */
606       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
607           xvimagesink->handle_expose, xvimagesink->handle_events);
608       xvimagesink->running = TRUE;
609       xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
610           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
611     }
612   } else {
613     if (xvimagesink->event_thread) {
614       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
615           xvimagesink->handle_expose, xvimagesink->handle_events);
616       xvimagesink->running = FALSE;
617       /* grab thread and mark it as NULL */
618       thread = xvimagesink->event_thread;
619       xvimagesink->event_thread = NULL;
620     }
621   }
622   GST_OBJECT_UNLOCK (xvimagesink);
623
624   /* Wait for our event thread to finish */
625   if (thread)
626     g_thread_join (thread);
627
628 }
629
630 /* Element stuff */
631
632 static GstCaps *
633 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
634 {
635   GstXvImageSink *xvimagesink;
636   GstCaps *caps;
637
638   xvimagesink = GST_XVIMAGESINK (bsink);
639
640   if (xvimagesink->context) {
641     if (filter)
642       return gst_caps_intersect_full (filter, xvimagesink->context->caps,
643           GST_CAPS_INTERSECT_FIRST);
644     else
645       return gst_caps_ref (xvimagesink->context->caps);
646   }
647
648   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
649   if (filter) {
650     GstCaps *intersection;
651
652     intersection =
653         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
654     gst_caps_unref (caps);
655     caps = intersection;
656   }
657   return caps;
658 }
659
660 static gboolean
661 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
662 {
663   GstXvImageSink *xvimagesink;
664   GstXvContext *context;
665   GstStructure *structure;
666   GstBufferPool *newpool, *oldpool;
667   GstVideoInfo info;
668   guint32 im_format = 0;
669   gint video_par_n, video_par_d;        /* video's PAR */
670   gint display_par_n, display_par_d;    /* display's PAR */
671   guint num, den;
672   gint size;
673
674   xvimagesink = GST_XVIMAGESINK (bsink);
675   context = xvimagesink->context;
676
677   GST_DEBUG_OBJECT (xvimagesink,
678       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
679       GST_PTR_FORMAT, context->caps, caps);
680
681   if (!gst_caps_can_intersect (context->caps, caps))
682     goto incompatible_caps;
683
684   if (!gst_video_info_from_caps (&info, caps))
685     goto invalid_format;
686
687   xvimagesink->fps_n = info.fps_n;
688   xvimagesink->fps_d = info.fps_d;
689
690   xvimagesink->video_width = info.width;
691   xvimagesink->video_height = info.height;
692
693   im_format = gst_xvcontext_get_format_from_info (context, &info);
694   if (im_format == -1)
695     goto invalid_format;
696
697   gst_xvcontext_set_colorimetry (context, &info.colorimetry);
698
699   size = info.size;
700
701   /* get aspect ratio from caps if it's present, and
702    * convert video width and height to a display width and height
703    * using wd / hd = wv / hv * PARv / PARd */
704
705   /* get video's PAR */
706   video_par_n = info.par_n;
707   video_par_d = info.par_d;
708
709   /* get display's PAR */
710   if (xvimagesink->par) {
711     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
712     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
713   } else {
714     display_par_n = 1;
715     display_par_d = 1;
716   }
717
718   if (!gst_video_calculate_display_ratio (&num, &den, info.width,
719           info.height, video_par_n, video_par_d, display_par_n, display_par_d))
720     goto no_disp_ratio;
721
722   GST_DEBUG_OBJECT (xvimagesink,
723       "video width/height: %dx%d, calculated display ratio: %d/%d",
724       info.width, info.height, num, den);
725
726   /* now find a width x height that respects this display ratio.
727    * prefer those that have one of w/h the same as the incoming video
728    * using wd / hd = num / den */
729
730   /* start with same height, because of interlaced video */
731   /* check hd / den is an integer scale factor, and scale wd with the PAR */
732   if (info.height % den == 0) {
733     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
734     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
735         gst_util_uint64_scale_int (info.height, num, den);
736     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
737   } else if (info.width % num == 0) {
738     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
739     GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
740     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
741         gst_util_uint64_scale_int (info.width, den, num);
742   } else {
743     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
744     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
745         gst_util_uint64_scale_int (info.height, num, den);
746     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
747   }
748   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
749       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
750
751   /* Notify application to set xwindow id now */
752   g_mutex_lock (&xvimagesink->flow_lock);
753   if (!xvimagesink->xwindow) {
754     g_mutex_unlock (&xvimagesink->flow_lock);
755     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
756   } else {
757     g_mutex_unlock (&xvimagesink->flow_lock);
758   }
759
760   /* Creating our window and our image with the display size in pixels */
761   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
762       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
763     goto no_display_size;
764
765   g_mutex_lock (&xvimagesink->flow_lock);
766   if (!xvimagesink->xwindow) {
767     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
768         GST_VIDEO_SINK_WIDTH (xvimagesink),
769         GST_VIDEO_SINK_HEIGHT (xvimagesink));
770   }
771
772   xvimagesink->info = info;
773
774   /* After a resize, we want to redraw the borders in case the new frame size
775    * doesn't cover the same area */
776   xvimagesink->redraw_border = TRUE;
777
778   /* create a new pool for the new configuration */
779   newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
780
781   structure = gst_buffer_pool_get_config (newpool);
782   gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
783   if (!gst_buffer_pool_set_config (newpool, structure))
784     goto config_failed;
785
786   oldpool = xvimagesink->pool;
787   /* we don't activate the pool yet, this will be done by downstream after it
788    * has configured the pool. If downstream does not want our pool we will
789    * activate it when we render into it */
790   xvimagesink->pool = newpool;
791   g_mutex_unlock (&xvimagesink->flow_lock);
792
793   /* unref the old sink */
794   if (oldpool) {
795     /* we don't deactivate, some elements might still be using it, it will
796      * be deactivated when the last ref is gone */
797     gst_object_unref (oldpool);
798   }
799
800   return TRUE;
801
802   /* ERRORS */
803 incompatible_caps:
804   {
805     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
806     return FALSE;
807   }
808 invalid_format:
809   {
810     GST_DEBUG_OBJECT (xvimagesink,
811         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
812     return FALSE;
813   }
814 no_disp_ratio:
815   {
816     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
817         ("Error calculating the output display ratio of the video."));
818     return FALSE;
819   }
820 no_display_size:
821   {
822     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
823         ("Error calculating the output display ratio of the video."));
824     return FALSE;
825   }
826 config_failed:
827   {
828     GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
829     g_mutex_unlock (&xvimagesink->flow_lock);
830     return FALSE;
831   }
832 }
833
834 static GstStateChangeReturn
835 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
836 {
837   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
838   GstXvImageSink *xvimagesink;
839
840   xvimagesink = GST_XVIMAGESINK (element);
841
842   switch (transition) {
843     case GST_STATE_CHANGE_NULL_TO_READY:
844       if (!gst_xvimagesink_open (xvimagesink))
845         goto error;
846       break;
847     case GST_STATE_CHANGE_READY_TO_PAUSED:
848       break;
849     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
850       break;
851     case GST_STATE_CHANGE_PAUSED_TO_READY:
852       break;
853     default:
854       break;
855   }
856
857   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
858
859   switch (transition) {
860     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
861       break;
862     case GST_STATE_CHANGE_PAUSED_TO_READY:
863       xvimagesink->fps_n = 0;
864       xvimagesink->fps_d = 1;
865       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
866       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
867       g_mutex_lock (&xvimagesink->flow_lock);
868       if (xvimagesink->pool)
869         gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
870       g_mutex_unlock (&xvimagesink->flow_lock);
871       break;
872     case GST_STATE_CHANGE_READY_TO_NULL:
873       gst_xvimagesink_close (xvimagesink);
874       break;
875     default:
876       break;
877   }
878   return ret;
879
880 error:
881   {
882     return GST_STATE_CHANGE_FAILURE;
883   }
884 }
885
886 static void
887 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
888     GstClockTime * start, GstClockTime * end)
889 {
890   GstXvImageSink *xvimagesink;
891
892   xvimagesink = GST_XVIMAGESINK (bsink);
893
894   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
895     *start = GST_BUFFER_TIMESTAMP (buf);
896     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
897       *end = *start + GST_BUFFER_DURATION (buf);
898     } else {
899       if (xvimagesink->fps_n > 0) {
900         *end = *start +
901             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
902             xvimagesink->fps_n);
903       }
904     }
905   }
906 }
907
908 static GstFlowReturn
909 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
910 {
911   GstFlowReturn res;
912   GstXvImageSink *xvimagesink;
913   GstBuffer *to_put = NULL;
914   GstMemory *mem;
915
916   xvimagesink = GST_XVIMAGESINK (vsink);
917
918   if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
919       && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
920     /* If this buffer has been allocated using our buffer management we simply
921        put the ximage which is in the PRIVATE pointer */
922     GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
923         buf);
924     to_put = buf;
925     res = GST_FLOW_OK;
926   } else {
927     GstVideoFrame src, dest;
928     GstBufferPoolAcquireParams params = { 0, };
929
930     /* Else we have to copy the data into our private image, */
931     /* if we have one... */
932     GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
933
934     /* we should have a pool, configured in setcaps */
935     if (xvimagesink->pool == NULL)
936       goto no_pool;
937
938     if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
939       goto activate_failed;
940
941     /* take a buffer from our pool, if there is no buffer in the pool something
942      * is seriously wrong, waiting for the pool here might deadlock when we try
943      * to go to PAUSED because we never flush the pool then. */
944     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
945     res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, &params);
946     if (res != GST_FLOW_OK)
947       goto no_buffer;
948
949     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
950         "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
951
952     if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
953       goto invalid_buffer;
954
955     if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
956       gst_video_frame_unmap (&src);
957       goto invalid_buffer;
958     }
959
960     gst_video_frame_copy (&dest, &src);
961
962     gst_video_frame_unmap (&dest);
963     gst_video_frame_unmap (&src);
964   }
965
966   if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
967     goto no_window;
968
969 done:
970   if (to_put != buf)
971     gst_buffer_unref (to_put);
972
973   return res;
974
975   /* ERRORS */
976 no_pool:
977   {
978     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
979         ("Internal error: can't allocate images"),
980         ("We don't have a bufferpool negotiated"));
981     return GST_FLOW_ERROR;
982   }
983 no_buffer:
984   {
985     /* No image available. That's very bad ! */
986     GST_WARNING_OBJECT (xvimagesink, "could not create image");
987     return GST_FLOW_OK;
988   }
989 invalid_buffer:
990   {
991     /* No Window available to put our image into */
992     GST_WARNING_OBJECT (xvimagesink, "could not map image");
993     res = GST_FLOW_OK;
994     goto done;
995   }
996 no_window:
997   {
998     /* No Window available to put our image into */
999     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1000     res = GST_FLOW_ERROR;
1001     goto done;
1002   }
1003 activate_failed:
1004   {
1005     GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1006     res = GST_FLOW_ERROR;
1007     goto done;
1008   }
1009 }
1010
1011 static gboolean
1012 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1013 {
1014   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1015
1016   switch (GST_EVENT_TYPE (event)) {
1017     case GST_EVENT_TAG:{
1018       GstTagList *l;
1019       gchar *title = NULL;
1020
1021       gst_event_parse_tag (event, &l);
1022       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1023
1024       if (title) {
1025         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1026         gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1027             title);
1028
1029         g_free (title);
1030       }
1031       break;
1032     }
1033     default:
1034       break;
1035   }
1036   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1037 }
1038
1039 static gboolean
1040 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1041 {
1042   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1043   GstBufferPool *pool;
1044   GstStructure *config;
1045   GstCaps *caps;
1046   guint size;
1047   gboolean need_pool;
1048
1049   gst_query_parse_allocation (query, &caps, &need_pool);
1050
1051   if (caps == NULL)
1052     goto no_caps;
1053
1054   g_mutex_lock (&xvimagesink->flow_lock);
1055   if ((pool = xvimagesink->pool))
1056     gst_object_ref (pool);
1057   g_mutex_unlock (&xvimagesink->flow_lock);
1058
1059   if (pool != NULL) {
1060     GstCaps *pcaps;
1061
1062     /* we had a pool, check caps */
1063     GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1064     config = gst_buffer_pool_get_config (pool);
1065     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1066
1067     if (!gst_caps_is_equal (caps, pcaps)) {
1068       GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1069       /* different caps, we can't use this pool */
1070       gst_object_unref (pool);
1071       pool = NULL;
1072     }
1073     gst_structure_free (config);
1074   }
1075   if (pool == NULL && need_pool) {
1076     GstVideoInfo info;
1077
1078     if (!gst_video_info_from_caps (&info, caps))
1079       goto invalid_caps;
1080
1081     GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1082     pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1083
1084     /* the normal size of a frame */
1085     size = info.size;
1086
1087     config = gst_buffer_pool_get_config (pool);
1088     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1089     if (!gst_buffer_pool_set_config (pool, config))
1090       goto config_failed;
1091   }
1092   if (pool) {
1093     /* we need at least 2 buffer because we hold on to the last one */
1094     gst_query_add_allocation_pool (query, pool, size, 2, 0);
1095     gst_object_unref (pool);
1096   }
1097
1098   /* we also support various metadata */
1099   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1100   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1101
1102   return TRUE;
1103
1104   /* ERRORS */
1105 no_caps:
1106   {
1107     GST_DEBUG_OBJECT (bsink, "no caps specified");
1108     return FALSE;
1109   }
1110 invalid_caps:
1111   {
1112     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1113     return FALSE;
1114   }
1115 config_failed:
1116   {
1117     GST_DEBUG_OBJECT (bsink, "failed setting config");
1118     gst_object_unref (pool);
1119     return FALSE;
1120   }
1121 }
1122
1123 /* Interfaces stuff */
1124 static void
1125 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1126     GstStructure * structure)
1127 {
1128   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1129   GstPad *peer;
1130
1131   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1132     GstEvent *event;
1133     GstVideoRectangle src = { 0, };
1134     GstVideoRectangle dst = { 0, };
1135     GstVideoRectangle result;
1136     gdouble x, y, xscale = 1.0, yscale = 1.0;
1137     GstXWindow *xwindow;
1138
1139     event = gst_event_new_navigation (structure);
1140
1141     /* We take the flow_lock while we look at the window */
1142     g_mutex_lock (&xvimagesink->flow_lock);
1143
1144     if (!(xwindow = xvimagesink->xwindow)) {
1145       g_mutex_unlock (&xvimagesink->flow_lock);
1146       return;
1147     }
1148
1149     if (xvimagesink->keep_aspect) {
1150       /* We get the frame position using the calculated geometry from _setcaps
1151          that respect pixel aspect ratios */
1152       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1153       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1154       dst.w = xwindow->render_rect.w;
1155       dst.h = xwindow->render_rect.h;
1156
1157       gst_video_sink_center_rect (src, dst, &result, TRUE);
1158       result.x += xwindow->render_rect.x;
1159       result.y += xwindow->render_rect.y;
1160     } else {
1161       memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1162     }
1163
1164     g_mutex_unlock (&xvimagesink->flow_lock);
1165
1166     /* We calculate scaling using the original video frames geometry to include
1167        pixel aspect ratio scaling. */
1168     xscale = (gdouble) xvimagesink->video_width / result.w;
1169     yscale = (gdouble) xvimagesink->video_height / result.h;
1170
1171     /* Converting pointer coordinates to the non scaled geometry */
1172     if (gst_structure_get_double (structure, "pointer_x", &x)) {
1173       x = MIN (x, result.x + result.w);
1174       x = MAX (x - result.x, 0);
1175       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1176           (gdouble) x * xscale, NULL);
1177     }
1178     if (gst_structure_get_double (structure, "pointer_y", &y)) {
1179       y = MIN (y, result.y + result.h);
1180       y = MAX (y - result.y, 0);
1181       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1182           (gdouble) y * yscale, NULL);
1183     }
1184
1185     gst_pad_send_event (peer, event);
1186     gst_object_unref (peer);
1187   }
1188 }
1189
1190 static void
1191 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1192 {
1193   iface->send_event = gst_xvimagesink_navigation_send_event;
1194 }
1195
1196 static void
1197 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1198 {
1199   XID xwindow_id = id;
1200   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1201   GstXWindow *xwindow = NULL;
1202   GstXvContext *context;
1203
1204   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1205
1206   g_mutex_lock (&xvimagesink->flow_lock);
1207
1208   /* If we already use that window return */
1209   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1210     g_mutex_unlock (&xvimagesink->flow_lock);
1211     return;
1212   }
1213
1214   /* If the element has not initialized the X11 context try to do so */
1215   if (!xvimagesink->context &&
1216       !(xvimagesink->context =
1217           gst_xvcontext_new (&xvimagesink->config, NULL))) {
1218     g_mutex_unlock (&xvimagesink->flow_lock);
1219     /* we have thrown a GST_ELEMENT_ERROR now */
1220     return;
1221   }
1222
1223   context = xvimagesink->context;
1224
1225   gst_xvimagesink_update_colorbalance (xvimagesink);
1226
1227   /* If a window is there already we destroy it */
1228   if (xvimagesink->xwindow) {
1229     gst_xwindow_destroy (xvimagesink->xwindow);
1230     xvimagesink->xwindow = NULL;
1231   }
1232
1233   /* If the xid is 0 we go back to an internal window */
1234   if (xwindow_id == 0) {
1235     /* If no width/height caps nego did not happen window will be created
1236        during caps nego then */
1237     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1238         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1239       xwindow =
1240           gst_xvimagesink_xwindow_new (xvimagesink,
1241           GST_VIDEO_SINK_WIDTH (xvimagesink),
1242           GST_VIDEO_SINK_HEIGHT (xvimagesink));
1243     }
1244   } else {
1245     xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1246     gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1247   }
1248
1249   if (xwindow)
1250     xvimagesink->xwindow = xwindow;
1251
1252   g_mutex_unlock (&xvimagesink->flow_lock);
1253 }
1254
1255 static void
1256 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1257 {
1258   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1259
1260   GST_DEBUG ("doing expose");
1261   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1262   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1263 }
1264
1265 static void
1266 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1267     gboolean handle_events)
1268 {
1269   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1270
1271   g_mutex_lock (&xvimagesink->flow_lock);
1272   xvimagesink->handle_events = handle_events;
1273   if (G_LIKELY (xvimagesink->xwindow))
1274     gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1275   g_mutex_unlock (&xvimagesink->flow_lock);
1276 }
1277
1278 static void
1279 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1280     gint width, gint height)
1281 {
1282   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1283
1284   g_mutex_lock (&xvimagesink->flow_lock);
1285   if (G_LIKELY (xvimagesink->xwindow))
1286     gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1287         height);
1288   g_mutex_unlock (&xvimagesink->flow_lock);
1289 }
1290
1291 static void
1292 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1293 {
1294   iface->set_window_handle = gst_xvimagesink_set_window_handle;
1295   iface->expose = gst_xvimagesink_expose;
1296   iface->handle_events = gst_xvimagesink_set_event_handling;
1297   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1298 }
1299
1300 static const GList *
1301 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1302 {
1303   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1304
1305   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1306
1307   if (xvimagesink->context)
1308     return xvimagesink->context->channels_list;
1309   else
1310     return NULL;
1311 }
1312
1313 static void
1314 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1315     GstColorBalanceChannel * channel, gint value)
1316 {
1317   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1318
1319   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1320   g_return_if_fail (channel->label != NULL);
1321
1322   xvimagesink->config.cb_changed = TRUE;
1323
1324   /* Normalize val to [-1000, 1000] */
1325   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1326       (double) (channel->max_value - channel->min_value));
1327
1328   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1329     xvimagesink->config.hue = value;
1330   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1331     xvimagesink->config.saturation = value;
1332   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1333     xvimagesink->config.contrast = value;
1334   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1335     xvimagesink->config.brightness = value;
1336   } else {
1337     g_warning ("got an unknown channel %s", channel->label);
1338     return;
1339   }
1340
1341   gst_xvimagesink_update_colorbalance (xvimagesink);
1342 }
1343
1344 static gint
1345 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1346     GstColorBalanceChannel * channel)
1347 {
1348   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1349   gint value = 0;
1350
1351   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1352   g_return_val_if_fail (channel->label != NULL, 0);
1353
1354   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1355     value = xvimagesink->config.hue;
1356   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1357     value = xvimagesink->config.saturation;
1358   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1359     value = xvimagesink->config.contrast;
1360   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1361     value = xvimagesink->config.brightness;
1362   } else {
1363     g_warning ("got an unknown channel %s", channel->label);
1364   }
1365
1366   /* Normalize val to [channel->min_value, channel->max_value] */
1367   value = channel->min_value + (channel->max_value - channel->min_value) *
1368       (value + 1000) / 2000;
1369
1370   return value;
1371 }
1372
1373 static GstColorBalanceType
1374 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1375 {
1376   return GST_COLOR_BALANCE_HARDWARE;
1377 }
1378
1379 static void
1380 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1381 {
1382   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1383   iface->set_value = gst_xvimagesink_colorbalance_set_value;
1384   iface->get_value = gst_xvimagesink_colorbalance_get_value;
1385   iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1386 }
1387
1388 #if 0
1389 static const GList *
1390 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1391 {
1392   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1393   static GList *list = NULL;
1394
1395   if (!list) {
1396     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1397     list =
1398         g_list_append (list, g_object_class_find_property (klass,
1399             "autopaint-colorkey"));
1400     list =
1401         g_list_append (list, g_object_class_find_property (klass,
1402             "double-buffer"));
1403     list =
1404         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1405   }
1406
1407   return list;
1408 }
1409
1410 static void
1411 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1412     guint prop_id, const GParamSpec * pspec)
1413 {
1414   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1415
1416   switch (prop_id) {
1417     case PROP_DEVICE:
1418     case PROP_AUTOPAINT_COLORKEY:
1419     case PROP_DOUBLE_BUFFER:
1420     case PROP_COLORKEY:
1421       GST_DEBUG_OBJECT (xvimagesink,
1422           "probing device list and get capabilities");
1423       if (!xvimagesink->context) {
1424         GST_DEBUG_OBJECT (xvimagesink, "generating context");
1425         xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1426       }
1427       break;
1428     default:
1429       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1430       break;
1431   }
1432 }
1433
1434 static gboolean
1435 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1436     guint prop_id, const GParamSpec * pspec)
1437 {
1438   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1439   gboolean ret = FALSE;
1440
1441   switch (prop_id) {
1442     case PROP_DEVICE:
1443     case PROP_AUTOPAINT_COLORKEY:
1444     case PROP_DOUBLE_BUFFER:
1445     case PROP_COLORKEY:
1446       if (xvimagesink->context != NULL) {
1447         ret = FALSE;
1448       } else {
1449         ret = TRUE;
1450       }
1451       break;
1452     default:
1453       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1454       break;
1455   }
1456
1457   return ret;
1458 }
1459
1460 static GValueArray *
1461 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1462     guint prop_id, const GParamSpec * pspec)
1463 {
1464   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1465   GValueArray *array = NULL;
1466
1467   if (G_UNLIKELY (!xvimagesink->context)) {
1468     GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1469         "get values");
1470     goto beach;
1471   }
1472
1473   switch (prop_id) {
1474     case PROP_DEVICE:
1475     {
1476       guint i;
1477       GValue value = { 0 };
1478
1479       array = g_value_array_new (xvimagesink->context->nb_adaptors);
1480       g_value_init (&value, G_TYPE_STRING);
1481
1482       for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1483         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1484
1485         g_value_set_string (&value, adaptor_id_s);
1486         g_value_array_append (array, &value);
1487         g_free (adaptor_id_s);
1488       }
1489       g_value_unset (&value);
1490       break;
1491     }
1492     case PROP_AUTOPAINT_COLORKEY:
1493       if (xvimagesink->have_autopaint_colorkey) {
1494         GValue value = { 0 };
1495
1496         array = g_value_array_new (2);
1497         g_value_init (&value, G_TYPE_BOOLEAN);
1498         g_value_set_boolean (&value, FALSE);
1499         g_value_array_append (array, &value);
1500         g_value_set_boolean (&value, TRUE);
1501         g_value_array_append (array, &value);
1502         g_value_unset (&value);
1503       }
1504       break;
1505     case PROP_DOUBLE_BUFFER:
1506       if (xvimagesink->have_double_buffer) {
1507         GValue value = { 0 };
1508
1509         array = g_value_array_new (2);
1510         g_value_init (&value, G_TYPE_BOOLEAN);
1511         g_value_set_boolean (&value, FALSE);
1512         g_value_array_append (array, &value);
1513         g_value_set_boolean (&value, TRUE);
1514         g_value_array_append (array, &value);
1515         g_value_unset (&value);
1516       }
1517       break;
1518     case PROP_COLORKEY:
1519       if (xvimagesink->have_colorkey) {
1520         GValue value = { 0 };
1521
1522         array = g_value_array_new (1);
1523         g_value_init (&value, GST_TYPE_INT_RANGE);
1524         gst_value_set_int_range (&value, 0, 0xffffff);
1525         g_value_array_append (array, &value);
1526         g_value_unset (&value);
1527       }
1528       break;
1529     default:
1530       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1531       break;
1532   }
1533
1534 beach:
1535   return array;
1536 }
1537
1538 static void
1539 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1540     iface)
1541 {
1542   iface->get_properties = gst_xvimagesink_probe_get_properties;
1543   iface->probe_property = gst_xvimagesink_probe_probe_property;
1544   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1545   iface->get_values = gst_xvimagesink_probe_get_values;
1546 }
1547 #endif
1548
1549 /* =========================================== */
1550 /*                                             */
1551 /*              Init & Class init              */
1552 /*                                             */
1553 /* =========================================== */
1554
1555 static void
1556 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1557     const GValue * value, GParamSpec * pspec)
1558 {
1559   GstXvImageSink *xvimagesink;
1560
1561   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1562
1563   xvimagesink = GST_XVIMAGESINK (object);
1564
1565   switch (prop_id) {
1566     case PROP_HUE:
1567       xvimagesink->config.hue = g_value_get_int (value);
1568       xvimagesink->config.cb_changed = TRUE;
1569       gst_xvimagesink_update_colorbalance (xvimagesink);
1570       break;
1571     case PROP_CONTRAST:
1572       xvimagesink->config.contrast = g_value_get_int (value);
1573       xvimagesink->config.cb_changed = TRUE;
1574       gst_xvimagesink_update_colorbalance (xvimagesink);
1575       break;
1576     case PROP_BRIGHTNESS:
1577       xvimagesink->config.brightness = g_value_get_int (value);
1578       xvimagesink->config.cb_changed = TRUE;
1579       gst_xvimagesink_update_colorbalance (xvimagesink);
1580       break;
1581     case PROP_SATURATION:
1582       xvimagesink->config.saturation = g_value_get_int (value);
1583       xvimagesink->config.cb_changed = TRUE;
1584       gst_xvimagesink_update_colorbalance (xvimagesink);
1585       break;
1586     case PROP_DISPLAY:
1587       g_free (xvimagesink->config.display_name);
1588       xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1589       break;
1590     case PROP_SYNCHRONOUS:
1591       xvimagesink->synchronous = g_value_get_boolean (value);
1592       if (xvimagesink->context) {
1593         gst_xvcontext_set_synchronous (xvimagesink->context,
1594             xvimagesink->synchronous);
1595       }
1596       break;
1597     case PROP_PIXEL_ASPECT_RATIO:
1598       g_free (xvimagesink->par);
1599       xvimagesink->par = g_new0 (GValue, 1);
1600       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1601       if (!g_value_transform (value, xvimagesink->par)) {
1602         g_warning ("Could not transform string to aspect ratio");
1603         gst_value_set_fraction (xvimagesink->par, 1, 1);
1604       }
1605       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1606           gst_value_get_fraction_numerator (xvimagesink->par),
1607           gst_value_get_fraction_denominator (xvimagesink->par));
1608       break;
1609     case PROP_FORCE_ASPECT_RATIO:
1610       xvimagesink->keep_aspect = g_value_get_boolean (value);
1611       break;
1612     case PROP_HANDLE_EVENTS:
1613       gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1614           g_value_get_boolean (value));
1615       gst_xvimagesink_manage_event_thread (xvimagesink);
1616       break;
1617     case PROP_DEVICE:
1618       xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1619       break;
1620     case PROP_HANDLE_EXPOSE:
1621       xvimagesink->handle_expose = g_value_get_boolean (value);
1622       gst_xvimagesink_manage_event_thread (xvimagesink);
1623       break;
1624     case PROP_DOUBLE_BUFFER:
1625       xvimagesink->double_buffer = g_value_get_boolean (value);
1626       break;
1627     case PROP_AUTOPAINT_COLORKEY:
1628       xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1629       break;
1630     case PROP_COLORKEY:
1631       xvimagesink->config.colorkey = g_value_get_int (value);
1632       break;
1633     case PROP_DRAW_BORDERS:
1634       xvimagesink->draw_borders = g_value_get_boolean (value);
1635       break;
1636     default:
1637       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1638       break;
1639   }
1640 }
1641
1642 static void
1643 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1644     GValue * value, GParamSpec * pspec)
1645 {
1646   GstXvImageSink *xvimagesink;
1647
1648   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1649
1650   xvimagesink = GST_XVIMAGESINK (object);
1651
1652   switch (prop_id) {
1653     case PROP_HUE:
1654       g_value_set_int (value, xvimagesink->config.hue);
1655       break;
1656     case PROP_CONTRAST:
1657       g_value_set_int (value, xvimagesink->config.contrast);
1658       break;
1659     case PROP_BRIGHTNESS:
1660       g_value_set_int (value, xvimagesink->config.brightness);
1661       break;
1662     case PROP_SATURATION:
1663       g_value_set_int (value, xvimagesink->config.saturation);
1664       break;
1665     case PROP_DISPLAY:
1666       g_value_set_string (value, xvimagesink->config.display_name);
1667       break;
1668     case PROP_SYNCHRONOUS:
1669       g_value_set_boolean (value, xvimagesink->synchronous);
1670       break;
1671     case PROP_PIXEL_ASPECT_RATIO:
1672       if (xvimagesink->par)
1673         g_value_transform (xvimagesink->par, value);
1674       break;
1675     case PROP_FORCE_ASPECT_RATIO:
1676       g_value_set_boolean (value, xvimagesink->keep_aspect);
1677       break;
1678     case PROP_HANDLE_EVENTS:
1679       g_value_set_boolean (value, xvimagesink->handle_events);
1680       break;
1681     case PROP_DEVICE:
1682     {
1683       char *adaptor_nr_s =
1684           g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1685
1686       g_value_set_string (value, adaptor_nr_s);
1687       g_free (adaptor_nr_s);
1688       break;
1689     }
1690     case PROP_DEVICE_NAME:
1691       if (xvimagesink->context && xvimagesink->context->adaptors) {
1692         g_value_set_string (value,
1693             xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1694       } else {
1695         g_value_set_string (value, NULL);
1696       }
1697       break;
1698     case PROP_HANDLE_EXPOSE:
1699       g_value_set_boolean (value, xvimagesink->handle_expose);
1700       break;
1701     case PROP_DOUBLE_BUFFER:
1702       g_value_set_boolean (value, xvimagesink->double_buffer);
1703       break;
1704     case PROP_AUTOPAINT_COLORKEY:
1705       g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1706       break;
1707     case PROP_COLORKEY:
1708       g_value_set_int (value, xvimagesink->config.colorkey);
1709       break;
1710     case PROP_DRAW_BORDERS:
1711       g_value_set_boolean (value, xvimagesink->draw_borders);
1712       break;
1713     case PROP_WINDOW_WIDTH:
1714       if (xvimagesink->xwindow)
1715         g_value_set_uint64 (value, xvimagesink->xwindow->width);
1716       else
1717         g_value_set_uint64 (value, 0);
1718       break;
1719     case PROP_WINDOW_HEIGHT:
1720       if (xvimagesink->xwindow)
1721         g_value_set_uint64 (value, xvimagesink->xwindow->height);
1722       else
1723         g_value_set_uint64 (value, 0);
1724       break;
1725     default:
1726       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1727       break;
1728   }
1729 }
1730
1731 static gboolean
1732 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1733 {
1734   GError *error = NULL;
1735
1736   /* Initializing the XvContext unless already done through GstVideoOverlay */
1737   if (!xvimagesink->context) {
1738     GstXvContext *context;
1739     if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1740       goto no_context;
1741
1742     GST_OBJECT_LOCK (xvimagesink);
1743     xvimagesink->context = context;
1744   } else
1745     GST_OBJECT_LOCK (xvimagesink);
1746   /* make an allocator for this context */
1747   xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1748   GST_OBJECT_UNLOCK (xvimagesink);
1749
1750   /* update object's par with calculated one if not set yet */
1751   if (!xvimagesink->par) {
1752     xvimagesink->par = g_new0 (GValue, 1);
1753     gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1754     GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1755   }
1756   /* call XSynchronize with the current value of synchronous */
1757   gst_xvcontext_set_synchronous (xvimagesink->context,
1758       xvimagesink->synchronous);
1759   gst_xvimagesink_update_colorbalance (xvimagesink);
1760   gst_xvimagesink_manage_event_thread (xvimagesink);
1761
1762   return TRUE;
1763
1764 no_context:
1765   {
1766     gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1767         error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1768         error->message, __FILE__, GST_FUNCTION, __LINE__);
1769     return FALSE;
1770   }
1771 }
1772
1773 static void
1774 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1775 {
1776   GThread *thread;
1777   GstXvContext *context;
1778
1779   GST_OBJECT_LOCK (xvimagesink);
1780   xvimagesink->running = FALSE;
1781   /* grab thread and mark it as NULL */
1782   thread = xvimagesink->event_thread;
1783   xvimagesink->event_thread = NULL;
1784   GST_OBJECT_UNLOCK (xvimagesink);
1785
1786   /* Wait for our event thread to finish before we clean up our stuff. */
1787   if (thread)
1788     g_thread_join (thread);
1789
1790   if (xvimagesink->cur_image) {
1791     gst_buffer_unref (xvimagesink->cur_image);
1792     xvimagesink->cur_image = NULL;
1793   }
1794
1795   g_mutex_lock (&xvimagesink->flow_lock);
1796
1797   if (xvimagesink->pool) {
1798     gst_object_unref (xvimagesink->pool);
1799     xvimagesink->pool = NULL;
1800   }
1801
1802   if (xvimagesink->xwindow) {
1803     gst_xwindow_clear (xvimagesink->xwindow);
1804     gst_xwindow_destroy (xvimagesink->xwindow);
1805     xvimagesink->xwindow = NULL;
1806   }
1807   g_mutex_unlock (&xvimagesink->flow_lock);
1808
1809   if (xvimagesink->allocator) {
1810     gst_object_unref (xvimagesink->allocator);
1811     xvimagesink->allocator = NULL;
1812   }
1813
1814   GST_OBJECT_LOCK (xvimagesink);
1815   /* grab context and mark it as NULL */
1816   context = xvimagesink->context;
1817   xvimagesink->context = NULL;
1818   GST_OBJECT_UNLOCK (xvimagesink);
1819
1820   if (context)
1821     gst_xvcontext_unref (context);
1822 }
1823
1824 /* Finalize is called only once, dispose can be called multiple times.
1825  * We use mutexes and don't reset stuff to NULL here so let's register
1826  * as a finalize. */
1827 static void
1828 gst_xvimagesink_finalize (GObject * object)
1829 {
1830   GstXvImageSink *xvimagesink;
1831
1832   xvimagesink = GST_XVIMAGESINK (object);
1833
1834   gst_xvimagesink_close (xvimagesink);
1835
1836   gst_xvcontext_config_clear (&xvimagesink->config);
1837
1838   if (xvimagesink->par) {
1839     g_free (xvimagesink->par);
1840     xvimagesink->par = NULL;
1841   }
1842   g_mutex_clear (&xvimagesink->flow_lock);
1843   g_free (xvimagesink->media_title);
1844
1845   G_OBJECT_CLASS (parent_class)->finalize (object);
1846 }
1847
1848 static void
1849 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1850 {
1851   xvimagesink->config.display_name = NULL;
1852   xvimagesink->config.adaptor_nr = 0;
1853   xvimagesink->config.autopaint_colorkey = TRUE;
1854   xvimagesink->config.double_buffer = TRUE;
1855   /* on 16bit displays this becomes r,g,b = 1,2,3
1856    * on 24bit displays this becomes r,g,b = 8,8,16
1857    * as a port atom value */
1858   xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1859   xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1860   xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1861   xvimagesink->config.cb_changed = FALSE;
1862
1863   xvimagesink->context = NULL;
1864   xvimagesink->xwindow = NULL;
1865   xvimagesink->cur_image = NULL;
1866
1867   xvimagesink->fps_n = 0;
1868   xvimagesink->fps_d = 0;
1869   xvimagesink->video_width = 0;
1870   xvimagesink->video_height = 0;
1871
1872   g_mutex_init (&xvimagesink->flow_lock);
1873
1874   xvimagesink->pool = NULL;
1875
1876   xvimagesink->synchronous = FALSE;
1877   xvimagesink->running = FALSE;
1878   xvimagesink->keep_aspect = TRUE;
1879   xvimagesink->handle_events = TRUE;
1880   xvimagesink->par = NULL;
1881   xvimagesink->handle_expose = TRUE;
1882
1883   xvimagesink->draw_borders = TRUE;
1884 }
1885
1886 static void
1887 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1888 {
1889   GObjectClass *gobject_class;
1890   GstElementClass *gstelement_class;
1891   GstBaseSinkClass *gstbasesink_class;
1892   GstVideoSinkClass *videosink_class;
1893
1894   gobject_class = (GObjectClass *) klass;
1895   gstelement_class = (GstElementClass *) klass;
1896   gstbasesink_class = (GstBaseSinkClass *) klass;
1897   videosink_class = (GstVideoSinkClass *) klass;
1898
1899   parent_class = g_type_class_peek_parent (klass);
1900
1901   gobject_class->set_property = gst_xvimagesink_set_property;
1902   gobject_class->get_property = gst_xvimagesink_get_property;
1903
1904   g_object_class_install_property (gobject_class, PROP_CONTRAST,
1905       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1906           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1907   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1908       g_param_spec_int ("brightness", "Brightness",
1909           "The brightness of the video", -1000, 1000, 0,
1910           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1911   g_object_class_install_property (gobject_class, PROP_HUE,
1912       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1913           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1914   g_object_class_install_property (gobject_class, PROP_SATURATION,
1915       g_param_spec_int ("saturation", "Saturation",
1916           "The saturation of the video", -1000, 1000, 0,
1917           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1918   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1919       g_param_spec_string ("display", "Display", "X Display name", NULL,
1920           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1921   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1922       g_param_spec_boolean ("synchronous", "Synchronous",
1923           "When enabled, runs the X display in synchronous mode. "
1924           "(unrelated to A/V sync, used only for debugging)", FALSE,
1925           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1926   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1927       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1928           "The pixel aspect ratio of the device", "1/1",
1929           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1931       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1932           "When enabled, scaling will respect original aspect ratio", TRUE,
1933           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1934   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1935       g_param_spec_boolean ("handle-events", "Handle XEvents",
1936           "When enabled, XEvents will be selected and handled", TRUE,
1937           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1938   g_object_class_install_property (gobject_class, PROP_DEVICE,
1939       g_param_spec_string ("device", "Adaptor number",
1940           "The number of the video adaptor", "0",
1941           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1942   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1943       g_param_spec_string ("device-name", "Adaptor name",
1944           "The name of the video adaptor", NULL,
1945           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1946   /**
1947    * GstXvImageSink:handle-expose
1948    *
1949    * When enabled, the current frame will always be drawn in response to X
1950    * Expose.
1951    */
1952   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1953       g_param_spec_boolean ("handle-expose", "Handle expose",
1954           "When enabled, "
1955           "the current frame will always be drawn in response to X Expose "
1956           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957
1958   /**
1959    * GstXvImageSink:double-buffer
1960    *
1961    * Whether to double-buffer the output.
1962    */
1963   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1964       g_param_spec_boolean ("double-buffer", "Double-buffer",
1965           "Whether to double-buffer the output", TRUE,
1966           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1967   /**
1968    * GstXvImageSink:autopaint-colorkey
1969    *
1970    * Whether to autofill overlay with colorkey
1971    */
1972   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1973       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1974           "Whether to autofill overlay with colorkey", TRUE,
1975           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1976   /**
1977    * GstXvImageSink:colorkey
1978    *
1979    * Color to use for the overlay mask.
1980    */
1981   g_object_class_install_property (gobject_class, PROP_COLORKEY,
1982       g_param_spec_int ("colorkey", "Colorkey",
1983           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1984           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1985
1986   /**
1987    * GstXvImageSink:draw-borders
1988    *
1989    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1990    * unused parts of the video area.
1991    */
1992   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1993       g_param_spec_boolean ("draw-borders", "Draw Borders",
1994           "Draw black borders to fill unused area in force-aspect-ratio mode",
1995           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1996
1997   /**
1998    * GstXvImageSink:window-width
1999    *
2000    * Actual width of the video window.
2001    */
2002   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2003       g_param_spec_uint64 ("window-width", "window-width",
2004           "Width of the window", 0, G_MAXUINT64, 0,
2005           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2006
2007   /**
2008    * GstXvImageSink:window-height
2009    *
2010    * Actual height of the video window.
2011    */
2012   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2013       g_param_spec_uint64 ("window-height", "window-height",
2014           "Height of the window", 0, G_MAXUINT64, 0,
2015           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2016
2017   gobject_class->finalize = gst_xvimagesink_finalize;
2018
2019   gst_element_class_set_static_metadata (gstelement_class,
2020       "Video sink", "Sink/Video",
2021       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2022
2023   gst_element_class_add_pad_template (gstelement_class,
2024       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2025
2026   gstelement_class->change_state =
2027       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2028
2029   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2030   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2031   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2032   gstbasesink_class->propose_allocation =
2033       GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2034   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2035
2036   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
2037 }