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