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