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