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