xvimagesink: fix navigation event leak when early returning
[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   gboolean handled = FALSE;
1131   GstEvent *event = NULL;
1132
1133   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1134     GstVideoRectangle src = { 0, };
1135     GstVideoRectangle dst = { 0, };
1136     GstVideoRectangle result;
1137     gdouble x, y, xscale = 1.0, yscale = 1.0;
1138     GstXWindow *xwindow;
1139
1140     /* We take the flow_lock while we look at the window */
1141     g_mutex_lock (&xvimagesink->flow_lock);
1142
1143     if (!(xwindow = xvimagesink->xwindow)) {
1144       g_mutex_unlock (&xvimagesink->flow_lock);
1145       return;
1146     }
1147
1148     if (xvimagesink->keep_aspect) {
1149       /* We get the frame position using the calculated geometry from _setcaps
1150          that respect pixel aspect ratios */
1151       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1152       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1153       dst.w = xwindow->render_rect.w;
1154       dst.h = xwindow->render_rect.h;
1155
1156       gst_video_sink_center_rect (src, dst, &result, TRUE);
1157       result.x += xwindow->render_rect.x;
1158       result.y += xwindow->render_rect.y;
1159     } else {
1160       memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1161     }
1162
1163     g_mutex_unlock (&xvimagesink->flow_lock);
1164
1165     /* We calculate scaling using the original video frames geometry to include
1166        pixel aspect ratio scaling. */
1167     xscale = (gdouble) xvimagesink->video_width / result.w;
1168     yscale = (gdouble) xvimagesink->video_height / result.h;
1169
1170     /* Converting pointer coordinates to the non scaled geometry */
1171     if (gst_structure_get_double (structure, "pointer_x", &x)) {
1172       x = MIN (x, result.x + result.w);
1173       x = MAX (x - result.x, 0);
1174       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1175           (gdouble) x * xscale, NULL);
1176     }
1177     if (gst_structure_get_double (structure, "pointer_y", &y)) {
1178       y = MIN (y, result.y + result.h);
1179       y = MAX (y - result.y, 0);
1180       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1181           (gdouble) y * yscale, NULL);
1182     }
1183
1184     event = gst_event_new_navigation (structure);
1185     gst_event_ref (event);
1186     handled = gst_pad_send_event (peer, event);
1187     gst_object_unref (peer);
1188   }
1189
1190   if (!handled && event) {
1191     gst_element_post_message ((GstElement *) xvimagesink,
1192         gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1193   }
1194
1195   if (event)
1196     gst_event_unref (event);
1197 }
1198
1199 static void
1200 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1201 {
1202   iface->send_event = gst_xvimagesink_navigation_send_event;
1203 }
1204
1205 static void
1206 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1207 {
1208   XID xwindow_id = id;
1209   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1210   GstXWindow *xwindow = NULL;
1211   GstXvContext *context;
1212
1213   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1214
1215   g_mutex_lock (&xvimagesink->flow_lock);
1216
1217   /* If we already use that window return */
1218   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1219     g_mutex_unlock (&xvimagesink->flow_lock);
1220     return;
1221   }
1222
1223   /* If the element has not initialized the X11 context try to do so */
1224   if (!xvimagesink->context &&
1225       !(xvimagesink->context =
1226           gst_xvcontext_new (&xvimagesink->config, NULL))) {
1227     g_mutex_unlock (&xvimagesink->flow_lock);
1228     /* we have thrown a GST_ELEMENT_ERROR now */
1229     return;
1230   }
1231
1232   context = xvimagesink->context;
1233
1234   gst_xvimagesink_update_colorbalance (xvimagesink);
1235
1236   /* If a window is there already we destroy it */
1237   if (xvimagesink->xwindow) {
1238     gst_xwindow_destroy (xvimagesink->xwindow);
1239     xvimagesink->xwindow = NULL;
1240   }
1241
1242   /* If the xid is 0 we go back to an internal window */
1243   if (xwindow_id == 0) {
1244     /* If no width/height caps nego did not happen window will be created
1245        during caps nego then */
1246     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1247         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1248       xwindow =
1249           gst_xvimagesink_xwindow_new (xvimagesink,
1250           GST_VIDEO_SINK_WIDTH (xvimagesink),
1251           GST_VIDEO_SINK_HEIGHT (xvimagesink));
1252     }
1253   } else {
1254     xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1255     gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1256   }
1257
1258   if (xwindow)
1259     xvimagesink->xwindow = xwindow;
1260
1261   g_mutex_unlock (&xvimagesink->flow_lock);
1262 }
1263
1264 static void
1265 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1266 {
1267   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1268
1269   GST_DEBUG ("doing expose");
1270   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1271   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1272 }
1273
1274 static void
1275 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1276     gboolean handle_events)
1277 {
1278   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1279
1280   g_mutex_lock (&xvimagesink->flow_lock);
1281   xvimagesink->handle_events = handle_events;
1282   if (G_LIKELY (xvimagesink->xwindow))
1283     gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1284   g_mutex_unlock (&xvimagesink->flow_lock);
1285 }
1286
1287 static void
1288 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1289     gint width, gint height)
1290 {
1291   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1292
1293   g_mutex_lock (&xvimagesink->flow_lock);
1294   if (G_LIKELY (xvimagesink->xwindow))
1295     gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1296         height);
1297   g_mutex_unlock (&xvimagesink->flow_lock);
1298 }
1299
1300 static void
1301 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1302 {
1303   iface->set_window_handle = gst_xvimagesink_set_window_handle;
1304   iface->expose = gst_xvimagesink_expose;
1305   iface->handle_events = gst_xvimagesink_set_event_handling;
1306   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1307 }
1308
1309 static const GList *
1310 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1311 {
1312   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1313
1314   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1315
1316   if (xvimagesink->context)
1317     return xvimagesink->context->channels_list;
1318   else
1319     return NULL;
1320 }
1321
1322 static void
1323 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1324     GstColorBalanceChannel * channel, gint value)
1325 {
1326   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1327
1328   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1329   g_return_if_fail (channel->label != NULL);
1330
1331   xvimagesink->config.cb_changed = TRUE;
1332
1333   /* Normalize val to [-1000, 1000] */
1334   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1335       (double) (channel->max_value - channel->min_value));
1336
1337   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1338     xvimagesink->config.hue = value;
1339   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1340     xvimagesink->config.saturation = value;
1341   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1342     xvimagesink->config.contrast = value;
1343   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1344     xvimagesink->config.brightness = value;
1345   } else {
1346     g_warning ("got an unknown channel %s", channel->label);
1347     return;
1348   }
1349
1350   gst_xvimagesink_update_colorbalance (xvimagesink);
1351 }
1352
1353 static gint
1354 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1355     GstColorBalanceChannel * channel)
1356 {
1357   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1358   gint value = 0;
1359
1360   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1361   g_return_val_if_fail (channel->label != NULL, 0);
1362
1363   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1364     value = xvimagesink->config.hue;
1365   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1366     value = xvimagesink->config.saturation;
1367   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1368     value = xvimagesink->config.contrast;
1369   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1370     value = xvimagesink->config.brightness;
1371   } else {
1372     g_warning ("got an unknown channel %s", channel->label);
1373   }
1374
1375   /* Normalize val to [channel->min_value, channel->max_value] */
1376   value = channel->min_value + (channel->max_value - channel->min_value) *
1377       (value + 1000) / 2000;
1378
1379   return value;
1380 }
1381
1382 static GstColorBalanceType
1383 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1384 {
1385   return GST_COLOR_BALANCE_HARDWARE;
1386 }
1387
1388 static void
1389 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1390 {
1391   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1392   iface->set_value = gst_xvimagesink_colorbalance_set_value;
1393   iface->get_value = gst_xvimagesink_colorbalance_get_value;
1394   iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1395 }
1396
1397 #if 0
1398 static const GList *
1399 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1400 {
1401   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1402   static GList *list = NULL;
1403
1404   if (!list) {
1405     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1406     list =
1407         g_list_append (list, g_object_class_find_property (klass,
1408             "autopaint-colorkey"));
1409     list =
1410         g_list_append (list, g_object_class_find_property (klass,
1411             "double-buffer"));
1412     list =
1413         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1414   }
1415
1416   return list;
1417 }
1418
1419 static void
1420 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1421     guint prop_id, const GParamSpec * pspec)
1422 {
1423   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1424
1425   switch (prop_id) {
1426     case PROP_DEVICE:
1427     case PROP_AUTOPAINT_COLORKEY:
1428     case PROP_DOUBLE_BUFFER:
1429     case PROP_COLORKEY:
1430       GST_DEBUG_OBJECT (xvimagesink,
1431           "probing device list and get capabilities");
1432       if (!xvimagesink->context) {
1433         GST_DEBUG_OBJECT (xvimagesink, "generating context");
1434         xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1435       }
1436       break;
1437     default:
1438       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1439       break;
1440   }
1441 }
1442
1443 static gboolean
1444 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1445     guint prop_id, const GParamSpec * pspec)
1446 {
1447   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1448   gboolean ret = FALSE;
1449
1450   switch (prop_id) {
1451     case PROP_DEVICE:
1452     case PROP_AUTOPAINT_COLORKEY:
1453     case PROP_DOUBLE_BUFFER:
1454     case PROP_COLORKEY:
1455       if (xvimagesink->context != NULL) {
1456         ret = FALSE;
1457       } else {
1458         ret = TRUE;
1459       }
1460       break;
1461     default:
1462       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1463       break;
1464   }
1465
1466   return ret;
1467 }
1468
1469 static GValueArray *
1470 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1471     guint prop_id, const GParamSpec * pspec)
1472 {
1473   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1474   GValueArray *array = NULL;
1475
1476   if (G_UNLIKELY (!xvimagesink->context)) {
1477     GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1478         "get values");
1479     goto beach;
1480   }
1481
1482   switch (prop_id) {
1483     case PROP_DEVICE:
1484     {
1485       guint i;
1486       GValue value = { 0 };
1487
1488       array = g_value_array_new (xvimagesink->context->nb_adaptors);
1489       g_value_init (&value, G_TYPE_STRING);
1490
1491       for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1492         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1493
1494         g_value_set_string (&value, adaptor_id_s);
1495         g_value_array_append (array, &value);
1496         g_free (adaptor_id_s);
1497       }
1498       g_value_unset (&value);
1499       break;
1500     }
1501     case PROP_AUTOPAINT_COLORKEY:
1502       if (xvimagesink->have_autopaint_colorkey) {
1503         GValue value = { 0 };
1504
1505         array = g_value_array_new (2);
1506         g_value_init (&value, G_TYPE_BOOLEAN);
1507         g_value_set_boolean (&value, FALSE);
1508         g_value_array_append (array, &value);
1509         g_value_set_boolean (&value, TRUE);
1510         g_value_array_append (array, &value);
1511         g_value_unset (&value);
1512       }
1513       break;
1514     case PROP_DOUBLE_BUFFER:
1515       if (xvimagesink->have_double_buffer) {
1516         GValue value = { 0 };
1517
1518         array = g_value_array_new (2);
1519         g_value_init (&value, G_TYPE_BOOLEAN);
1520         g_value_set_boolean (&value, FALSE);
1521         g_value_array_append (array, &value);
1522         g_value_set_boolean (&value, TRUE);
1523         g_value_array_append (array, &value);
1524         g_value_unset (&value);
1525       }
1526       break;
1527     case PROP_COLORKEY:
1528       if (xvimagesink->have_colorkey) {
1529         GValue value = { 0 };
1530
1531         array = g_value_array_new (1);
1532         g_value_init (&value, GST_TYPE_INT_RANGE);
1533         gst_value_set_int_range (&value, 0, 0xffffff);
1534         g_value_array_append (array, &value);
1535         g_value_unset (&value);
1536       }
1537       break;
1538     default:
1539       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1540       break;
1541   }
1542
1543 beach:
1544   return array;
1545 }
1546
1547 static void
1548 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1549     iface)
1550 {
1551   iface->get_properties = gst_xvimagesink_probe_get_properties;
1552   iface->probe_property = gst_xvimagesink_probe_probe_property;
1553   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1554   iface->get_values = gst_xvimagesink_probe_get_values;
1555 }
1556 #endif
1557
1558 /* =========================================== */
1559 /*                                             */
1560 /*              Init & Class init              */
1561 /*                                             */
1562 /* =========================================== */
1563
1564 static void
1565 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1566     const GValue * value, GParamSpec * pspec)
1567 {
1568   GstXvImageSink *xvimagesink;
1569
1570   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1571
1572   xvimagesink = GST_XVIMAGESINK (object);
1573
1574   switch (prop_id) {
1575     case PROP_HUE:
1576       xvimagesink->config.hue = g_value_get_int (value);
1577       xvimagesink->config.cb_changed = TRUE;
1578       gst_xvimagesink_update_colorbalance (xvimagesink);
1579       break;
1580     case PROP_CONTRAST:
1581       xvimagesink->config.contrast = g_value_get_int (value);
1582       xvimagesink->config.cb_changed = TRUE;
1583       gst_xvimagesink_update_colorbalance (xvimagesink);
1584       break;
1585     case PROP_BRIGHTNESS:
1586       xvimagesink->config.brightness = g_value_get_int (value);
1587       xvimagesink->config.cb_changed = TRUE;
1588       gst_xvimagesink_update_colorbalance (xvimagesink);
1589       break;
1590     case PROP_SATURATION:
1591       xvimagesink->config.saturation = g_value_get_int (value);
1592       xvimagesink->config.cb_changed = TRUE;
1593       gst_xvimagesink_update_colorbalance (xvimagesink);
1594       break;
1595     case PROP_DISPLAY:
1596       g_free (xvimagesink->config.display_name);
1597       xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1598       break;
1599     case PROP_SYNCHRONOUS:
1600       xvimagesink->synchronous = g_value_get_boolean (value);
1601       if (xvimagesink->context) {
1602         gst_xvcontext_set_synchronous (xvimagesink->context,
1603             xvimagesink->synchronous);
1604       }
1605       break;
1606     case PROP_PIXEL_ASPECT_RATIO:
1607       g_free (xvimagesink->par);
1608       xvimagesink->par = g_new0 (GValue, 1);
1609       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1610       if (!g_value_transform (value, xvimagesink->par)) {
1611         g_warning ("Could not transform string to aspect ratio");
1612         gst_value_set_fraction (xvimagesink->par, 1, 1);
1613       }
1614       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1615           gst_value_get_fraction_numerator (xvimagesink->par),
1616           gst_value_get_fraction_denominator (xvimagesink->par));
1617       break;
1618     case PROP_FORCE_ASPECT_RATIO:
1619       xvimagesink->keep_aspect = g_value_get_boolean (value);
1620       break;
1621     case PROP_HANDLE_EVENTS:
1622       gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1623           g_value_get_boolean (value));
1624       gst_xvimagesink_manage_event_thread (xvimagesink);
1625       break;
1626     case PROP_DEVICE:
1627       xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1628       break;
1629     case PROP_HANDLE_EXPOSE:
1630       xvimagesink->handle_expose = g_value_get_boolean (value);
1631       gst_xvimagesink_manage_event_thread (xvimagesink);
1632       break;
1633     case PROP_DOUBLE_BUFFER:
1634       xvimagesink->double_buffer = g_value_get_boolean (value);
1635       break;
1636     case PROP_AUTOPAINT_COLORKEY:
1637       xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1638       break;
1639     case PROP_COLORKEY:
1640       xvimagesink->config.colorkey = g_value_get_int (value);
1641       break;
1642     case PROP_DRAW_BORDERS:
1643       xvimagesink->draw_borders = g_value_get_boolean (value);
1644       break;
1645     default:
1646       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1647       break;
1648   }
1649 }
1650
1651 static void
1652 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1653     GValue * value, GParamSpec * pspec)
1654 {
1655   GstXvImageSink *xvimagesink;
1656
1657   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1658
1659   xvimagesink = GST_XVIMAGESINK (object);
1660
1661   switch (prop_id) {
1662     case PROP_HUE:
1663       g_value_set_int (value, xvimagesink->config.hue);
1664       break;
1665     case PROP_CONTRAST:
1666       g_value_set_int (value, xvimagesink->config.contrast);
1667       break;
1668     case PROP_BRIGHTNESS:
1669       g_value_set_int (value, xvimagesink->config.brightness);
1670       break;
1671     case PROP_SATURATION:
1672       g_value_set_int (value, xvimagesink->config.saturation);
1673       break;
1674     case PROP_DISPLAY:
1675       g_value_set_string (value, xvimagesink->config.display_name);
1676       break;
1677     case PROP_SYNCHRONOUS:
1678       g_value_set_boolean (value, xvimagesink->synchronous);
1679       break;
1680     case PROP_PIXEL_ASPECT_RATIO:
1681       if (xvimagesink->par)
1682         g_value_transform (xvimagesink->par, value);
1683       break;
1684     case PROP_FORCE_ASPECT_RATIO:
1685       g_value_set_boolean (value, xvimagesink->keep_aspect);
1686       break;
1687     case PROP_HANDLE_EVENTS:
1688       g_value_set_boolean (value, xvimagesink->handle_events);
1689       break;
1690     case PROP_DEVICE:
1691     {
1692       char *adaptor_nr_s =
1693           g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1694
1695       g_value_set_string (value, adaptor_nr_s);
1696       g_free (adaptor_nr_s);
1697       break;
1698     }
1699     case PROP_DEVICE_NAME:
1700       if (xvimagesink->context && xvimagesink->context->adaptors) {
1701         g_value_set_string (value,
1702             xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1703       } else {
1704         g_value_set_string (value, NULL);
1705       }
1706       break;
1707     case PROP_HANDLE_EXPOSE:
1708       g_value_set_boolean (value, xvimagesink->handle_expose);
1709       break;
1710     case PROP_DOUBLE_BUFFER:
1711       g_value_set_boolean (value, xvimagesink->double_buffer);
1712       break;
1713     case PROP_AUTOPAINT_COLORKEY:
1714       g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1715       break;
1716     case PROP_COLORKEY:
1717       g_value_set_int (value, xvimagesink->config.colorkey);
1718       break;
1719     case PROP_DRAW_BORDERS:
1720       g_value_set_boolean (value, xvimagesink->draw_borders);
1721       break;
1722     case PROP_WINDOW_WIDTH:
1723       if (xvimagesink->xwindow)
1724         g_value_set_uint64 (value, xvimagesink->xwindow->width);
1725       else
1726         g_value_set_uint64 (value, 0);
1727       break;
1728     case PROP_WINDOW_HEIGHT:
1729       if (xvimagesink->xwindow)
1730         g_value_set_uint64 (value, xvimagesink->xwindow->height);
1731       else
1732         g_value_set_uint64 (value, 0);
1733       break;
1734     default:
1735       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1736       break;
1737   }
1738 }
1739
1740 static gboolean
1741 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1742 {
1743   GError *error = NULL;
1744
1745   /* Initializing the XvContext unless already done through GstVideoOverlay */
1746   if (!xvimagesink->context) {
1747     GstXvContext *context;
1748     if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1749       goto no_context;
1750
1751     GST_OBJECT_LOCK (xvimagesink);
1752     xvimagesink->context = context;
1753   } else
1754     GST_OBJECT_LOCK (xvimagesink);
1755   /* make an allocator for this context */
1756   xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1757   GST_OBJECT_UNLOCK (xvimagesink);
1758
1759   /* update object's par with calculated one if not set yet */
1760   if (!xvimagesink->par) {
1761     xvimagesink->par = g_new0 (GValue, 1);
1762     gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1763     GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1764   }
1765   /* call XSynchronize with the current value of synchronous */
1766   gst_xvcontext_set_synchronous (xvimagesink->context,
1767       xvimagesink->synchronous);
1768   gst_xvimagesink_update_colorbalance (xvimagesink);
1769   gst_xvimagesink_manage_event_thread (xvimagesink);
1770
1771   return TRUE;
1772
1773 no_context:
1774   {
1775     gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1776         error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1777         error->message, __FILE__, GST_FUNCTION, __LINE__);
1778     return FALSE;
1779   }
1780 }
1781
1782 static void
1783 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1784 {
1785   GThread *thread;
1786   GstXvContext *context;
1787
1788   GST_OBJECT_LOCK (xvimagesink);
1789   xvimagesink->running = FALSE;
1790   /* grab thread and mark it as NULL */
1791   thread = xvimagesink->event_thread;
1792   xvimagesink->event_thread = NULL;
1793   GST_OBJECT_UNLOCK (xvimagesink);
1794
1795   /* Wait for our event thread to finish before we clean up our stuff. */
1796   if (thread)
1797     g_thread_join (thread);
1798
1799   if (xvimagesink->cur_image) {
1800     gst_buffer_unref (xvimagesink->cur_image);
1801     xvimagesink->cur_image = NULL;
1802   }
1803
1804   g_mutex_lock (&xvimagesink->flow_lock);
1805
1806   if (xvimagesink->pool) {
1807     gst_object_unref (xvimagesink->pool);
1808     xvimagesink->pool = NULL;
1809   }
1810
1811   if (xvimagesink->xwindow) {
1812     gst_xwindow_clear (xvimagesink->xwindow);
1813     gst_xwindow_destroy (xvimagesink->xwindow);
1814     xvimagesink->xwindow = NULL;
1815   }
1816   g_mutex_unlock (&xvimagesink->flow_lock);
1817
1818   if (xvimagesink->allocator) {
1819     gst_object_unref (xvimagesink->allocator);
1820     xvimagesink->allocator = NULL;
1821   }
1822
1823   GST_OBJECT_LOCK (xvimagesink);
1824   /* grab context and mark it as NULL */
1825   context = xvimagesink->context;
1826   xvimagesink->context = NULL;
1827   GST_OBJECT_UNLOCK (xvimagesink);
1828
1829   if (context)
1830     gst_xvcontext_unref (context);
1831 }
1832
1833 /* Finalize is called only once, dispose can be called multiple times.
1834  * We use mutexes and don't reset stuff to NULL here so let's register
1835  * as a finalize. */
1836 static void
1837 gst_xvimagesink_finalize (GObject * object)
1838 {
1839   GstXvImageSink *xvimagesink;
1840
1841   xvimagesink = GST_XVIMAGESINK (object);
1842
1843   gst_xvimagesink_close (xvimagesink);
1844
1845   gst_xvcontext_config_clear (&xvimagesink->config);
1846
1847   if (xvimagesink->par) {
1848     g_free (xvimagesink->par);
1849     xvimagesink->par = NULL;
1850   }
1851   g_mutex_clear (&xvimagesink->flow_lock);
1852   g_free (xvimagesink->media_title);
1853
1854   G_OBJECT_CLASS (parent_class)->finalize (object);
1855 }
1856
1857 static void
1858 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1859 {
1860   xvimagesink->config.display_name = NULL;
1861   xvimagesink->config.adaptor_nr = 0;
1862   xvimagesink->config.autopaint_colorkey = TRUE;
1863   xvimagesink->config.double_buffer = TRUE;
1864   /* on 16bit displays this becomes r,g,b = 1,2,3
1865    * on 24bit displays this becomes r,g,b = 8,8,16
1866    * as a port atom value */
1867   xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1868   xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1869   xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1870   xvimagesink->config.cb_changed = FALSE;
1871
1872   xvimagesink->context = NULL;
1873   xvimagesink->xwindow = NULL;
1874   xvimagesink->cur_image = NULL;
1875
1876   xvimagesink->fps_n = 0;
1877   xvimagesink->fps_d = 0;
1878   xvimagesink->video_width = 0;
1879   xvimagesink->video_height = 0;
1880
1881   g_mutex_init (&xvimagesink->flow_lock);
1882
1883   xvimagesink->pool = NULL;
1884
1885   xvimagesink->synchronous = FALSE;
1886   xvimagesink->running = FALSE;
1887   xvimagesink->keep_aspect = TRUE;
1888   xvimagesink->handle_events = TRUE;
1889   xvimagesink->par = NULL;
1890   xvimagesink->handle_expose = TRUE;
1891
1892   xvimagesink->draw_borders = TRUE;
1893 }
1894
1895 static void
1896 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1897 {
1898   GObjectClass *gobject_class;
1899   GstElementClass *gstelement_class;
1900   GstBaseSinkClass *gstbasesink_class;
1901   GstVideoSinkClass *videosink_class;
1902
1903   gobject_class = (GObjectClass *) klass;
1904   gstelement_class = (GstElementClass *) klass;
1905   gstbasesink_class = (GstBaseSinkClass *) klass;
1906   videosink_class = (GstVideoSinkClass *) klass;
1907
1908   parent_class = g_type_class_peek_parent (klass);
1909
1910   gobject_class->set_property = gst_xvimagesink_set_property;
1911   gobject_class->get_property = gst_xvimagesink_get_property;
1912
1913   g_object_class_install_property (gobject_class, PROP_CONTRAST,
1914       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1915           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1916   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1917       g_param_spec_int ("brightness", "Brightness",
1918           "The brightness of the video", -1000, 1000, 0,
1919           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1920   g_object_class_install_property (gobject_class, PROP_HUE,
1921       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1922           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1923   g_object_class_install_property (gobject_class, PROP_SATURATION,
1924       g_param_spec_int ("saturation", "Saturation",
1925           "The saturation of the video", -1000, 1000, 0,
1926           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1928       g_param_spec_string ("display", "Display", "X Display name", NULL,
1929           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1931       g_param_spec_boolean ("synchronous", "Synchronous",
1932           "When enabled, runs the X display in synchronous mode. "
1933           "(unrelated to A/V sync, used only for debugging)", FALSE,
1934           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1935   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1936       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1937           "The pixel aspect ratio of the device", "1/1",
1938           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1939   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1940       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1941           "When enabled, scaling will respect original aspect ratio", TRUE,
1942           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1943   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1944       g_param_spec_boolean ("handle-events", "Handle XEvents",
1945           "When enabled, XEvents will be selected and handled", TRUE,
1946           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1947   g_object_class_install_property (gobject_class, PROP_DEVICE,
1948       g_param_spec_string ("device", "Adaptor number",
1949           "The number of the video adaptor", "0",
1950           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1951   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1952       g_param_spec_string ("device-name", "Adaptor name",
1953           "The name of the video adaptor", NULL,
1954           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1955   /**
1956    * GstXvImageSink:handle-expose
1957    *
1958    * When enabled, the current frame will always be drawn in response to X
1959    * Expose.
1960    */
1961   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1962       g_param_spec_boolean ("handle-expose", "Handle expose",
1963           "When enabled, "
1964           "the current frame will always be drawn in response to X Expose "
1965           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1966
1967   /**
1968    * GstXvImageSink:double-buffer
1969    *
1970    * Whether to double-buffer the output.
1971    */
1972   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1973       g_param_spec_boolean ("double-buffer", "Double-buffer",
1974           "Whether to double-buffer the output", TRUE,
1975           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1976   /**
1977    * GstXvImageSink:autopaint-colorkey
1978    *
1979    * Whether to autofill overlay with colorkey
1980    */
1981   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1982       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1983           "Whether to autofill overlay with colorkey", TRUE,
1984           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1985   /**
1986    * GstXvImageSink:colorkey
1987    *
1988    * Color to use for the overlay mask.
1989    */
1990   g_object_class_install_property (gobject_class, PROP_COLORKEY,
1991       g_param_spec_int ("colorkey", "Colorkey",
1992           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1993           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1994
1995   /**
1996    * GstXvImageSink:draw-borders
1997    *
1998    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1999    * unused parts of the video area.
2000    */
2001   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2002       g_param_spec_boolean ("draw-borders", "Draw Borders",
2003           "Draw black borders to fill unused area in force-aspect-ratio mode",
2004           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2005
2006   /**
2007    * GstXvImageSink:window-width
2008    *
2009    * Actual width of the video window.
2010    */
2011   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2012       g_param_spec_uint64 ("window-width", "window-width",
2013           "Width of the window", 0, G_MAXUINT64, 0,
2014           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2015
2016   /**
2017    * GstXvImageSink:window-height
2018    *
2019    * Actual height of the video window.
2020    */
2021   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2022       g_param_spec_uint64 ("window-height", "window-height",
2023           "Height of the window", 0, G_MAXUINT64, 0,
2024           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2025
2026   gobject_class->finalize = gst_xvimagesink_finalize;
2027
2028   gst_element_class_set_static_metadata (gstelement_class,
2029       "Video sink", "Sink/Video",
2030       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2031
2032   gst_element_class_add_pad_template (gstelement_class,
2033       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2034
2035   gstelement_class->change_state =
2036       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2037
2038   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2039   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2040   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2041   gstbasesink_class->propose_allocation =
2042       GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2043   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2044
2045   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
2046 }