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