xvimagesink: use xvcontext for allocation
[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, 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   size = info.size;
697
698   /* get aspect ratio from caps if it's present, and
699    * convert video width and height to a display width and height
700    * using wd / hd = wv / hv * PARv / PARd */
701
702   /* get video's PAR */
703   video_par_n = info.par_n;
704   video_par_d = info.par_d;
705
706   /* get display's PAR */
707   if (xvimagesink->par) {
708     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
709     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
710   } else {
711     display_par_n = 1;
712     display_par_d = 1;
713   }
714
715   if (!gst_video_calculate_display_ratio (&num, &den, info.width,
716           info.height, video_par_n, video_par_d, display_par_n, display_par_d))
717     goto no_disp_ratio;
718
719   GST_DEBUG_OBJECT (xvimagesink,
720       "video width/height: %dx%d, calculated display ratio: %d/%d",
721       info.width, info.height, num, den);
722
723   /* now find a width x height that respects this display ratio.
724    * prefer those that have one of w/h the same as the incoming video
725    * using wd / hd = num / den */
726
727   /* start with same height, because of interlaced video */
728   /* check hd / den is an integer scale factor, and scale wd with the PAR */
729   if (info.height % den == 0) {
730     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
731     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
732         gst_util_uint64_scale_int (info.height, num, den);
733     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
734   } else if (info.width % num == 0) {
735     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
736     GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
737     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
738         gst_util_uint64_scale_int (info.width, den, num);
739   } else {
740     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
741     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
742         gst_util_uint64_scale_int (info.height, num, den);
743     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
744   }
745   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
746       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
747
748   /* Notify application to set xwindow id now */
749   g_mutex_lock (&xvimagesink->flow_lock);
750   if (!xvimagesink->xwindow) {
751     g_mutex_unlock (&xvimagesink->flow_lock);
752     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
753   } else {
754     g_mutex_unlock (&xvimagesink->flow_lock);
755   }
756
757   /* Creating our window and our image with the display size in pixels */
758   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
759       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
760     goto no_display_size;
761
762   g_mutex_lock (&xvimagesink->flow_lock);
763   if (!xvimagesink->xwindow) {
764     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
765         GST_VIDEO_SINK_WIDTH (xvimagesink),
766         GST_VIDEO_SINK_HEIGHT (xvimagesink));
767   }
768
769   xvimagesink->info = info;
770
771   /* After a resize, we want to redraw the borders in case the new frame size
772    * doesn't cover the same area */
773   xvimagesink->redraw_border = TRUE;
774
775   /* create a new pool for the new configuration */
776   newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
777
778   structure = gst_buffer_pool_get_config (newpool);
779   gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
780   if (!gst_buffer_pool_set_config (newpool, structure))
781     goto config_failed;
782
783   oldpool = xvimagesink->pool;
784   /* we don't activate the pool yet, this will be done by downstream after it
785    * has configured the pool. If downstream does not want our pool we will
786    * activate it when we render into it */
787   xvimagesink->pool = newpool;
788   g_mutex_unlock (&xvimagesink->flow_lock);
789
790   /* unref the old sink */
791   if (oldpool) {
792     /* we don't deactivate, some elements might still be using it, it will
793      * be deactivated when the last ref is gone */
794     gst_object_unref (oldpool);
795   }
796
797   return TRUE;
798
799   /* ERRORS */
800 incompatible_caps:
801   {
802     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
803     return FALSE;
804   }
805 invalid_format:
806   {
807     GST_DEBUG_OBJECT (xvimagesink,
808         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
809     return FALSE;
810   }
811 no_disp_ratio:
812   {
813     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
814         ("Error calculating the output display ratio of the video."));
815     return FALSE;
816   }
817 no_display_size:
818   {
819     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
820         ("Error calculating the output display ratio of the video."));
821     return FALSE;
822   }
823 config_failed:
824   {
825     GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
826     g_mutex_unlock (&xvimagesink->flow_lock);
827     return FALSE;
828   }
829 }
830
831 static GstStateChangeReturn
832 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
833 {
834   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
835   GstXvImageSink *xvimagesink;
836
837   xvimagesink = GST_XVIMAGESINK (element);
838
839   switch (transition) {
840     case GST_STATE_CHANGE_NULL_TO_READY:
841       if (!gst_xvimagesink_open (xvimagesink))
842         goto error;
843       break;
844     case GST_STATE_CHANGE_READY_TO_PAUSED:
845       break;
846     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
847       break;
848     case GST_STATE_CHANGE_PAUSED_TO_READY:
849       break;
850     default:
851       break;
852   }
853
854   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
855
856   switch (transition) {
857     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
858       break;
859     case GST_STATE_CHANGE_PAUSED_TO_READY:
860       xvimagesink->fps_n = 0;
861       xvimagesink->fps_d = 1;
862       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
863       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
864       g_mutex_lock (&xvimagesink->flow_lock);
865       if (xvimagesink->pool)
866         gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
867       g_mutex_unlock (&xvimagesink->flow_lock);
868       break;
869     case GST_STATE_CHANGE_READY_TO_NULL:
870       gst_xvimagesink_close (xvimagesink);
871       break;
872     default:
873       break;
874   }
875   return ret;
876
877 error:
878   {
879     return GST_STATE_CHANGE_FAILURE;
880   }
881 }
882
883 static void
884 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
885     GstClockTime * start, GstClockTime * end)
886 {
887   GstXvImageSink *xvimagesink;
888
889   xvimagesink = GST_XVIMAGESINK (bsink);
890
891   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
892     *start = GST_BUFFER_TIMESTAMP (buf);
893     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
894       *end = *start + GST_BUFFER_DURATION (buf);
895     } else {
896       if (xvimagesink->fps_n > 0) {
897         *end = *start +
898             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
899             xvimagesink->fps_n);
900       }
901     }
902   }
903 }
904
905 static GstFlowReturn
906 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
907 {
908   GstFlowReturn res;
909   GstXvImageSink *xvimagesink;
910   GstBuffer *to_put;
911   GstMemory *mem;
912
913   xvimagesink = GST_XVIMAGESINK (vsink);
914
915   if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
916       && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
917     /* If this buffer has been allocated using our buffer management we simply
918        put the ximage which is in the PRIVATE pointer */
919     GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
920         buf);
921     to_put = buf;
922     res = GST_FLOW_OK;
923   } else {
924     GstVideoFrame src, dest;
925     GstBufferPoolAcquireParams params = { 0, };
926
927     /* Else we have to copy the data into our private image, */
928     /* if we have one... */
929     GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
930
931     /* we should have a pool, configured in setcaps */
932     if (xvimagesink->pool == NULL)
933       goto no_pool;
934
935     if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
936       goto activate_failed;
937
938     /* take a buffer from our pool, if there is no buffer in the pool something
939      * is seriously wrong, waiting for the pool here might deadlock when we try
940      * to go to PAUSED because we never flush the pool then. */
941     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
942     res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, &params);
943     if (res != GST_FLOW_OK)
944       goto no_buffer;
945
946     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
947         "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
948
949     if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
950       goto invalid_buffer;
951
952     if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
953       gst_video_frame_unmap (&src);
954       goto invalid_buffer;
955     }
956
957     gst_video_frame_copy (&dest, &src);
958
959     gst_video_frame_unmap (&dest);
960     gst_video_frame_unmap (&src);
961   }
962
963   if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
964     goto no_window;
965
966 done:
967   if (to_put != buf)
968     gst_buffer_unref (to_put);
969
970   return res;
971
972   /* ERRORS */
973 no_pool:
974   {
975     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
976         ("Internal error: can't allocate images"),
977         ("We don't have a bufferpool negotiated"));
978     return GST_FLOW_ERROR;
979   }
980 no_buffer:
981   {
982     /* No image available. That's very bad ! */
983     GST_WARNING_OBJECT (xvimagesink, "could not create image");
984     return GST_FLOW_OK;
985   }
986 invalid_buffer:
987   {
988     /* No Window available to put our image into */
989     GST_WARNING_OBJECT (xvimagesink, "could not map image");
990     res = GST_FLOW_OK;
991     goto done;
992   }
993 no_window:
994   {
995     /* No Window available to put our image into */
996     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
997     res = GST_FLOW_ERROR;
998     goto done;
999   }
1000 activate_failed:
1001   {
1002     GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1003     res = GST_FLOW_ERROR;
1004     goto done;
1005   }
1006 }
1007
1008 static gboolean
1009 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1010 {
1011   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1012
1013   switch (GST_EVENT_TYPE (event)) {
1014     case GST_EVENT_TAG:{
1015       GstTagList *l;
1016       gchar *title = NULL;
1017
1018       gst_event_parse_tag (event, &l);
1019       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1020
1021       if (title) {
1022         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1023         gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1024             title);
1025
1026         g_free (title);
1027       }
1028       break;
1029     }
1030     default:
1031       break;
1032   }
1033   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1034 }
1035
1036 static gboolean
1037 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1038 {
1039   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1040   GstBufferPool *pool;
1041   GstStructure *config;
1042   GstCaps *caps;
1043   guint size;
1044   gboolean need_pool;
1045
1046   gst_query_parse_allocation (query, &caps, &need_pool);
1047
1048   if (caps == NULL)
1049     goto no_caps;
1050
1051   g_mutex_lock (&xvimagesink->flow_lock);
1052   if ((pool = xvimagesink->pool))
1053     gst_object_ref (pool);
1054   g_mutex_unlock (&xvimagesink->flow_lock);
1055
1056   if (pool != NULL) {
1057     GstCaps *pcaps;
1058
1059     /* we had a pool, check caps */
1060     GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1061     config = gst_buffer_pool_get_config (pool);
1062     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1063
1064     if (!gst_caps_is_equal (caps, pcaps)) {
1065       GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1066       /* different caps, we can't use this pool */
1067       gst_object_unref (pool);
1068       pool = NULL;
1069     }
1070     gst_structure_free (config);
1071   }
1072   if (pool == NULL && need_pool) {
1073     GstVideoInfo info;
1074
1075     if (!gst_video_info_from_caps (&info, caps))
1076       goto invalid_caps;
1077
1078     GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1079     pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1080
1081     /* the normal size of a frame */
1082     size = info.size;
1083
1084     config = gst_buffer_pool_get_config (pool);
1085     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1086     if (!gst_buffer_pool_set_config (pool, config))
1087       goto config_failed;
1088   }
1089   if (pool) {
1090     /* we need at least 2 buffer because we hold on to the last one */
1091     gst_query_add_allocation_pool (query, pool, size, 2, 0);
1092     gst_object_unref (pool);
1093   }
1094
1095   /* we also support various metadata */
1096   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1097   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1098
1099   return TRUE;
1100
1101   /* ERRORS */
1102 no_caps:
1103   {
1104     GST_DEBUG_OBJECT (bsink, "no caps specified");
1105     return FALSE;
1106   }
1107 invalid_caps:
1108   {
1109     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1110     return FALSE;
1111   }
1112 config_failed:
1113   {
1114     GST_DEBUG_OBJECT (bsink, "failed setting config");
1115     gst_object_unref (pool);
1116     return FALSE;
1117   }
1118 }
1119
1120 /* Interfaces stuff */
1121 static void
1122 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1123     GstStructure * structure)
1124 {
1125   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1126   GstPad *peer;
1127
1128   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1129     GstEvent *event;
1130     GstVideoRectangle src, dst, result;
1131     gdouble x, y, xscale = 1.0, yscale = 1.0;
1132     GstXWindow *xwindow;
1133
1134     event = gst_event_new_navigation (structure);
1135
1136     /* We take the flow_lock while we look at the window */
1137     g_mutex_lock (&xvimagesink->flow_lock);
1138
1139     if (!(xwindow = xvimagesink->xwindow)) {
1140       g_mutex_unlock (&xvimagesink->flow_lock);
1141       return;
1142     }
1143
1144     if (xvimagesink->keep_aspect) {
1145       /* We get the frame position using the calculated geometry from _setcaps
1146          that respect pixel aspect ratios */
1147       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1148       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1149       dst.w = xwindow->render_rect.w;
1150       dst.h = xwindow->render_rect.h;
1151
1152       gst_video_sink_center_rect (src, dst, &result, TRUE);
1153       result.x += xwindow->render_rect.x;
1154       result.y += xwindow->render_rect.y;
1155     } else {
1156       memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1157     }
1158
1159     g_mutex_unlock (&xvimagesink->flow_lock);
1160
1161     /* We calculate scaling using the original video frames geometry to include
1162        pixel aspect ratio scaling. */
1163     xscale = (gdouble) xvimagesink->video_width / result.w;
1164     yscale = (gdouble) xvimagesink->video_height / result.h;
1165
1166     /* Converting pointer coordinates to the non scaled geometry */
1167     if (gst_structure_get_double (structure, "pointer_x", &x)) {
1168       x = MIN (x, result.x + result.w);
1169       x = MAX (x - result.x, 0);
1170       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1171           (gdouble) x * xscale, NULL);
1172     }
1173     if (gst_structure_get_double (structure, "pointer_y", &y)) {
1174       y = MIN (y, result.y + result.h);
1175       y = MAX (y - result.y, 0);
1176       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1177           (gdouble) y * yscale, NULL);
1178     }
1179
1180     gst_pad_send_event (peer, event);
1181     gst_object_unref (peer);
1182   }
1183 }
1184
1185 static void
1186 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1187 {
1188   iface->send_event = gst_xvimagesink_navigation_send_event;
1189 }
1190
1191 static void
1192 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1193 {
1194   XID xwindow_id = id;
1195   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1196   GstXWindow *xwindow = NULL;
1197   GstXvContext *context;
1198
1199   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1200
1201   g_mutex_lock (&xvimagesink->flow_lock);
1202
1203   /* If we already use that window return */
1204   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1205     g_mutex_unlock (&xvimagesink->flow_lock);
1206     return;
1207   }
1208
1209   /* If the element has not initialized the X11 context try to do so */
1210   if (!xvimagesink->context &&
1211       !(xvimagesink->context =
1212           gst_xvcontext_new (&xvimagesink->config, NULL))) {
1213     g_mutex_unlock (&xvimagesink->flow_lock);
1214     /* we have thrown a GST_ELEMENT_ERROR now */
1215     return;
1216   }
1217
1218   context = xvimagesink->context;
1219
1220   gst_xvimagesink_update_colorbalance (xvimagesink);
1221
1222   /* If a window is there already we destroy it */
1223   if (xvimagesink->xwindow) {
1224     gst_xwindow_destroy (xvimagesink->xwindow);
1225     xvimagesink->xwindow = NULL;
1226   }
1227
1228   /* If the xid is 0 we go back to an internal window */
1229   if (xwindow_id == 0) {
1230     /* If no width/height caps nego did not happen window will be created
1231        during caps nego then */
1232     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1233         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1234       xwindow =
1235           gst_xvimagesink_xwindow_new (xvimagesink,
1236           GST_VIDEO_SINK_WIDTH (xvimagesink),
1237           GST_VIDEO_SINK_HEIGHT (xvimagesink));
1238     }
1239   } else {
1240     xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1241     gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1242   }
1243
1244   if (xwindow)
1245     xvimagesink->xwindow = xwindow;
1246
1247   g_mutex_unlock (&xvimagesink->flow_lock);
1248 }
1249
1250 static void
1251 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1252 {
1253   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1254
1255   GST_DEBUG ("doing expose");
1256   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1257   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1258 }
1259
1260 static void
1261 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1262     gboolean handle_events)
1263 {
1264   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1265
1266   g_mutex_lock (&xvimagesink->flow_lock);
1267   xvimagesink->handle_events = handle_events;
1268   if (G_LIKELY (xvimagesink->xwindow))
1269     gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1270   g_mutex_unlock (&xvimagesink->flow_lock);
1271 }
1272
1273 static void
1274 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1275     gint width, gint height)
1276 {
1277   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1278
1279   g_mutex_lock (&xvimagesink->flow_lock);
1280   if (G_LIKELY (xvimagesink->xwindow))
1281     gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1282         height);
1283   g_mutex_unlock (&xvimagesink->flow_lock);
1284 }
1285
1286 static void
1287 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1288 {
1289   iface->set_window_handle = gst_xvimagesink_set_window_handle;
1290   iface->expose = gst_xvimagesink_expose;
1291   iface->handle_events = gst_xvimagesink_set_event_handling;
1292   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1293 }
1294
1295 static const GList *
1296 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1297 {
1298   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1299
1300   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1301
1302   if (xvimagesink->context)
1303     return xvimagesink->context->channels_list;
1304   else
1305     return NULL;
1306 }
1307
1308 static void
1309 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1310     GstColorBalanceChannel * channel, gint value)
1311 {
1312   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1313
1314   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1315   g_return_if_fail (channel->label != NULL);
1316
1317   xvimagesink->config.cb_changed = TRUE;
1318
1319   /* Normalize val to [-1000, 1000] */
1320   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1321       (double) (channel->max_value - channel->min_value));
1322
1323   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1324     xvimagesink->config.hue = value;
1325   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1326     xvimagesink->config.saturation = value;
1327   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1328     xvimagesink->config.contrast = value;
1329   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1330     xvimagesink->config.brightness = value;
1331   } else {
1332     g_warning ("got an unknown channel %s", channel->label);
1333     return;
1334   }
1335
1336   gst_xvimagesink_update_colorbalance (xvimagesink);
1337 }
1338
1339 static gint
1340 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1341     GstColorBalanceChannel * channel)
1342 {
1343   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1344   gint value = 0;
1345
1346   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1347   g_return_val_if_fail (channel->label != NULL, 0);
1348
1349   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1350     value = xvimagesink->config.hue;
1351   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1352     value = xvimagesink->config.saturation;
1353   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1354     value = xvimagesink->config.contrast;
1355   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1356     value = xvimagesink->config.brightness;
1357   } else {
1358     g_warning ("got an unknown channel %s", channel->label);
1359   }
1360
1361   /* Normalize val to [channel->min_value, channel->max_value] */
1362   value = channel->min_value + (channel->max_value - channel->min_value) *
1363       (value + 1000) / 2000;
1364
1365   return value;
1366 }
1367
1368 static GstColorBalanceType
1369 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1370 {
1371   return GST_COLOR_BALANCE_HARDWARE;
1372 }
1373
1374 static void
1375 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1376 {
1377   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1378   iface->set_value = gst_xvimagesink_colorbalance_set_value;
1379   iface->get_value = gst_xvimagesink_colorbalance_get_value;
1380   iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1381 }
1382
1383 #if 0
1384 static const GList *
1385 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1386 {
1387   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1388   static GList *list = NULL;
1389
1390   if (!list) {
1391     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1392     list =
1393         g_list_append (list, g_object_class_find_property (klass,
1394             "autopaint-colorkey"));
1395     list =
1396         g_list_append (list, g_object_class_find_property (klass,
1397             "double-buffer"));
1398     list =
1399         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1400   }
1401
1402   return list;
1403 }
1404
1405 static void
1406 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1407     guint prop_id, const GParamSpec * pspec)
1408 {
1409   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1410
1411   switch (prop_id) {
1412     case PROP_DEVICE:
1413     case PROP_AUTOPAINT_COLORKEY:
1414     case PROP_DOUBLE_BUFFER:
1415     case PROP_COLORKEY:
1416       GST_DEBUG_OBJECT (xvimagesink,
1417           "probing device list and get capabilities");
1418       if (!xvimagesink->context) {
1419         GST_DEBUG_OBJECT (xvimagesink, "generating context");
1420         xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1421       }
1422       break;
1423     default:
1424       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1425       break;
1426   }
1427 }
1428
1429 static gboolean
1430 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1431     guint prop_id, const GParamSpec * pspec)
1432 {
1433   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1434   gboolean ret = FALSE;
1435
1436   switch (prop_id) {
1437     case PROP_DEVICE:
1438     case PROP_AUTOPAINT_COLORKEY:
1439     case PROP_DOUBLE_BUFFER:
1440     case PROP_COLORKEY:
1441       if (xvimagesink->context != NULL) {
1442         ret = FALSE;
1443       } else {
1444         ret = TRUE;
1445       }
1446       break;
1447     default:
1448       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1449       break;
1450   }
1451
1452   return ret;
1453 }
1454
1455 static GValueArray *
1456 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1457     guint prop_id, const GParamSpec * pspec)
1458 {
1459   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1460   GValueArray *array = NULL;
1461
1462   if (G_UNLIKELY (!xvimagesink->context)) {
1463     GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1464         "get values");
1465     goto beach;
1466   }
1467
1468   switch (prop_id) {
1469     case PROP_DEVICE:
1470     {
1471       guint i;
1472       GValue value = { 0 };
1473
1474       array = g_value_array_new (xvimagesink->context->nb_adaptors);
1475       g_value_init (&value, G_TYPE_STRING);
1476
1477       for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1478         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1479
1480         g_value_set_string (&value, adaptor_id_s);
1481         g_value_array_append (array, &value);
1482         g_free (adaptor_id_s);
1483       }
1484       g_value_unset (&value);
1485       break;
1486     }
1487     case PROP_AUTOPAINT_COLORKEY:
1488       if (xvimagesink->have_autopaint_colorkey) {
1489         GValue value = { 0 };
1490
1491         array = g_value_array_new (2);
1492         g_value_init (&value, G_TYPE_BOOLEAN);
1493         g_value_set_boolean (&value, FALSE);
1494         g_value_array_append (array, &value);
1495         g_value_set_boolean (&value, TRUE);
1496         g_value_array_append (array, &value);
1497         g_value_unset (&value);
1498       }
1499       break;
1500     case PROP_DOUBLE_BUFFER:
1501       if (xvimagesink->have_double_buffer) {
1502         GValue value = { 0 };
1503
1504         array = g_value_array_new (2);
1505         g_value_init (&value, G_TYPE_BOOLEAN);
1506         g_value_set_boolean (&value, FALSE);
1507         g_value_array_append (array, &value);
1508         g_value_set_boolean (&value, TRUE);
1509         g_value_array_append (array, &value);
1510         g_value_unset (&value);
1511       }
1512       break;
1513     case PROP_COLORKEY:
1514       if (xvimagesink->have_colorkey) {
1515         GValue value = { 0 };
1516
1517         array = g_value_array_new (1);
1518         g_value_init (&value, GST_TYPE_INT_RANGE);
1519         gst_value_set_int_range (&value, 0, 0xffffff);
1520         g_value_array_append (array, &value);
1521         g_value_unset (&value);
1522       }
1523       break;
1524     default:
1525       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1526       break;
1527   }
1528
1529 beach:
1530   return array;
1531 }
1532
1533 static void
1534 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1535     iface)
1536 {
1537   iface->get_properties = gst_xvimagesink_probe_get_properties;
1538   iface->probe_property = gst_xvimagesink_probe_probe_property;
1539   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1540   iface->get_values = gst_xvimagesink_probe_get_values;
1541 }
1542 #endif
1543
1544 /* =========================================== */
1545 /*                                             */
1546 /*              Init & Class init              */
1547 /*                                             */
1548 /* =========================================== */
1549
1550 static void
1551 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1552     const GValue * value, GParamSpec * pspec)
1553 {
1554   GstXvImageSink *xvimagesink;
1555
1556   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1557
1558   xvimagesink = GST_XVIMAGESINK (object);
1559
1560   switch (prop_id) {
1561     case PROP_HUE:
1562       xvimagesink->config.hue = g_value_get_int (value);
1563       xvimagesink->config.cb_changed = TRUE;
1564       gst_xvimagesink_update_colorbalance (xvimagesink);
1565       break;
1566     case PROP_CONTRAST:
1567       xvimagesink->config.contrast = g_value_get_int (value);
1568       xvimagesink->config.cb_changed = TRUE;
1569       gst_xvimagesink_update_colorbalance (xvimagesink);
1570       break;
1571     case PROP_BRIGHTNESS:
1572       xvimagesink->config.brightness = g_value_get_int (value);
1573       xvimagesink->config.cb_changed = TRUE;
1574       gst_xvimagesink_update_colorbalance (xvimagesink);
1575       break;
1576     case PROP_SATURATION:
1577       xvimagesink->config.saturation = g_value_get_int (value);
1578       xvimagesink->config.cb_changed = TRUE;
1579       gst_xvimagesink_update_colorbalance (xvimagesink);
1580       break;
1581     case PROP_DISPLAY:
1582       g_free (xvimagesink->config.display_name);
1583       xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1584       break;
1585     case PROP_SYNCHRONOUS:
1586       xvimagesink->synchronous = g_value_get_boolean (value);
1587       if (xvimagesink->context) {
1588         gst_xvcontext_set_synchronous (xvimagesink->context,
1589             xvimagesink->synchronous);
1590       }
1591       break;
1592     case PROP_PIXEL_ASPECT_RATIO:
1593       g_free (xvimagesink->par);
1594       xvimagesink->par = g_new0 (GValue, 1);
1595       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1596       if (!g_value_transform (value, xvimagesink->par)) {
1597         g_warning ("Could not transform string to aspect ratio");
1598         gst_value_set_fraction (xvimagesink->par, 1, 1);
1599       }
1600       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1601           gst_value_get_fraction_numerator (xvimagesink->par),
1602           gst_value_get_fraction_denominator (xvimagesink->par));
1603       break;
1604     case PROP_FORCE_ASPECT_RATIO:
1605       xvimagesink->keep_aspect = g_value_get_boolean (value);
1606       break;
1607     case PROP_HANDLE_EVENTS:
1608       gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1609           g_value_get_boolean (value));
1610       gst_xvimagesink_manage_event_thread (xvimagesink);
1611       break;
1612     case PROP_DEVICE:
1613       xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1614       break;
1615     case PROP_HANDLE_EXPOSE:
1616       xvimagesink->handle_expose = g_value_get_boolean (value);
1617       gst_xvimagesink_manage_event_thread (xvimagesink);
1618       break;
1619     case PROP_DOUBLE_BUFFER:
1620       xvimagesink->double_buffer = g_value_get_boolean (value);
1621       break;
1622     case PROP_AUTOPAINT_COLORKEY:
1623       xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1624       break;
1625     case PROP_COLORKEY:
1626       xvimagesink->config.colorkey = g_value_get_int (value);
1627       break;
1628     case PROP_DRAW_BORDERS:
1629       xvimagesink->draw_borders = g_value_get_boolean (value);
1630       break;
1631     default:
1632       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1633       break;
1634   }
1635 }
1636
1637 static void
1638 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1639     GValue * value, GParamSpec * pspec)
1640 {
1641   GstXvImageSink *xvimagesink;
1642
1643   g_return_if_fail (GST_IS_XVIMAGESINK (object));
1644
1645   xvimagesink = GST_XVIMAGESINK (object);
1646
1647   switch (prop_id) {
1648     case PROP_HUE:
1649       g_value_set_int (value, xvimagesink->config.hue);
1650       break;
1651     case PROP_CONTRAST:
1652       g_value_set_int (value, xvimagesink->config.contrast);
1653       break;
1654     case PROP_BRIGHTNESS:
1655       g_value_set_int (value, xvimagesink->config.brightness);
1656       break;
1657     case PROP_SATURATION:
1658       g_value_set_int (value, xvimagesink->config.saturation);
1659       break;
1660     case PROP_DISPLAY:
1661       g_value_set_string (value, xvimagesink->config.display_name);
1662       break;
1663     case PROP_SYNCHRONOUS:
1664       g_value_set_boolean (value, xvimagesink->synchronous);
1665       break;
1666     case PROP_PIXEL_ASPECT_RATIO:
1667       if (xvimagesink->par)
1668         g_value_transform (xvimagesink->par, value);
1669       break;
1670     case PROP_FORCE_ASPECT_RATIO:
1671       g_value_set_boolean (value, xvimagesink->keep_aspect);
1672       break;
1673     case PROP_HANDLE_EVENTS:
1674       g_value_set_boolean (value, xvimagesink->handle_events);
1675       break;
1676     case PROP_DEVICE:
1677     {
1678       char *adaptor_nr_s =
1679           g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1680
1681       g_value_set_string (value, adaptor_nr_s);
1682       g_free (adaptor_nr_s);
1683       break;
1684     }
1685     case PROP_DEVICE_NAME:
1686       if (xvimagesink->context && xvimagesink->context->adaptors) {
1687         g_value_set_string (value,
1688             xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1689       } else {
1690         g_value_set_string (value, NULL);
1691       }
1692       break;
1693     case PROP_HANDLE_EXPOSE:
1694       g_value_set_boolean (value, xvimagesink->handle_expose);
1695       break;
1696     case PROP_DOUBLE_BUFFER:
1697       g_value_set_boolean (value, xvimagesink->double_buffer);
1698       break;
1699     case PROP_AUTOPAINT_COLORKEY:
1700       g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1701       break;
1702     case PROP_COLORKEY:
1703       g_value_set_int (value, xvimagesink->config.colorkey);
1704       break;
1705     case PROP_DRAW_BORDERS:
1706       g_value_set_boolean (value, xvimagesink->draw_borders);
1707       break;
1708     case PROP_WINDOW_WIDTH:
1709       if (xvimagesink->xwindow)
1710         g_value_set_uint64 (value, xvimagesink->xwindow->width);
1711       else
1712         g_value_set_uint64 (value, 0);
1713       break;
1714     case PROP_WINDOW_HEIGHT:
1715       if (xvimagesink->xwindow)
1716         g_value_set_uint64 (value, xvimagesink->xwindow->height);
1717       else
1718         g_value_set_uint64 (value, 0);
1719       break;
1720     default:
1721       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1722       break;
1723   }
1724 }
1725
1726 static gboolean
1727 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1728 {
1729   GstXvContext *context;
1730   GError *error = NULL;
1731
1732   /* Initializing the XvContext */
1733   if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1734     goto no_context;
1735
1736   GST_OBJECT_LOCK (xvimagesink);
1737   xvimagesink->context = context;
1738   /* make an allocator for this context */
1739   xvimagesink->allocator = gst_xvimage_allocator_new (context);
1740   GST_OBJECT_UNLOCK (xvimagesink);
1741
1742   /* update object's par with calculated one if not set yet */
1743   if (!xvimagesink->par) {
1744     xvimagesink->par = g_new0 (GValue, 1);
1745     gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1746     GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1747   }
1748   /* call XSynchronize with the current value of synchronous */
1749   gst_xvcontext_set_synchronous (xvimagesink->context,
1750       xvimagesink->synchronous);
1751   gst_xvimagesink_update_colorbalance (xvimagesink);
1752   gst_xvimagesink_manage_event_thread (xvimagesink);
1753
1754   return TRUE;
1755
1756 no_context:
1757   {
1758     gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1759         error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1760         error->message, __FILE__, GST_FUNCTION, __LINE__);
1761     return FALSE;
1762   }
1763 }
1764
1765 static void
1766 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1767 {
1768   GThread *thread;
1769   GstXvContext *context;
1770
1771   GST_OBJECT_LOCK (xvimagesink);
1772   xvimagesink->running = FALSE;
1773   /* grab thread and mark it as NULL */
1774   thread = xvimagesink->event_thread;
1775   xvimagesink->event_thread = NULL;
1776   GST_OBJECT_UNLOCK (xvimagesink);
1777
1778   /* Wait for our event thread to finish before we clean up our stuff. */
1779   if (thread)
1780     g_thread_join (thread);
1781
1782   if (xvimagesink->cur_image) {
1783     gst_buffer_unref (xvimagesink->cur_image);
1784     xvimagesink->cur_image = NULL;
1785   }
1786
1787   g_mutex_lock (&xvimagesink->flow_lock);
1788
1789   if (xvimagesink->pool) {
1790     gst_object_unref (xvimagesink->pool);
1791     xvimagesink->pool = NULL;
1792   }
1793
1794   if (xvimagesink->xwindow) {
1795     gst_xwindow_clear (xvimagesink->xwindow);
1796     gst_xwindow_destroy (xvimagesink->xwindow);
1797     xvimagesink->xwindow = NULL;
1798   }
1799   g_mutex_unlock (&xvimagesink->flow_lock);
1800
1801   if (xvimagesink->allocator) {
1802     gst_object_unref (xvimagesink->allocator);
1803     xvimagesink->allocator = NULL;
1804   }
1805
1806   GST_OBJECT_LOCK (xvimagesink);
1807   /* grab context and mark it as NULL */
1808   context = xvimagesink->context;
1809   xvimagesink->context = NULL;
1810   GST_OBJECT_UNLOCK (xvimagesink);
1811
1812   if (context)
1813     gst_xvcontext_unref (context);
1814 }
1815
1816 /* Finalize is called only once, dispose can be called multiple times.
1817  * We use mutexes and don't reset stuff to NULL here so let's register
1818  * as a finalize. */
1819 static void
1820 gst_xvimagesink_finalize (GObject * object)
1821 {
1822   GstXvImageSink *xvimagesink;
1823
1824   xvimagesink = GST_XVIMAGESINK (object);
1825
1826   gst_xvimagesink_close (xvimagesink);
1827
1828   gst_xvcontext_config_clear (&xvimagesink->config);
1829
1830   if (xvimagesink->par) {
1831     g_free (xvimagesink->par);
1832     xvimagesink->par = NULL;
1833   }
1834   g_mutex_clear (&xvimagesink->flow_lock);
1835   g_free (xvimagesink->media_title);
1836
1837   G_OBJECT_CLASS (parent_class)->finalize (object);
1838 }
1839
1840 static void
1841 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1842 {
1843   xvimagesink->config.display_name = NULL;
1844   xvimagesink->config.adaptor_nr = 0;
1845   xvimagesink->config.autopaint_colorkey = TRUE;
1846   xvimagesink->config.double_buffer = TRUE;
1847   /* on 16bit displays this becomes r,g,b = 1,2,3
1848    * on 24bit displays this becomes r,g,b = 8,8,16
1849    * as a port atom value */
1850   xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1851   xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1852   xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1853   xvimagesink->config.cb_changed = FALSE;
1854
1855   xvimagesink->context = NULL;
1856   xvimagesink->xwindow = NULL;
1857   xvimagesink->cur_image = NULL;
1858
1859   xvimagesink->fps_n = 0;
1860   xvimagesink->fps_d = 0;
1861   xvimagesink->video_width = 0;
1862   xvimagesink->video_height = 0;
1863
1864   g_mutex_init (&xvimagesink->flow_lock);
1865
1866   xvimagesink->pool = NULL;
1867
1868   xvimagesink->synchronous = FALSE;
1869   xvimagesink->running = FALSE;
1870   xvimagesink->keep_aspect = TRUE;
1871   xvimagesink->handle_events = TRUE;
1872   xvimagesink->par = NULL;
1873   xvimagesink->handle_expose = TRUE;
1874
1875   xvimagesink->draw_borders = TRUE;
1876 }
1877
1878 static void
1879 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1880 {
1881   GObjectClass *gobject_class;
1882   GstElementClass *gstelement_class;
1883   GstBaseSinkClass *gstbasesink_class;
1884   GstVideoSinkClass *videosink_class;
1885
1886   gobject_class = (GObjectClass *) klass;
1887   gstelement_class = (GstElementClass *) klass;
1888   gstbasesink_class = (GstBaseSinkClass *) klass;
1889   videosink_class = (GstVideoSinkClass *) klass;
1890
1891   parent_class = g_type_class_peek_parent (klass);
1892
1893   gobject_class->set_property = gst_xvimagesink_set_property;
1894   gobject_class->get_property = gst_xvimagesink_get_property;
1895
1896   g_object_class_install_property (gobject_class, PROP_CONTRAST,
1897       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1898           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1899   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1900       g_param_spec_int ("brightness", "Brightness",
1901           "The brightness of the video", -1000, 1000, 0,
1902           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1903   g_object_class_install_property (gobject_class, PROP_HUE,
1904       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1905           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1906   g_object_class_install_property (gobject_class, PROP_SATURATION,
1907       g_param_spec_int ("saturation", "Saturation",
1908           "The saturation of the video", -1000, 1000, 0,
1909           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1910   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1911       g_param_spec_string ("display", "Display", "X Display name", NULL,
1912           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1913   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1914       g_param_spec_boolean ("synchronous", "Synchronous",
1915           "When enabled, runs the X display in synchronous mode. "
1916           "(unrelated to A/V sync, used only for debugging)", FALSE,
1917           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1918   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1919       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1920           "The pixel aspect ratio of the device", "1/1",
1921           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1922   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1923       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1924           "When enabled, scaling will respect original aspect ratio", TRUE,
1925           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1926   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1927       g_param_spec_boolean ("handle-events", "Handle XEvents",
1928           "When enabled, XEvents will be selected and handled", TRUE,
1929           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930   g_object_class_install_property (gobject_class, PROP_DEVICE,
1931       g_param_spec_string ("device", "Adaptor number",
1932           "The number of the video adaptor", "0",
1933           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1934   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1935       g_param_spec_string ("device-name", "Adaptor name",
1936           "The name of the video adaptor", NULL,
1937           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1938   /**
1939    * GstXvImageSink:handle-expose
1940    *
1941    * When enabled, the current frame will always be drawn in response to X
1942    * Expose.
1943    *
1944    * Since: 0.10.14
1945    */
1946   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1947       g_param_spec_boolean ("handle-expose", "Handle expose",
1948           "When enabled, "
1949           "the current frame will always be drawn in response to X Expose "
1950           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1951
1952   /**
1953    * GstXvImageSink:double-buffer
1954    *
1955    * Whether to double-buffer the output.
1956    *
1957    * Since: 0.10.14
1958    */
1959   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1960       g_param_spec_boolean ("double-buffer", "Double-buffer",
1961           "Whether to double-buffer the output", TRUE,
1962           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1963   /**
1964    * GstXvImageSink:autopaint-colorkey
1965    *
1966    * Whether to autofill overlay with colorkey
1967    *
1968    * Since: 0.10.21
1969    */
1970   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1971       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1972           "Whether to autofill overlay with colorkey", TRUE,
1973           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1974   /**
1975    * GstXvImageSink:colorkey
1976    *
1977    * Color to use for the overlay mask.
1978    *
1979    * Since: 0.10.21
1980    */
1981   g_object_class_install_property (gobject_class, PROP_COLORKEY,
1982       g_param_spec_int ("colorkey", "Colorkey",
1983           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1984           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1985
1986   /**
1987    * GstXvImageSink:draw-borders
1988    *
1989    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1990    * unused parts of the video area.
1991    *
1992    * Since: 0.10.21
1993    */
1994   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1995       g_param_spec_boolean ("draw-borders", "Colorkey",
1996           "Draw black borders to fill unused area in force-aspect-ratio mode",
1997           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1998
1999   /**
2000    * GstXvImageSink:window-width
2001    *
2002    * Actual width of the video window.
2003    *
2004    * Since: 0.10.32
2005    */
2006   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2007       g_param_spec_uint64 ("window-width", "window-width",
2008           "Width of the window", 0, G_MAXUINT64, 0,
2009           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2010
2011   /**
2012    * GstXvImageSink:window-height
2013    *
2014    * Actual height of the video window.
2015    *
2016    * Since: 0.10.32
2017    */
2018   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2019       g_param_spec_uint64 ("window-height", "window-height",
2020           "Height of the window", 0, G_MAXUINT64, 0,
2021           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2022
2023   gobject_class->finalize = gst_xvimagesink_finalize;
2024
2025   gst_element_class_set_static_metadata (gstelement_class,
2026       "Video sink", "Sink/Video",
2027       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2028
2029   gst_element_class_add_pad_template (gstelement_class,
2030       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2031
2032   gstelement_class->change_state =
2033       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2034
2035   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2036   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2037   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2038   gstbasesink_class->propose_allocation =
2039       GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2040   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2041
2042   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
2043 }