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