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