x11: don't block in buffer acquire
[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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, 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 XOverlay interface and will then render video frames
29  * in this drawable. If no Window ID was provided by the application, the
30  * element will create its own internal window and render into it.
31  *
32  * <refsect2>
33  * <title>Scaling</title>
34  * <para>
35  * The XVideo extension, when it's available, handles hardware accelerated
36  * scaling of video frames. This means that the element will just accept
37  * incoming video frames no matter their geometry and will then put them to the
38  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39  * property it is possible to enforce scaling with a constant aspect ratio,
40  * which means drawing black borders around the video frame.
41  * </para>
42  * </refsect2>
43  * <refsect2>
44  * <title>Events</title>
45  * <para>
46  * XvImageSink creates a thread to handle events coming from the drawable. There
47  * are several kind of events that can be grouped in 2 big categories: input
48  * events and window state related events. Input events will be translated to
49  * navigation events and pushed upstream for other elements to react on them.
50  * This includes events such as pointer moves, key press/release, clicks etc...
51  * Other events are used to handle the drawable appearance even when the data
52  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53  * paused, it will receive expose events from the drawable and draw the latest
54  * frame with correct borders/aspect-ratio.
55  * </para>
56  * </refsect2>
57  * <refsect2>
58  * <title>Pixel aspect ratio</title>
59  * <para>
60  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61  * the display specified in the #GstXvImageSink:display property or the
62  * default display if nothing specified. Once this connection is open it will
63  * inspect the display configuration including the physical display geometry and
64  * then calculate the pixel aspect ratio. When receiving video frames with a
65  * different pixel aspect ratio, XvImageSink will use hardware scaling to
66  * display the video frames correctly on display's pixel aspect ratio.
67  * Sometimes the calculated pixel aspect ratio can be wrong, it is
68  * then possible to enforce a specific pixel aspect ratio using the
69  * #GstXvImageSink:pixel-aspect-ratio property.
70  * </para>
71  * </refsect2>
72  * <refsect2>
73  * <title>Examples</title>
74  * |[
75  * gst-launch -v videotestsrc ! xvimagesink
76  * ]| A pipeline to test hardware scaling.
77  * When the test video signal appears you can resize the window and see that
78  * video frames are scaled through hardware (no extra CPU cost).
79  * |[
80  * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81  * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82  * You can observe the borders drawn around the scaled image respecting aspect
83  * ratio.
84  * |[
85  * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86  * ]| A pipeline to test navigation events.
87  * While moving the mouse pointer over the test signal you will see a black box
88  * following the mouse pointer. If you press the mouse button somewhere on the
89  * video and release it somewhere else a green box will appear where you pressed
90  * the button and a red one where you released it. (The navigationtest element
91  * is part of gst-plugins-good.) You can observe here that even if the images
92  * are scaled through hardware the pointer coordinates are converted back to the
93  * original video frame geometry so that the box can be drawn to the correct
94  * position. This also handles borders correctly, limiting coordinates to the
95  * image area
96  * |[
97  * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99  * videotestsrc, in most cases the pixel aspect ratio of the display will be
100  * 1/1. This means that XvImageSink will have to do the scaling to convert
101  * incoming frames to a size that will match the display pixel aspect ratio
102  * (from 320x240 to 320x180 in this case). Note that you might have to escape
103  * some characters for your shell like '\(fraction\)'.
104  * |[
105  * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106  * ]| Demonstrates how to use the colorbalance interface.
107  * </refsect2>
108  */
109
110 /* for developers: there are two useful tools : xvinfo and xvattr */
111
112 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
113  * with newer GLib versions (>= 2.31.0) */
114 #define GLIB_DISABLE_DEPRECATION_WARNINGS
115
116 #ifdef HAVE_CONFIG_H
117 #include "config.h"
118 #endif
119
120 /* Our interfaces */
121 #include <gst/video/navigation.h>
122 #include <gst/video/videooverlay.h>
123 #include <gst/video/colorbalance.h>
124 /* Helper functions */
125 #include <gst/video/gstvideometa.h>
126
127 /* Object header */
128 #include "xvimagesink.h"
129
130 /* Debugging category */
131 #include <gst/gstinfo.h>
132
133 #include "gst/glib-compat-private.h"
134
135 /* for XkbKeycodeToKeysym */
136 #include <X11/XKBlib.h>
137
138 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
139 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
140 #define GST_CAT_DEFAULT gst_debug_xvimagesink
141
142 typedef struct
143 {
144   unsigned long flags;
145   unsigned long functions;
146   unsigned long decorations;
147   long input_mode;
148   unsigned long status;
149 }
150 MotifWmHints, MwmHints;
151
152 #define MWM_HINTS_DECORATIONS   (1L << 1)
153
154 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
155 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
156     xvimagesink);
157 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
158
159 /* Default template - initiated with class struct to allow gst-register to work
160    without X running */
161 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
162 GST_STATIC_PAD_TEMPLATE ("sink",
163     GST_PAD_SINK,
164     GST_PAD_ALWAYS,
165     GST_STATIC_CAPS ("video/x-raw, "
166         "framerate = (fraction) [ 0, MAX ], "
167         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
168     );
169
170 enum
171 {
172   PROP_0,
173   PROP_CONTRAST,
174   PROP_BRIGHTNESS,
175   PROP_HUE,
176   PROP_SATURATION,
177   PROP_DISPLAY,
178   PROP_SYNCHRONOUS,
179   PROP_PIXEL_ASPECT_RATIO,
180   PROP_FORCE_ASPECT_RATIO,
181   PROP_HANDLE_EVENTS,
182   PROP_DEVICE,
183   PROP_DEVICE_NAME,
184   PROP_HANDLE_EXPOSE,
185   PROP_DOUBLE_BUFFER,
186   PROP_AUTOPAINT_COLORKEY,
187   PROP_COLORKEY,
188   PROP_DRAW_BORDERS,
189   PROP_WINDOW_WIDTH,
190   PROP_WINDOW_HEIGHT
191 };
192
193 /* ============================================================= */
194 /*                                                               */
195 /*                       Public Methods                          */
196 /*                                                               */
197 /* ============================================================= */
198
199 /* =========================================== */
200 /*                                             */
201 /*          Object typing & Creation           */
202 /*                                             */
203 /* =========================================== */
204 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
205 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
206     iface);
207 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
208     iface);
209 #define gst_xvimagesink_parent_class parent_class
210 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
211     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
212         gst_xvimagesink_navigation_init);
213     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
214         gst_xvimagesink_video_overlay_init);
215     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
216         gst_xvimagesink_colorbalance_init));
217
218
219 /* ============================================================= */
220 /*                                                               */
221 /*                       Private Methods                         */
222 /*                                                               */
223 /* ============================================================= */
224
225
226 /* We are called with the x_lock taken */
227 static void
228 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
229     GstXWindow * xwindow, GstVideoRectangle rect)
230 {
231   gint t1, t2;
232
233   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
234   g_return_if_fail (xwindow != NULL);
235
236   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
237       xvimagesink->xcontext->black);
238
239   /* Left border */
240   if (rect.x > xvimagesink->render_rect.x) {
241     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
242         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
243         rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
244   }
245
246   /* Right border */
247   t1 = rect.x + rect.w;
248   t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
249   if (t1 < t2) {
250     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
251         t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
252   }
253
254   /* Top border */
255   if (rect.y > xvimagesink->render_rect.y) {
256     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
257         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
258         xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
259   }
260
261   /* Bottom border */
262   t1 = rect.y + rect.h;
263   t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
264   if (t1 < t2) {
265     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
266         xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
267   }
268 }
269
270 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
271  * if no window was available  */
272 static gboolean
273 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
274 {
275   GstXvImageMeta *meta;
276   GstVideoCropMeta *crop;
277   GstVideoRectangle result;
278   gboolean draw_border = FALSE;
279   GstVideoRectangle src, dst;
280
281   /* We take the flow_lock. If expose is in there we don't want to run
282      concurrently from the data flow thread */
283   g_mutex_lock (xvimagesink->flow_lock);
284
285   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
286     g_mutex_unlock (xvimagesink->flow_lock);
287     return FALSE;
288   }
289
290   /* Draw borders when displaying the first frame. After this
291      draw borders only on expose event or after a size change. */
292   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
293     draw_border = TRUE;
294   }
295
296   /* Store a reference to the last image we put, lose the previous one */
297   if (xvimage && xvimagesink->cur_image != xvimage) {
298     if (xvimagesink->cur_image) {
299       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
300       gst_buffer_unref (xvimagesink->cur_image);
301     }
302     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
303     xvimagesink->cur_image = gst_buffer_ref (xvimage);
304   }
305
306   /* Expose sends a NULL image, we take the latest frame */
307   if (!xvimage) {
308     if (xvimagesink->cur_image) {
309       draw_border = TRUE;
310       xvimage = xvimagesink->cur_image;
311     } else {
312       g_mutex_unlock (xvimagesink->flow_lock);
313       return TRUE;
314     }
315   }
316
317   meta = gst_buffer_get_xvimage_meta (xvimage);
318
319   crop = gst_buffer_get_video_crop_meta (xvimage);
320
321   if (crop) {
322     src.x = crop->x + meta->x;
323     src.y = crop->y + meta->y;
324     src.w = crop->width;
325     src.h = crop->height;
326     GST_LOG_OBJECT (xvimagesink,
327         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
328   } else {
329     src.x = meta->x;
330     src.y = meta->y;
331     src.w = meta->width;
332     src.h = meta->height;
333   }
334
335   if (xvimagesink->keep_aspect) {
336     GstVideoRectangle s;
337
338     /* We take the size of the source material as it was negotiated and
339      * corrected for DAR. This size can be different from the cropped size in
340      * which case the image will be scaled to fit the negotiated size. */
341     s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
342     s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
343     dst.w = xvimagesink->render_rect.w;
344     dst.h = xvimagesink->render_rect.h;
345
346     gst_video_sink_center_rect (s, dst, &result, TRUE);
347     result.x += xvimagesink->render_rect.x;
348     result.y += xvimagesink->render_rect.y;
349   } else {
350     memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
351   }
352
353   g_mutex_lock (xvimagesink->x_lock);
354
355   if (draw_border && xvimagesink->draw_borders) {
356     gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
357         result);
358     xvimagesink->redraw_border = FALSE;
359   }
360 #ifdef HAVE_XSHM
361   if (xvimagesink->xcontext->use_xshm) {
362     GST_LOG_OBJECT (xvimagesink,
363         "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
364         GST_PTR_FORMAT, meta->width, meta->height,
365         xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
366
367     XvShmPutImage (xvimagesink->xcontext->disp,
368         xvimagesink->xcontext->xv_port_id,
369         xvimagesink->xwindow->win,
370         xvimagesink->xwindow->gc, meta->xvimage,
371         src.x, src.y, src.w, src.h,
372         result.x, result.y, result.w, result.h, FALSE);
373   } else
374 #endif /* HAVE_XSHM */
375   {
376     XvPutImage (xvimagesink->xcontext->disp,
377         xvimagesink->xcontext->xv_port_id,
378         xvimagesink->xwindow->win,
379         xvimagesink->xwindow->gc, meta->xvimage,
380         src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
381   }
382
383   XSync (xvimagesink->xcontext->disp, FALSE);
384
385   g_mutex_unlock (xvimagesink->x_lock);
386
387   g_mutex_unlock (xvimagesink->flow_lock);
388
389   return TRUE;
390 }
391
392 static gboolean
393 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
394     GstXWindow * window)
395 {
396   Atom hints_atom = None;
397   MotifWmHints *hints;
398
399   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
400   g_return_val_if_fail (window != NULL, FALSE);
401
402   g_mutex_lock (xvimagesink->x_lock);
403
404   hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
405       True);
406   if (hints_atom == None) {
407     g_mutex_unlock (xvimagesink->x_lock);
408     return FALSE;
409   }
410
411   hints = g_malloc0 (sizeof (MotifWmHints));
412
413   hints->flags |= MWM_HINTS_DECORATIONS;
414   hints->decorations = 1 << 0;
415
416   XChangeProperty (xvimagesink->xcontext->disp, window->win,
417       hints_atom, hints_atom, 32, PropModeReplace,
418       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
419
420   XSync (xvimagesink->xcontext->disp, FALSE);
421
422   g_mutex_unlock (xvimagesink->x_lock);
423
424   g_free (hints);
425
426   return TRUE;
427 }
428
429 static void
430 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
431     GstXWindow * xwindow, const gchar * media_title)
432 {
433   if (media_title) {
434     g_free (xvimagesink->media_title);
435     xvimagesink->media_title = g_strdup (media_title);
436   }
437   if (xwindow) {
438     /* we have a window */
439     if (xwindow->internal) {
440       XTextProperty xproperty;
441       const gchar *app_name;
442       const gchar *title = NULL;
443       gchar *title_mem = NULL;
444
445       /* set application name as a title */
446       app_name = g_get_application_name ();
447
448       if (app_name && xvimagesink->media_title) {
449         title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
450             app_name, NULL);
451       } else if (app_name) {
452         title = app_name;
453       } else if (xvimagesink->media_title) {
454         title = xvimagesink->media_title;
455       }
456
457       if (title) {
458         if ((XStringListToTextProperty (((char **) &title), 1,
459                     &xproperty)) != 0) {
460           XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
461           XFree (xproperty.value);
462         }
463
464         g_free (title_mem);
465       }
466     }
467   }
468 }
469
470 /* This function handles a GstXWindow creation
471  * The width and height are the actual pixel size on the display */
472 static GstXWindow *
473 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
474     gint width, gint height)
475 {
476   GstXWindow *xwindow = NULL;
477   XGCValues values;
478
479   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
480
481   xwindow = g_new0 (GstXWindow, 1);
482
483   xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
484   xvimagesink->render_rect.w = width;
485   xvimagesink->render_rect.h = height;
486
487   xwindow->width = width;
488   xwindow->height = height;
489   xwindow->internal = TRUE;
490
491   g_mutex_lock (xvimagesink->x_lock);
492
493   xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
494       xvimagesink->xcontext->root,
495       0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
496
497   /* We have to do that to prevent X from redrawing the background on
498    * ConfigureNotify. This takes away flickering of video when resizing. */
499   XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
500
501   /* set application name as a title */
502   gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
503
504   if (xvimagesink->handle_events) {
505     Atom wm_delete;
506
507     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
508         StructureNotifyMask | PointerMotionMask | KeyPressMask |
509         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
510
511     /* Tell the window manager we'd like delete client messages instead of
512      * being killed */
513     wm_delete = XInternAtom (xvimagesink->xcontext->disp,
514         "WM_DELETE_WINDOW", True);
515     if (wm_delete != None) {
516       (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
517           &wm_delete, 1);
518     }
519   }
520
521   xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
522       xwindow->win, 0, &values);
523
524   XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
525
526   XSync (xvimagesink->xcontext->disp, FALSE);
527
528   g_mutex_unlock (xvimagesink->x_lock);
529
530   gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
531
532   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
533       xwindow->win);
534
535   return xwindow;
536 }
537
538 /* This function destroys a GstXWindow */
539 static void
540 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
541     GstXWindow * xwindow)
542 {
543   g_return_if_fail (xwindow != NULL);
544   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
545
546   g_mutex_lock (xvimagesink->x_lock);
547
548   /* If we did not create that window we just free the GC and let it live */
549   if (xwindow->internal)
550     XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
551   else
552     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
553
554   XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
555
556   XSync (xvimagesink->xcontext->disp, FALSE);
557
558   g_mutex_unlock (xvimagesink->x_lock);
559
560   g_free (xwindow);
561 }
562
563 static void
564 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
565 {
566   XWindowAttributes attr;
567
568   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
569
570   /* Update the window geometry */
571   g_mutex_lock (xvimagesink->x_lock);
572   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
573     g_mutex_unlock (xvimagesink->x_lock);
574     return;
575   }
576
577   XGetWindowAttributes (xvimagesink->xcontext->disp,
578       xvimagesink->xwindow->win, &attr);
579
580   xvimagesink->xwindow->width = attr.width;
581   xvimagesink->xwindow->height = attr.height;
582
583   if (!xvimagesink->have_render_rect) {
584     xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
585     xvimagesink->render_rect.w = attr.width;
586     xvimagesink->render_rect.h = attr.height;
587   }
588
589   g_mutex_unlock (xvimagesink->x_lock);
590 }
591
592 static void
593 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
594     GstXWindow * xwindow)
595 {
596   g_return_if_fail (xwindow != NULL);
597   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
598
599   g_mutex_lock (xvimagesink->x_lock);
600
601   XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
602       xwindow->win);
603
604   XSync (xvimagesink->xcontext->disp, FALSE);
605
606   g_mutex_unlock (xvimagesink->x_lock);
607 }
608
609 /* This function commits our internal colorbalance settings to our grabbed Xv
610    port. If the xcontext is not initialized yet it simply returns */
611 static void
612 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
613 {
614   GList *channels = NULL;
615
616   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
617
618   /* If we haven't initialized the X context we can't update anything */
619   if (xvimagesink->xcontext == NULL)
620     return;
621
622   /* Don't set the attributes if they haven't been changed, to avoid
623    * rounding errors changing the values */
624   if (!xvimagesink->cb_changed)
625     return;
626
627   /* For each channel of the colorbalance we calculate the correct value
628      doing range conversion and then set the Xv port attribute to match our
629      values. */
630   channels = xvimagesink->xcontext->channels_list;
631
632   while (channels) {
633     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
634       GstColorBalanceChannel *channel = NULL;
635       Atom prop_atom;
636       gint value = 0;
637       gdouble convert_coef;
638
639       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
640       g_object_ref (channel);
641
642       /* Our range conversion coef */
643       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
644
645       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
646         value = xvimagesink->hue;
647       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
648         value = xvimagesink->saturation;
649       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
650         value = xvimagesink->contrast;
651       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
652         value = xvimagesink->brightness;
653       } else {
654         g_warning ("got an unknown channel %s", channel->label);
655         g_object_unref (channel);
656         return;
657       }
658
659       /* Committing to Xv port */
660       g_mutex_lock (xvimagesink->x_lock);
661       prop_atom =
662           XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
663       if (prop_atom != None) {
664         int xv_value;
665         xv_value =
666             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
667         XvSetPortAttribute (xvimagesink->xcontext->disp,
668             xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
669       }
670       g_mutex_unlock (xvimagesink->x_lock);
671
672       g_object_unref (channel);
673     }
674     channels = g_list_next (channels);
675   }
676 }
677
678 /* This function handles XEvents that might be in the queue. It generates
679    GstEvent that will be sent upstream in the pipeline to handle interactivity
680    and navigation. It will also listen for configure events on the window to
681    trigger caps renegotiation so on the fly software scaling can work. */
682 static void
683 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
684 {
685   XEvent e;
686   guint pointer_x = 0, pointer_y = 0;
687   gboolean pointer_moved = FALSE;
688   gboolean exposed = FALSE, configured = FALSE;
689
690   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
691
692   /* Handle Interaction, produces navigation events */
693
694   /* We get all pointer motion events, only the last position is
695      interesting. */
696   g_mutex_lock (xvimagesink->flow_lock);
697   g_mutex_lock (xvimagesink->x_lock);
698   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
699           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
700     g_mutex_unlock (xvimagesink->x_lock);
701     g_mutex_unlock (xvimagesink->flow_lock);
702
703     switch (e.type) {
704       case MotionNotify:
705         pointer_x = e.xmotion.x;
706         pointer_y = e.xmotion.y;
707         pointer_moved = TRUE;
708         break;
709       default:
710         break;
711     }
712     g_mutex_lock (xvimagesink->flow_lock);
713     g_mutex_lock (xvimagesink->x_lock);
714   }
715
716   if (pointer_moved) {
717     g_mutex_unlock (xvimagesink->x_lock);
718     g_mutex_unlock (xvimagesink->flow_lock);
719
720     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
721         pointer_x, pointer_y);
722     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
723         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
724
725     g_mutex_lock (xvimagesink->flow_lock);
726     g_mutex_lock (xvimagesink->x_lock);
727   }
728
729   /* We get all events on our window to throw them upstream */
730   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
731           xvimagesink->xwindow->win,
732           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
733           &e)) {
734     KeySym keysym;
735     const char *key_str = NULL;
736
737     /* We lock only for the X function call */
738     g_mutex_unlock (xvimagesink->x_lock);
739     g_mutex_unlock (xvimagesink->flow_lock);
740
741     switch (e.type) {
742       case ButtonPress:
743         /* Mouse button pressed over our window. We send upstream
744            events for interactivity/navigation */
745         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
746             e.xbutton.button, e.xbutton.x, e.xbutton.y);
747         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
748             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
749         break;
750       case ButtonRelease:
751         /* Mouse button released over our window. We send upstream
752            events for interactivity/navigation */
753         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
754             e.xbutton.button, e.xbutton.x, e.xbutton.y);
755         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
756             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
757         break;
758       case KeyPress:
759       case KeyRelease:
760         /* Key pressed/released over our window. We send upstream
761            events for interactivity/navigation */
762         g_mutex_lock (xvimagesink->x_lock);
763         keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
764             e.xkey.keycode, 0, 0);
765         if (keysym != NoSymbol) {
766           key_str = XKeysymToString (keysym);
767         } else {
768           key_str = "unknown";
769         }
770         g_mutex_unlock (xvimagesink->x_lock);
771         GST_DEBUG_OBJECT (xvimagesink,
772             "key %d pressed over window at %d,%d (%s)",
773             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
774         gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
775             e.type == KeyPress ? "key-press" : "key-release", key_str);
776         break;
777       default:
778         GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
779             e.type);
780     }
781     g_mutex_lock (xvimagesink->flow_lock);
782     g_mutex_lock (xvimagesink->x_lock);
783   }
784
785   /* Handle Expose */
786   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
787           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
788     switch (e.type) {
789       case Expose:
790         exposed = TRUE;
791         break;
792       case ConfigureNotify:
793         g_mutex_unlock (xvimagesink->x_lock);
794         gst_xvimagesink_xwindow_update_geometry (xvimagesink);
795         g_mutex_lock (xvimagesink->x_lock);
796         configured = TRUE;
797         break;
798       default:
799         break;
800     }
801   }
802
803   if (xvimagesink->handle_expose && (exposed || configured)) {
804     g_mutex_unlock (xvimagesink->x_lock);
805     g_mutex_unlock (xvimagesink->flow_lock);
806
807     gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
808
809     g_mutex_lock (xvimagesink->flow_lock);
810     g_mutex_lock (xvimagesink->x_lock);
811   }
812
813   /* Handle Display events */
814   while (XPending (xvimagesink->xcontext->disp)) {
815     XNextEvent (xvimagesink->xcontext->disp, &e);
816
817     switch (e.type) {
818       case ClientMessage:{
819         Atom wm_delete;
820
821         wm_delete = XInternAtom (xvimagesink->xcontext->disp,
822             "WM_DELETE_WINDOW", True);
823         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
824           /* Handle window deletion by posting an error on the bus */
825           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
826               ("Output window was closed"), (NULL));
827
828           g_mutex_unlock (xvimagesink->x_lock);
829           gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
830           xvimagesink->xwindow = NULL;
831           g_mutex_lock (xvimagesink->x_lock);
832         }
833         break;
834       }
835       default:
836         break;
837     }
838   }
839
840   g_mutex_unlock (xvimagesink->x_lock);
841   g_mutex_unlock (xvimagesink->flow_lock);
842 }
843
844 static void
845 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
846     XvAdaptorInfo * adaptors, int adaptor_no)
847 {
848   gint j;
849   gint res;
850
851   /* Do we support XvImageMask ? */
852   if (!(adaptors[adaptor_no].type & XvImageMask)) {
853     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
854         adaptors[adaptor_no].name);
855     return;
856   }
857
858   /* We found such an adaptor, looking for an available port */
859   for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
860     /* We try to grab the port */
861     res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
862     if (Success == res) {
863       xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
864       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
865           adaptors[adaptor_no].num_ports);
866     } else {
867       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
868           adaptors[adaptor_no].name, res);
869     }
870   }
871 }
872
873 /* This function generates a caps with all supported format by the first
874    Xv grabable port we find. We store each one of the supported formats in a
875    format list and append the format to a newly created caps that we return
876    If this function does not return NULL because of an error, it also grabs
877    the port via XvGrabPort */
878 static GstCaps *
879 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
880     GstXContext * xcontext)
881 {
882   gint i;
883   XvAdaptorInfo *adaptors;
884   gint nb_formats;
885   XvImageFormatValues *formats = NULL;
886   guint nb_encodings;
887   XvEncodingInfo *encodings = NULL;
888   gulong max_w = G_MAXINT, max_h = G_MAXINT;
889   GstCaps *caps = NULL;
890   GstCaps *rgb_caps = NULL;
891
892   g_return_val_if_fail (xcontext != NULL, NULL);
893
894   /* First let's check that XVideo extension is available */
895   if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
896     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
897         ("Could not initialise Xv output"),
898         ("XVideo extension is not available"));
899     return NULL;
900   }
901
902   /* Then we get adaptors list */
903   if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
904           &xcontext->nb_adaptors, &adaptors)) {
905     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
906         ("Could not initialise Xv output"),
907         ("Failed getting XV adaptors list"));
908     return NULL;
909   }
910
911   xcontext->xv_port_id = 0;
912
913   GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
914
915   xcontext->adaptors =
916       (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
917
918   /* Now fill up our adaptor name array */
919   for (i = 0; i < xcontext->nb_adaptors; i++) {
920     xcontext->adaptors[i] = g_strdup (adaptors[i].name);
921   }
922
923   if (xvimagesink->adaptor_no != -1 &&
924       xvimagesink->adaptor_no < xcontext->nb_adaptors) {
925     /* Find xv port from user defined adaptor */
926     gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
927         xvimagesink->adaptor_no);
928   }
929
930   if (!xcontext->xv_port_id) {
931     /* Now search for an adaptor that supports XvImageMask */
932     for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
933       gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
934       xvimagesink->adaptor_no = i;
935     }
936   }
937
938   XvFreeAdaptorInfo (adaptors);
939
940   if (!xcontext->xv_port_id) {
941     xvimagesink->adaptor_no = -1;
942     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
943         ("Could not initialise Xv output"), ("No port available"));
944     return NULL;
945   }
946
947   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
948   {
949     int count, todo = 3;
950     XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
951         xcontext->xv_port_id, &count);
952     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
953     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
954     static const char colorkey[] = "XV_COLORKEY";
955
956     GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
957
958     xvimagesink->have_autopaint_colorkey = FALSE;
959     xvimagesink->have_double_buffer = FALSE;
960     xvimagesink->have_colorkey = FALSE;
961
962     for (i = 0; ((i < count) && todo); i++)
963       if (!strcmp (attr[i].name, autopaint)) {
964         const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
965
966         /* turn on autopaint colorkey */
967         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
968             (xvimagesink->autopaint_colorkey ? 1 : 0));
969         todo--;
970         xvimagesink->have_autopaint_colorkey = TRUE;
971       } else if (!strcmp (attr[i].name, dbl_buffer)) {
972         const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
973
974         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
975             (xvimagesink->double_buffer ? 1 : 0));
976         todo--;
977         xvimagesink->have_double_buffer = TRUE;
978       } else if (!strcmp (attr[i].name, colorkey)) {
979         /* Set the colorkey, default is something that is dark but hopefully
980          * won't randomly appear on the screen elsewhere (ie not black or greys)
981          * can be overridden by setting "colorkey" property
982          */
983         const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
984         guint32 ckey = 0;
985         gboolean set_attr = TRUE;
986         guint cr, cg, cb;
987
988         /* set a colorkey in the right format RGB565/RGB888
989          * We only handle these 2 cases, because they're the only types of
990          * devices we've encountered. If we don't recognise it, leave it alone
991          */
992         cr = (xvimagesink->colorkey >> 16);
993         cg = (xvimagesink->colorkey >> 8) & 0xFF;
994         cb = (xvimagesink->colorkey) & 0xFF;
995         switch (xcontext->depth) {
996           case 16:             /* RGB 565 */
997             cr >>= 3;
998             cg >>= 2;
999             cb >>= 3;
1000             ckey = (cr << 11) | (cg << 5) | cb;
1001             break;
1002           case 24:
1003           case 32:             /* RGB 888 / ARGB 8888 */
1004             ckey = (cr << 16) | (cg << 8) | cb;
1005             break;
1006           default:
1007             GST_DEBUG_OBJECT (xvimagesink,
1008                 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1009                 xcontext->depth);
1010             set_attr = FALSE;
1011             break;
1012         }
1013
1014         if (set_attr) {
1015           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1016               (guint32) attr[i].max_value);
1017           GST_LOG_OBJECT (xvimagesink,
1018               "Setting color key for display depth %d to 0x%x",
1019               xcontext->depth, ckey);
1020
1021           XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1022               (gint) ckey);
1023         }
1024         todo--;
1025         xvimagesink->have_colorkey = TRUE;
1026       }
1027
1028     XFree (attr);
1029   }
1030
1031   /* Get the list of encodings supported by the adapter and look for the
1032    * XV_IMAGE encoding so we can determine the maximum width and height
1033    * supported */
1034   XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1035       &encodings);
1036
1037   for (i = 0; i < nb_encodings; i++) {
1038     GST_LOG_OBJECT (xvimagesink,
1039         "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1040         i, encodings[i].name, encodings[i].width, encodings[i].height,
1041         encodings[i].rate.numerator, encodings[i].rate.denominator);
1042     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1043       max_w = encodings[i].width;
1044       max_h = encodings[i].height;
1045     }
1046   }
1047
1048   XvFreeEncodingInfo (encodings);
1049
1050   /* We get all image formats supported by our port */
1051   formats = XvListImageFormats (xcontext->disp,
1052       xcontext->xv_port_id, &nb_formats);
1053   caps = gst_caps_new_empty ();
1054   for (i = 0; i < nb_formats; i++) {
1055     GstCaps *format_caps = NULL;
1056     gboolean is_rgb_format = FALSE;
1057     GstVideoFormat vformat;
1058
1059     /* We set the image format of the xcontext to an existing one. This
1060        is just some valid image format for making our xshm calls check before
1061        caps negotiation really happens. */
1062     xcontext->im_format = formats[i].id;
1063
1064     switch (formats[i].type) {
1065       case XvRGB:
1066       {
1067         XvImageFormatValues *fmt = &(formats[i]);
1068         gint endianness;
1069
1070         endianness =
1071             (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1072
1073         vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1074             endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1075         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1076           break;
1077
1078         format_caps = gst_caps_new_simple ("video/x-raw",
1079             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1080             "width", GST_TYPE_INT_RANGE, 1, max_w,
1081             "height", GST_TYPE_INT_RANGE, 1, max_h,
1082             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1083
1084         is_rgb_format = TRUE;
1085         break;
1086       }
1087       case XvYUV:
1088       {
1089         vformat = gst_video_format_from_fourcc (formats[i].id);
1090         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1091           break;
1092
1093         format_caps = gst_caps_new_simple ("video/x-raw",
1094             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1095             "width", GST_TYPE_INT_RANGE, 1, max_w,
1096             "height", GST_TYPE_INT_RANGE, 1, max_h,
1097             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1098         break;
1099       }
1100       default:
1101         vformat = GST_VIDEO_FORMAT_UNKNOWN;
1102         g_assert_not_reached ();
1103         break;
1104     }
1105
1106     if (format_caps) {
1107       GstXvImageFormat *format = NULL;
1108
1109       format = g_new0 (GstXvImageFormat, 1);
1110       if (format) {
1111         format->format = formats[i].id;
1112         format->vformat = vformat;
1113         format->caps = gst_caps_copy (format_caps);
1114         xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1115       }
1116
1117       if (is_rgb_format) {
1118         if (rgb_caps == NULL)
1119           rgb_caps = format_caps;
1120         else
1121           gst_caps_append (rgb_caps, format_caps);
1122       } else
1123         gst_caps_append (caps, format_caps);
1124     }
1125   }
1126
1127   /* Collected all caps into either the caps or rgb_caps structures.
1128    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1129   if (rgb_caps)
1130     gst_caps_append (caps, rgb_caps);
1131
1132   if (formats)
1133     XFree (formats);
1134
1135   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1136
1137   if (gst_caps_is_empty (caps)) {
1138     gst_caps_unref (caps);
1139     XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1140     GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1141         ("No supported format found"));
1142     return NULL;
1143   }
1144
1145   return caps;
1146 }
1147
1148 static gpointer
1149 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1150 {
1151   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1152
1153   GST_OBJECT_LOCK (xvimagesink);
1154   while (xvimagesink->running) {
1155     GST_OBJECT_UNLOCK (xvimagesink);
1156
1157     if (xvimagesink->xwindow) {
1158       gst_xvimagesink_handle_xevents (xvimagesink);
1159     }
1160     /* FIXME: do we want to align this with the framerate or anything else? */
1161     g_usleep (G_USEC_PER_SEC / 20);
1162
1163     GST_OBJECT_LOCK (xvimagesink);
1164   }
1165   GST_OBJECT_UNLOCK (xvimagesink);
1166
1167   return NULL;
1168 }
1169
1170 static void
1171 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1172 {
1173   GThread *thread = NULL;
1174
1175   /* don't start the thread too early */
1176   if (xvimagesink->xcontext == NULL) {
1177     return;
1178   }
1179
1180   GST_OBJECT_LOCK (xvimagesink);
1181   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1182     if (!xvimagesink->event_thread) {
1183       /* Setup our event listening thread */
1184       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1185           xvimagesink->handle_expose, xvimagesink->handle_events);
1186       xvimagesink->running = TRUE;
1187       xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1188           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1189     }
1190   } else {
1191     if (xvimagesink->event_thread) {
1192       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1193           xvimagesink->handle_expose, xvimagesink->handle_events);
1194       xvimagesink->running = FALSE;
1195       /* grab thread and mark it as NULL */
1196       thread = xvimagesink->event_thread;
1197       xvimagesink->event_thread = NULL;
1198     }
1199   }
1200   GST_OBJECT_UNLOCK (xvimagesink);
1201
1202   /* Wait for our event thread to finish */
1203   if (thread)
1204     g_thread_join (thread);
1205
1206 }
1207
1208
1209 /* This function calculates the pixel aspect ratio based on the properties
1210  * in the xcontext structure and stores it there. */
1211 static void
1212 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1213 {
1214   static const gint par[][2] = {
1215     {1, 1},                     /* regular screen */
1216     {16, 15},                   /* PAL TV */
1217     {11, 10},                   /* 525 line Rec.601 video */
1218     {54, 59},                   /* 625 line Rec.601 video */
1219     {64, 45},                   /* 1280x1024 on 16:9 display */
1220     {5, 3},                     /* 1280x1024 on 4:3 display */
1221     {4, 3}                      /*  800x600 on 16:9 display */
1222   };
1223   gint i;
1224   gint index;
1225   gdouble ratio;
1226   gdouble delta;
1227
1228 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1229
1230   /* first calculate the "real" ratio based on the X values;
1231    * which is the "physical" w/h divided by the w/h in pixels of the display */
1232   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1233       / (xcontext->heightmm * xcontext->width);
1234
1235   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1236    * override here */
1237   if (xcontext->width == 720 && xcontext->height == 576) {
1238     ratio = 4.0 * 576 / (3.0 * 720);
1239   }
1240   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1241
1242   /* now find the one from par[][2] with the lowest delta to the real one */
1243   delta = DELTA (0);
1244   index = 0;
1245
1246   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1247     gdouble this_delta = DELTA (i);
1248
1249     if (this_delta < delta) {
1250       index = i;
1251       delta = this_delta;
1252     }
1253   }
1254
1255   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1256       par[index][0], par[index][1]);
1257
1258   g_free (xcontext->par);
1259   xcontext->par = g_new0 (GValue, 1);
1260   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1261   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1262   GST_DEBUG ("set xcontext PAR to %d/%d",
1263       gst_value_get_fraction_numerator (xcontext->par),
1264       gst_value_get_fraction_denominator (xcontext->par));
1265 }
1266
1267 /* This function gets the X Display and global info about it. Everything is
1268    stored in our object and will be cleaned when the object is disposed. Note
1269    here that caps for supported format are generated without any window or
1270    image creation */
1271 static GstXContext *
1272 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1273 {
1274   GstXContext *xcontext = NULL;
1275   XPixmapFormatValues *px_formats = NULL;
1276   gint nb_formats = 0, i, j, N_attr;
1277   XvAttribute *xv_attr;
1278   Atom prop_atom;
1279   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1280     "XV_BRIGHTNESS", "XV_CONTRAST"
1281   };
1282
1283   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1284
1285   xcontext = g_new0 (GstXContext, 1);
1286   xcontext->im_format = 0;
1287
1288   g_mutex_lock (xvimagesink->x_lock);
1289
1290   xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1291
1292   if (!xcontext->disp) {
1293     g_mutex_unlock (xvimagesink->x_lock);
1294     g_free (xcontext);
1295     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1296         ("Could not initialise Xv output"), ("Could not open display"));
1297     return NULL;
1298   }
1299
1300   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1301   xcontext->screen_num = DefaultScreen (xcontext->disp);
1302   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1303   xcontext->root = DefaultRootWindow (xcontext->disp);
1304   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1305   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1306   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1307
1308   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1309   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1310   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1311   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1312
1313   GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1314       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1315
1316   gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1317   /* We get supported pixmap formats at supported depth */
1318   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1319
1320   if (!px_formats) {
1321     XCloseDisplay (xcontext->disp);
1322     g_mutex_unlock (xvimagesink->x_lock);
1323     g_free (xcontext->par);
1324     g_free (xcontext);
1325     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1326         ("Could not initialise Xv output"), ("Could not get pixel formats"));
1327     return NULL;
1328   }
1329
1330   /* We get bpp value corresponding to our running depth */
1331   for (i = 0; i < nb_formats; i++) {
1332     if (px_formats[i].depth == xcontext->depth)
1333       xcontext->bpp = px_formats[i].bits_per_pixel;
1334   }
1335
1336   XFree (px_formats);
1337
1338   xcontext->endianness =
1339       (ImageByteOrder (xcontext->disp) ==
1340       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1341
1342   /* our caps system handles 24/32bpp RGB as big-endian. */
1343   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1344       xcontext->endianness == G_LITTLE_ENDIAN) {
1345     xcontext->endianness = G_BIG_ENDIAN;
1346     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1347     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1348     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1349     if (xcontext->bpp == 24) {
1350       xcontext->visual->red_mask >>= 8;
1351       xcontext->visual->green_mask >>= 8;
1352       xcontext->visual->blue_mask >>= 8;
1353     }
1354   }
1355
1356   xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1357
1358   /* Search for XShm extension support */
1359 #ifdef HAVE_XSHM
1360   if (XShmQueryExtension (xcontext->disp) &&
1361       gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1362     xcontext->use_xshm = TRUE;
1363     GST_DEBUG ("xvimagesink is using XShm extension");
1364   } else
1365 #endif /* HAVE_XSHM */
1366   {
1367     xcontext->use_xshm = FALSE;
1368     GST_DEBUG ("xvimagesink is not using XShm extension");
1369   }
1370
1371   if (!xcontext->caps) {
1372     XCloseDisplay (xcontext->disp);
1373     g_mutex_unlock (xvimagesink->x_lock);
1374     g_free (xcontext->par);
1375     g_free (xcontext);
1376     /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1377     return NULL;
1378   }
1379
1380   xv_attr = XvQueryPortAttributes (xcontext->disp,
1381       xcontext->xv_port_id, &N_attr);
1382
1383
1384   /* Generate the channels list */
1385   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1386     XvAttribute *matching_attr = NULL;
1387
1388     /* Retrieve the property atom if it exists. If it doesn't exist,
1389      * the attribute itself must not either, so we can skip */
1390     prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1391     if (prop_atom == None)
1392       continue;
1393
1394     if (xv_attr != NULL) {
1395       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1396         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1397           matching_attr = xv_attr + j;
1398     }
1399
1400     if (matching_attr) {
1401       GstColorBalanceChannel *channel;
1402
1403       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1404       channel->label = g_strdup (channels[i]);
1405       channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1406       channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1407
1408       xcontext->channels_list = g_list_append (xcontext->channels_list,
1409           channel);
1410
1411       /* If the colorbalance settings have not been touched we get Xv values
1412          as defaults and update our internal variables */
1413       if (!xvimagesink->cb_changed) {
1414         gint val;
1415
1416         XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1417             prop_atom, &val);
1418         /* Normalize val to [-1000, 1000] */
1419         val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1420             (double) (channel->max_value - channel->min_value));
1421
1422         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1423           xvimagesink->hue = val;
1424         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1425           xvimagesink->saturation = val;
1426         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1427           xvimagesink->brightness = val;
1428         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1429           xvimagesink->contrast = val;
1430       }
1431     }
1432   }
1433
1434   if (xv_attr)
1435     XFree (xv_attr);
1436
1437   g_mutex_unlock (xvimagesink->x_lock);
1438
1439   return xcontext;
1440 }
1441
1442 /* This function cleans the X context. Closing the Display, releasing the XV
1443    port and unrefing the caps for supported formats. */
1444 static void
1445 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1446 {
1447   GList *formats_list, *channels_list;
1448   GstXContext *xcontext;
1449   gint i = 0;
1450
1451   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1452
1453   GST_OBJECT_LOCK (xvimagesink);
1454   if (xvimagesink->xcontext == NULL) {
1455     GST_OBJECT_UNLOCK (xvimagesink);
1456     return;
1457   }
1458
1459   /* Take the XContext from the sink and clean it up */
1460   xcontext = xvimagesink->xcontext;
1461   xvimagesink->xcontext = NULL;
1462
1463   GST_OBJECT_UNLOCK (xvimagesink);
1464
1465
1466   formats_list = xcontext->formats_list;
1467
1468   while (formats_list) {
1469     GstXvImageFormat *format = formats_list->data;
1470
1471     gst_caps_unref (format->caps);
1472     g_free (format);
1473     formats_list = g_list_next (formats_list);
1474   }
1475
1476   if (xcontext->formats_list)
1477     g_list_free (xcontext->formats_list);
1478
1479   channels_list = xcontext->channels_list;
1480
1481   while (channels_list) {
1482     GstColorBalanceChannel *channel = channels_list->data;
1483
1484     g_object_unref (channel);
1485     channels_list = g_list_next (channels_list);
1486   }
1487
1488   if (xcontext->channels_list)
1489     g_list_free (xcontext->channels_list);
1490
1491   gst_caps_unref (xcontext->caps);
1492   if (xcontext->last_caps)
1493     gst_caps_replace (&xcontext->last_caps, NULL);
1494
1495   for (i = 0; i < xcontext->nb_adaptors; i++) {
1496     g_free (xcontext->adaptors[i]);
1497   }
1498
1499   g_free (xcontext->adaptors);
1500
1501   g_free (xcontext->par);
1502
1503   g_mutex_lock (xvimagesink->x_lock);
1504
1505   GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1506
1507   XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1508
1509   XCloseDisplay (xcontext->disp);
1510
1511   g_mutex_unlock (xvimagesink->x_lock);
1512
1513   g_free (xcontext);
1514 }
1515
1516 /* Element stuff */
1517
1518 static GstCaps *
1519 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1520 {
1521   GstXvImageSink *xvimagesink;
1522   GstCaps *caps;
1523
1524   xvimagesink = GST_XVIMAGESINK (bsink);
1525
1526   if (xvimagesink->xcontext) {
1527     if (filter)
1528       return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1529           GST_CAPS_INTERSECT_FIRST);
1530     else
1531       return gst_caps_ref (xvimagesink->xcontext->caps);
1532   }
1533
1534   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1535   if (filter) {
1536     GstCaps *intersection;
1537
1538     intersection =
1539         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1540     gst_caps_unref (caps);
1541     caps = intersection;
1542   }
1543   return caps;
1544 }
1545
1546 static gboolean
1547 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1548 {
1549   GstXvImageSink *xvimagesink;
1550   GstStructure *structure;
1551   GstBufferPool *newpool, *oldpool;
1552   GstVideoInfo info;
1553   guint32 im_format = 0;
1554   gint video_par_n, video_par_d;        /* video's PAR */
1555   gint display_par_n, display_par_d;    /* display's PAR */
1556   guint num, den;
1557   gint size;
1558   static GstAllocationParams params = { 0, 15, 0, 0, };
1559
1560   xvimagesink = GST_XVIMAGESINK (bsink);
1561
1562   GST_DEBUG_OBJECT (xvimagesink,
1563       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1564       GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1565
1566   if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1567     goto incompatible_caps;
1568
1569   if (!gst_video_info_from_caps (&info, caps))
1570     goto invalid_format;
1571
1572   xvimagesink->fps_n = info.fps_n;
1573   xvimagesink->fps_d = info.fps_d;
1574
1575   xvimagesink->video_width = info.width;
1576   xvimagesink->video_height = info.height;
1577
1578   im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1579   if (im_format == -1)
1580     goto invalid_format;
1581
1582   size = info.size;
1583
1584   /* get aspect ratio from caps if it's present, and
1585    * convert video width and height to a display width and height
1586    * using wd / hd = wv / hv * PARv / PARd */
1587
1588   /* get video's PAR */
1589   video_par_n = info.par_n;
1590   video_par_d = info.par_d;
1591
1592   /* get display's PAR */
1593   if (xvimagesink->par) {
1594     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1595     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1596   } else {
1597     display_par_n = 1;
1598     display_par_d = 1;
1599   }
1600
1601   if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1602           info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1603     goto no_disp_ratio;
1604
1605   GST_DEBUG_OBJECT (xvimagesink,
1606       "video width/height: %dx%d, calculated display ratio: %d/%d",
1607       info.width, info.height, num, den);
1608
1609   /* now find a width x height that respects this display ratio.
1610    * prefer those that have one of w/h the same as the incoming video
1611    * using wd / hd = num / den */
1612
1613   /* start with same height, because of interlaced video */
1614   /* check hd / den is an integer scale factor, and scale wd with the PAR */
1615   if (info.height % den == 0) {
1616     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1617     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1618         gst_util_uint64_scale_int (info.height, num, den);
1619     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1620   } else if (info.width % num == 0) {
1621     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1622     GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1623     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1624         gst_util_uint64_scale_int (info.width, den, num);
1625   } else {
1626     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1627     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1628         gst_util_uint64_scale_int (info.height, num, den);
1629     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1630   }
1631   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1632       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1633
1634   /* Notify application to set xwindow id now */
1635   g_mutex_lock (xvimagesink->flow_lock);
1636   if (!xvimagesink->xwindow) {
1637     g_mutex_unlock (xvimagesink->flow_lock);
1638     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1639   } else {
1640     g_mutex_unlock (xvimagesink->flow_lock);
1641   }
1642
1643   /* Creating our window and our image with the display size in pixels */
1644   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1645       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1646     goto no_display_size;
1647
1648   g_mutex_lock (xvimagesink->flow_lock);
1649   if (!xvimagesink->xwindow) {
1650     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1651         GST_VIDEO_SINK_WIDTH (xvimagesink),
1652         GST_VIDEO_SINK_HEIGHT (xvimagesink));
1653   }
1654
1655   xvimagesink->info = info;
1656
1657   /* After a resize, we want to redraw the borders in case the new frame size
1658    * doesn't cover the same area */
1659   xvimagesink->redraw_border = TRUE;
1660
1661   /* create a new pool for the new configuration */
1662   newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1663
1664   structure = gst_buffer_pool_get_config (newpool);
1665   gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1666   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
1667   if (!gst_buffer_pool_set_config (newpool, structure))
1668     goto config_failed;
1669
1670   oldpool = xvimagesink->pool;
1671   /* we don't activate the pool yet, this will be done by downstream after it
1672    * has configured the pool. If downstream does not want our pool we will
1673    * activate it when we render into it */
1674   xvimagesink->pool = newpool;
1675   g_mutex_unlock (xvimagesink->flow_lock);
1676
1677   /* unref the old sink */
1678   if (oldpool) {
1679     /* we don't deactivate, some elements might still be using it, it will
1680      * be deactivated when the last ref is gone */
1681     gst_object_unref (oldpool);
1682   }
1683
1684   return TRUE;
1685
1686   /* ERRORS */
1687 incompatible_caps:
1688   {
1689     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1690     return FALSE;
1691   }
1692 invalid_format:
1693   {
1694     GST_DEBUG_OBJECT (xvimagesink,
1695         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1696     return FALSE;
1697   }
1698 no_disp_ratio:
1699   {
1700     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1701         ("Error calculating the output display ratio of the video."));
1702     return FALSE;
1703   }
1704 no_display_size:
1705   {
1706     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1707         ("Error calculating the output display ratio of the video."));
1708     return FALSE;
1709   }
1710 config_failed:
1711   {
1712     GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1713     g_mutex_unlock (xvimagesink->flow_lock);
1714     return FALSE;
1715   }
1716 }
1717
1718 static GstStateChangeReturn
1719 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1720 {
1721   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1722   GstXvImageSink *xvimagesink;
1723   GstXContext *xcontext = NULL;
1724
1725   xvimagesink = GST_XVIMAGESINK (element);
1726
1727   switch (transition) {
1728     case GST_STATE_CHANGE_NULL_TO_READY:
1729       /* Initializing the XContext */
1730       if (xvimagesink->xcontext == NULL) {
1731         xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1732         if (xcontext == NULL) {
1733           ret = GST_STATE_CHANGE_FAILURE;
1734           goto beach;
1735         }
1736         GST_OBJECT_LOCK (xvimagesink);
1737         if (xcontext)
1738           xvimagesink->xcontext = xcontext;
1739         GST_OBJECT_UNLOCK (xvimagesink);
1740       }
1741
1742       /* update object's par with calculated one if not set yet */
1743       if (!xvimagesink->par) {
1744         xvimagesink->par = g_new0 (GValue, 1);
1745         gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1746         GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1747       }
1748       /* call XSynchronize with the current value of synchronous */
1749       GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1750           xvimagesink->synchronous ? "TRUE" : "FALSE");
1751       XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1752       gst_xvimagesink_update_colorbalance (xvimagesink);
1753       gst_xvimagesink_manage_event_thread (xvimagesink);
1754       break;
1755     case GST_STATE_CHANGE_READY_TO_PAUSED:
1756       break;
1757     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1758       break;
1759     case GST_STATE_CHANGE_PAUSED_TO_READY:
1760       break;
1761     default:
1762       break;
1763   }
1764
1765   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1766
1767   switch (transition) {
1768     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1769       break;
1770     case GST_STATE_CHANGE_PAUSED_TO_READY:
1771       xvimagesink->fps_n = 0;
1772       xvimagesink->fps_d = 1;
1773       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1774       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1775       g_mutex_lock (xvimagesink->flow_lock);
1776       if (xvimagesink->pool)
1777         gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1778       g_mutex_unlock (xvimagesink->flow_lock);
1779       break;
1780     case GST_STATE_CHANGE_READY_TO_NULL:
1781       gst_xvimagesink_reset (xvimagesink);
1782       break;
1783     default:
1784       break;
1785   }
1786
1787 beach:
1788   return ret;
1789 }
1790
1791 static void
1792 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1793     GstClockTime * start, GstClockTime * end)
1794 {
1795   GstXvImageSink *xvimagesink;
1796
1797   xvimagesink = GST_XVIMAGESINK (bsink);
1798
1799   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1800     *start = GST_BUFFER_TIMESTAMP (buf);
1801     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1802       *end = *start + GST_BUFFER_DURATION (buf);
1803     } else {
1804       if (xvimagesink->fps_n > 0) {
1805         *end = *start +
1806             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1807             xvimagesink->fps_n);
1808       }
1809     }
1810   }
1811 }
1812
1813 static GstFlowReturn
1814 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1815 {
1816   GstFlowReturn res;
1817   GstXvImageSink *xvimagesink;
1818   GstXvImageMeta *meta;
1819   GstBuffer *to_put;
1820
1821   xvimagesink = GST_XVIMAGESINK (vsink);
1822
1823   meta = gst_buffer_get_xvimage_meta (buf);
1824
1825   if (meta && meta->sink == xvimagesink) {
1826     /* If this buffer has been allocated using our buffer management we simply
1827        put the ximage which is in the PRIVATE pointer */
1828     GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1829         buf);
1830     to_put = buf;
1831     res = GST_FLOW_OK;
1832   } else {
1833     GstVideoFrame src, dest;
1834     GstBufferPoolAcquireParams params = { 0, };
1835
1836     /* Else we have to copy the data into our private image, */
1837     /* if we have one... */
1838     GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1839
1840     /* we should have a pool, configured in setcaps */
1841     if (xvimagesink->pool == NULL)
1842       goto no_pool;
1843
1844     if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1845       goto activate_failed;
1846
1847     /* take a buffer from our pool, if there is no buffer in the pool something
1848      * is seriously wrong, waiting for the pool here might deadlock when we try
1849      * to go to PAUSED because we never flush the pool then. */
1850     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1851     res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, &params);
1852     if (res != GST_FLOW_OK)
1853       goto no_buffer;
1854
1855     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1856         "slow copy into bufferpool buffer %p", to_put);
1857
1858     if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1859       goto invalid_buffer;
1860
1861     if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1862       gst_video_frame_unmap (&src);
1863       goto invalid_buffer;
1864     }
1865
1866     gst_video_frame_copy (&dest, &src);
1867
1868     gst_video_frame_unmap (&dest);
1869     gst_video_frame_unmap (&src);
1870   }
1871
1872   if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1873     goto no_window;
1874
1875 done:
1876   if (to_put != buf)
1877     gst_buffer_unref (to_put);
1878
1879   return res;
1880
1881   /* ERRORS */
1882 no_pool:
1883   {
1884     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1885         ("Internal error: can't allocate images"),
1886         ("We don't have a bufferpool negotiated"));
1887     return GST_FLOW_ERROR;
1888   }
1889 no_buffer:
1890   {
1891     /* No image available. That's very bad ! */
1892     GST_WARNING_OBJECT (xvimagesink, "could not create image");
1893     return GST_FLOW_OK;
1894   }
1895 invalid_buffer:
1896   {
1897     /* No Window available to put our image into */
1898     GST_WARNING_OBJECT (xvimagesink, "could not map image");
1899     res = GST_FLOW_OK;
1900     goto done;
1901   }
1902 no_window:
1903   {
1904     /* No Window available to put our image into */
1905     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1906     res = GST_FLOW_ERROR;
1907     goto done;
1908   }
1909 activate_failed:
1910   {
1911     GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1912     res = GST_FLOW_ERROR;
1913     goto done;
1914   }
1915 }
1916
1917 static gboolean
1918 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1919 {
1920   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1921
1922   switch (GST_EVENT_TYPE (event)) {
1923     case GST_EVENT_TAG:{
1924       GstTagList *l;
1925       gchar *title = NULL;
1926
1927       gst_event_parse_tag (event, &l);
1928       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1929
1930       if (title) {
1931         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1932         gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1933             title);
1934
1935         g_free (title);
1936       }
1937       break;
1938     }
1939     default:
1940       break;
1941   }
1942   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1943 }
1944
1945 static gboolean
1946 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1947 {
1948   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1949   GstBufferPool *pool;
1950   GstStructure *config;
1951   GstCaps *caps;
1952   guint size;
1953   gboolean need_pool;
1954
1955   gst_query_parse_allocation (query, &caps, &need_pool);
1956
1957   if (caps == NULL)
1958     goto no_caps;
1959
1960   g_mutex_lock (xvimagesink->flow_lock);
1961   if ((pool = xvimagesink->pool))
1962     gst_object_ref (pool);
1963   g_mutex_unlock (xvimagesink->flow_lock);
1964
1965   if (pool != NULL) {
1966     GstCaps *pcaps;
1967
1968     /* we had a pool, check caps */
1969     GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1970     config = gst_buffer_pool_get_config (pool);
1971     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1972
1973     if (!gst_caps_is_equal (caps, pcaps)) {
1974       GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1975       /* different caps, we can't use this pool */
1976       gst_object_unref (pool);
1977       pool = NULL;
1978     }
1979     gst_structure_free (config);
1980   }
1981   if (pool == NULL && need_pool) {
1982     GstVideoInfo info;
1983
1984     if (!gst_video_info_from_caps (&info, caps))
1985       goto invalid_caps;
1986
1987     GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1988     pool = gst_xvimage_buffer_pool_new (xvimagesink);
1989
1990     /* the normal size of a frame */
1991     size = info.size;
1992
1993     config = gst_buffer_pool_get_config (pool);
1994     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1995     if (!gst_buffer_pool_set_config (pool, config))
1996       goto config_failed;
1997   }
1998   if (pool) {
1999     /* we need at least 2 buffer because we hold on to the last one */
2000     gst_query_add_allocation_pool (query, pool, size, 2, 0);
2001     gst_object_unref (pool);
2002   }
2003
2004   /* we also support various metadata */
2005   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2006   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2007
2008   return TRUE;
2009
2010   /* ERRORS */
2011 no_caps:
2012   {
2013     GST_DEBUG_OBJECT (bsink, "no caps specified");
2014     return FALSE;
2015   }
2016 invalid_caps:
2017   {
2018     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2019     return FALSE;
2020   }
2021 config_failed:
2022   {
2023     GST_DEBUG_OBJECT (bsink, "failed setting config");
2024     gst_object_unref (pool);
2025     return FALSE;
2026   }
2027 }
2028
2029 /* Interfaces stuff */
2030 static void
2031 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2032     GstStructure * structure)
2033 {
2034   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2035   GstPad *peer;
2036
2037   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2038     GstEvent *event;
2039     GstVideoRectangle src, dst, result;
2040     gdouble x, y, xscale = 1.0, yscale = 1.0;
2041
2042     event = gst_event_new_navigation (structure);
2043
2044     /* We take the flow_lock while we look at the window */
2045     g_mutex_lock (xvimagesink->flow_lock);
2046
2047     if (!xvimagesink->xwindow) {
2048       g_mutex_unlock (xvimagesink->flow_lock);
2049       return;
2050     }
2051
2052     if (xvimagesink->keep_aspect) {
2053       /* We get the frame position using the calculated geometry from _setcaps
2054          that respect pixel aspect ratios */
2055       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2056       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2057       dst.w = xvimagesink->render_rect.w;
2058       dst.h = xvimagesink->render_rect.h;
2059
2060       gst_video_sink_center_rect (src, dst, &result, TRUE);
2061       result.x += xvimagesink->render_rect.x;
2062       result.y += xvimagesink->render_rect.y;
2063     } else {
2064       memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2065     }
2066
2067     g_mutex_unlock (xvimagesink->flow_lock);
2068
2069     /* We calculate scaling using the original video frames geometry to include
2070        pixel aspect ratio scaling. */
2071     xscale = (gdouble) xvimagesink->video_width / result.w;
2072     yscale = (gdouble) xvimagesink->video_height / result.h;
2073
2074     /* Converting pointer coordinates to the non scaled geometry */
2075     if (gst_structure_get_double (structure, "pointer_x", &x)) {
2076       x = MIN (x, result.x + result.w);
2077       x = MAX (x - result.x, 0);
2078       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2079           (gdouble) x * xscale, NULL);
2080     }
2081     if (gst_structure_get_double (structure, "pointer_y", &y)) {
2082       y = MIN (y, result.y + result.h);
2083       y = MAX (y - result.y, 0);
2084       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2085           (gdouble) y * yscale, NULL);
2086     }
2087
2088     gst_pad_send_event (peer, event);
2089     gst_object_unref (peer);
2090   }
2091 }
2092
2093 static void
2094 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2095 {
2096   iface->send_event = gst_xvimagesink_navigation_send_event;
2097 }
2098
2099 static void
2100 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2101 {
2102   XID xwindow_id = id;
2103   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2104   GstXWindow *xwindow = NULL;
2105
2106   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2107
2108   g_mutex_lock (xvimagesink->flow_lock);
2109
2110   /* If we already use that window return */
2111   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2112     g_mutex_unlock (xvimagesink->flow_lock);
2113     return;
2114   }
2115
2116   /* If the element has not initialized the X11 context try to do so */
2117   if (!xvimagesink->xcontext &&
2118       !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2119     g_mutex_unlock (xvimagesink->flow_lock);
2120     /* we have thrown a GST_ELEMENT_ERROR now */
2121     return;
2122   }
2123
2124   gst_xvimagesink_update_colorbalance (xvimagesink);
2125
2126   /* If a window is there already we destroy it */
2127   if (xvimagesink->xwindow) {
2128     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2129     xvimagesink->xwindow = NULL;
2130   }
2131
2132   /* If the xid is 0 we go back to an internal window */
2133   if (xwindow_id == 0) {
2134     /* If no width/height caps nego did not happen window will be created
2135        during caps nego then */
2136     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2137         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2138       xwindow =
2139           gst_xvimagesink_xwindow_new (xvimagesink,
2140           GST_VIDEO_SINK_WIDTH (xvimagesink),
2141           GST_VIDEO_SINK_HEIGHT (xvimagesink));
2142     }
2143   } else {
2144     XWindowAttributes attr;
2145
2146     xwindow = g_new0 (GstXWindow, 1);
2147     xwindow->win = xwindow_id;
2148
2149     /* Set the event we want to receive and create a GC */
2150     g_mutex_lock (xvimagesink->x_lock);
2151
2152     XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2153
2154     xwindow->width = attr.width;
2155     xwindow->height = attr.height;
2156     xwindow->internal = FALSE;
2157     if (!xvimagesink->have_render_rect) {
2158       xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2159       xvimagesink->render_rect.w = attr.width;
2160       xvimagesink->render_rect.h = attr.height;
2161     }
2162     if (xvimagesink->handle_events) {
2163       XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2164           StructureNotifyMask | PointerMotionMask | KeyPressMask |
2165           KeyReleaseMask);
2166     }
2167
2168     xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2169         xwindow->win, 0, NULL);
2170     g_mutex_unlock (xvimagesink->x_lock);
2171   }
2172
2173   if (xwindow)
2174     xvimagesink->xwindow = xwindow;
2175
2176   g_mutex_unlock (xvimagesink->flow_lock);
2177 }
2178
2179 static void
2180 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2181 {
2182   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2183
2184   GST_DEBUG ("doing expose");
2185   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2186   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2187 }
2188
2189 static void
2190 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2191     gboolean handle_events)
2192 {
2193   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2194
2195   xvimagesink->handle_events = handle_events;
2196
2197   g_mutex_lock (xvimagesink->flow_lock);
2198
2199   if (G_UNLIKELY (!xvimagesink->xwindow)) {
2200     g_mutex_unlock (xvimagesink->flow_lock);
2201     return;
2202   }
2203
2204   g_mutex_lock (xvimagesink->x_lock);
2205
2206   if (handle_events) {
2207     if (xvimagesink->xwindow->internal) {
2208       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2209           ExposureMask | StructureNotifyMask | PointerMotionMask |
2210           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2211     } else {
2212       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2213           ExposureMask | StructureNotifyMask | PointerMotionMask |
2214           KeyPressMask | KeyReleaseMask);
2215     }
2216   } else {
2217     XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2218   }
2219
2220   g_mutex_unlock (xvimagesink->x_lock);
2221
2222   g_mutex_unlock (xvimagesink->flow_lock);
2223 }
2224
2225 static void
2226 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2227     gint width, gint height)
2228 {
2229   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2230
2231   /* FIXME: how about some locking? */
2232   if (width >= 0 && height >= 0) {
2233     xvimagesink->render_rect.x = x;
2234     xvimagesink->render_rect.y = y;
2235     xvimagesink->render_rect.w = width;
2236     xvimagesink->render_rect.h = height;
2237     xvimagesink->have_render_rect = TRUE;
2238   } else {
2239     xvimagesink->render_rect.x = 0;
2240     xvimagesink->render_rect.y = 0;
2241     xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2242     xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2243     xvimagesink->have_render_rect = FALSE;
2244   }
2245 }
2246
2247 static void
2248 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2249 {
2250   iface->set_window_handle = gst_xvimagesink_set_window_handle;
2251   iface->expose = gst_xvimagesink_expose;
2252   iface->handle_events = gst_xvimagesink_set_event_handling;
2253   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2254 }
2255
2256 static const GList *
2257 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2258 {
2259   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2260
2261   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2262
2263   if (xvimagesink->xcontext)
2264     return xvimagesink->xcontext->channels_list;
2265   else
2266     return NULL;
2267 }
2268
2269 static void
2270 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2271     GstColorBalanceChannel * channel, gint value)
2272 {
2273   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2274
2275   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2276   g_return_if_fail (channel->label != NULL);
2277
2278   xvimagesink->cb_changed = TRUE;
2279
2280   /* Normalize val to [-1000, 1000] */
2281   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2282       (double) (channel->max_value - channel->min_value));
2283
2284   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2285     xvimagesink->hue = value;
2286   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2287     xvimagesink->saturation = value;
2288   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2289     xvimagesink->contrast = value;
2290   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2291     xvimagesink->brightness = value;
2292   } else {
2293     g_warning ("got an unknown channel %s", channel->label);
2294     return;
2295   }
2296
2297   gst_xvimagesink_update_colorbalance (xvimagesink);
2298 }
2299
2300 static gint
2301 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2302     GstColorBalanceChannel * channel)
2303 {
2304   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2305   gint value = 0;
2306
2307   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2308   g_return_val_if_fail (channel->label != NULL, 0);
2309
2310   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2311     value = xvimagesink->hue;
2312   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2313     value = xvimagesink->saturation;
2314   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2315     value = xvimagesink->contrast;
2316   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2317     value = xvimagesink->brightness;
2318   } else {
2319     g_warning ("got an unknown channel %s", channel->label);
2320   }
2321
2322   /* Normalize val to [channel->min_value, channel->max_value] */
2323   value = channel->min_value + (channel->max_value - channel->min_value) *
2324       (value + 1000) / 2000;
2325
2326   return value;
2327 }
2328
2329 static GstColorBalanceType
2330 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2331 {
2332   return GST_COLOR_BALANCE_HARDWARE;
2333 }
2334
2335 static void
2336 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2337 {
2338   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2339   iface->set_value = gst_xvimagesink_colorbalance_set_value;
2340   iface->get_value = gst_xvimagesink_colorbalance_get_value;
2341   iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2342 }
2343
2344 #if 0
2345 static const GList *
2346 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2347 {
2348   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2349   static GList *list = NULL;
2350
2351   if (!list) {
2352     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2353     list =
2354         g_list_append (list, g_object_class_find_property (klass,
2355             "autopaint-colorkey"));
2356     list =
2357         g_list_append (list, g_object_class_find_property (klass,
2358             "double-buffer"));
2359     list =
2360         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2361   }
2362
2363   return list;
2364 }
2365
2366 static void
2367 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2368     guint prop_id, const GParamSpec * pspec)
2369 {
2370   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2371
2372   switch (prop_id) {
2373     case PROP_DEVICE:
2374     case PROP_AUTOPAINT_COLORKEY:
2375     case PROP_DOUBLE_BUFFER:
2376     case PROP_COLORKEY:
2377       GST_DEBUG_OBJECT (xvimagesink,
2378           "probing device list and get capabilities");
2379       if (!xvimagesink->xcontext) {
2380         GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2381         xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2382       }
2383       break;
2384     default:
2385       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2386       break;
2387   }
2388 }
2389
2390 static gboolean
2391 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2392     guint prop_id, const GParamSpec * pspec)
2393 {
2394   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2395   gboolean ret = FALSE;
2396
2397   switch (prop_id) {
2398     case PROP_DEVICE:
2399     case PROP_AUTOPAINT_COLORKEY:
2400     case PROP_DOUBLE_BUFFER:
2401     case PROP_COLORKEY:
2402       if (xvimagesink->xcontext != NULL) {
2403         ret = FALSE;
2404       } else {
2405         ret = TRUE;
2406       }
2407       break;
2408     default:
2409       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2410       break;
2411   }
2412
2413   return ret;
2414 }
2415
2416 static GValueArray *
2417 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2418     guint prop_id, const GParamSpec * pspec)
2419 {
2420   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2421   GValueArray *array = NULL;
2422
2423   if (G_UNLIKELY (!xvimagesink->xcontext)) {
2424     GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2425         "get values");
2426     goto beach;
2427   }
2428
2429   switch (prop_id) {
2430     case PROP_DEVICE:
2431     {
2432       guint i;
2433       GValue value = { 0 };
2434
2435       array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2436       g_value_init (&value, G_TYPE_STRING);
2437
2438       for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2439         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2440
2441         g_value_set_string (&value, adaptor_id_s);
2442         g_value_array_append (array, &value);
2443         g_free (adaptor_id_s);
2444       }
2445       g_value_unset (&value);
2446       break;
2447     }
2448     case PROP_AUTOPAINT_COLORKEY:
2449       if (xvimagesink->have_autopaint_colorkey) {
2450         GValue value = { 0 };
2451
2452         array = g_value_array_new (2);
2453         g_value_init (&value, G_TYPE_BOOLEAN);
2454         g_value_set_boolean (&value, FALSE);
2455         g_value_array_append (array, &value);
2456         g_value_set_boolean (&value, TRUE);
2457         g_value_array_append (array, &value);
2458         g_value_unset (&value);
2459       }
2460       break;
2461     case PROP_DOUBLE_BUFFER:
2462       if (xvimagesink->have_double_buffer) {
2463         GValue value = { 0 };
2464
2465         array = g_value_array_new (2);
2466         g_value_init (&value, G_TYPE_BOOLEAN);
2467         g_value_set_boolean (&value, FALSE);
2468         g_value_array_append (array, &value);
2469         g_value_set_boolean (&value, TRUE);
2470         g_value_array_append (array, &value);
2471         g_value_unset (&value);
2472       }
2473       break;
2474     case PROP_COLORKEY:
2475       if (xvimagesink->have_colorkey) {
2476         GValue value = { 0 };
2477
2478         array = g_value_array_new (1);
2479         g_value_init (&value, GST_TYPE_INT_RANGE);
2480         gst_value_set_int_range (&value, 0, 0xffffff);
2481         g_value_array_append (array, &value);
2482         g_value_unset (&value);
2483       }
2484       break;
2485     default:
2486       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2487       break;
2488   }
2489
2490 beach:
2491   return array;
2492 }
2493
2494 static void
2495 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2496     iface)
2497 {
2498   iface->get_properties = gst_xvimagesink_probe_get_properties;
2499   iface->probe_property = gst_xvimagesink_probe_probe_property;
2500   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2501   iface->get_values = gst_xvimagesink_probe_get_values;
2502 }
2503 #endif
2504
2505 /* =========================================== */
2506 /*                                             */
2507 /*              Init & Class init              */
2508 /*                                             */
2509 /* =========================================== */
2510
2511 static void
2512 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2513     const GValue * value, GParamSpec * pspec)
2514 {
2515   GstXvImageSink *xvimagesink;
2516
2517   g_return_if_fail (GST_IS_XVIMAGESINK (object));
2518
2519   xvimagesink = GST_XVIMAGESINK (object);
2520
2521   switch (prop_id) {
2522     case PROP_HUE:
2523       xvimagesink->hue = g_value_get_int (value);
2524       xvimagesink->cb_changed = TRUE;
2525       gst_xvimagesink_update_colorbalance (xvimagesink);
2526       break;
2527     case PROP_CONTRAST:
2528       xvimagesink->contrast = g_value_get_int (value);
2529       xvimagesink->cb_changed = TRUE;
2530       gst_xvimagesink_update_colorbalance (xvimagesink);
2531       break;
2532     case PROP_BRIGHTNESS:
2533       xvimagesink->brightness = g_value_get_int (value);
2534       xvimagesink->cb_changed = TRUE;
2535       gst_xvimagesink_update_colorbalance (xvimagesink);
2536       break;
2537     case PROP_SATURATION:
2538       xvimagesink->saturation = g_value_get_int (value);
2539       xvimagesink->cb_changed = TRUE;
2540       gst_xvimagesink_update_colorbalance (xvimagesink);
2541       break;
2542     case PROP_DISPLAY:
2543       xvimagesink->display_name = g_strdup (g_value_get_string (value));
2544       break;
2545     case PROP_SYNCHRONOUS:
2546       xvimagesink->synchronous = g_value_get_boolean (value);
2547       if (xvimagesink->xcontext) {
2548         XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2549         GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2550             xvimagesink->synchronous ? "TRUE" : "FALSE");
2551       }
2552       break;
2553     case PROP_PIXEL_ASPECT_RATIO:
2554       g_free (xvimagesink->par);
2555       xvimagesink->par = g_new0 (GValue, 1);
2556       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2557       if (!g_value_transform (value, xvimagesink->par)) {
2558         g_warning ("Could not transform string to aspect ratio");
2559         gst_value_set_fraction (xvimagesink->par, 1, 1);
2560       }
2561       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2562           gst_value_get_fraction_numerator (xvimagesink->par),
2563           gst_value_get_fraction_denominator (xvimagesink->par));
2564       break;
2565     case PROP_FORCE_ASPECT_RATIO:
2566       xvimagesink->keep_aspect = g_value_get_boolean (value);
2567       break;
2568     case PROP_HANDLE_EVENTS:
2569       gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2570           g_value_get_boolean (value));
2571       gst_xvimagesink_manage_event_thread (xvimagesink);
2572       break;
2573     case PROP_DEVICE:
2574       xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2575       break;
2576     case PROP_HANDLE_EXPOSE:
2577       xvimagesink->handle_expose = g_value_get_boolean (value);
2578       gst_xvimagesink_manage_event_thread (xvimagesink);
2579       break;
2580     case PROP_DOUBLE_BUFFER:
2581       xvimagesink->double_buffer = g_value_get_boolean (value);
2582       break;
2583     case PROP_AUTOPAINT_COLORKEY:
2584       xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2585       break;
2586     case PROP_COLORKEY:
2587       xvimagesink->colorkey = g_value_get_int (value);
2588       break;
2589     case PROP_DRAW_BORDERS:
2590       xvimagesink->draw_borders = g_value_get_boolean (value);
2591       break;
2592     default:
2593       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2594       break;
2595   }
2596 }
2597
2598 static void
2599 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2600     GValue * value, GParamSpec * pspec)
2601 {
2602   GstXvImageSink *xvimagesink;
2603
2604   g_return_if_fail (GST_IS_XVIMAGESINK (object));
2605
2606   xvimagesink = GST_XVIMAGESINK (object);
2607
2608   switch (prop_id) {
2609     case PROP_HUE:
2610       g_value_set_int (value, xvimagesink->hue);
2611       break;
2612     case PROP_CONTRAST:
2613       g_value_set_int (value, xvimagesink->contrast);
2614       break;
2615     case PROP_BRIGHTNESS:
2616       g_value_set_int (value, xvimagesink->brightness);
2617       break;
2618     case PROP_SATURATION:
2619       g_value_set_int (value, xvimagesink->saturation);
2620       break;
2621     case PROP_DISPLAY:
2622       g_value_set_string (value, xvimagesink->display_name);
2623       break;
2624     case PROP_SYNCHRONOUS:
2625       g_value_set_boolean (value, xvimagesink->synchronous);
2626       break;
2627     case PROP_PIXEL_ASPECT_RATIO:
2628       if (xvimagesink->par)
2629         g_value_transform (xvimagesink->par, value);
2630       break;
2631     case PROP_FORCE_ASPECT_RATIO:
2632       g_value_set_boolean (value, xvimagesink->keep_aspect);
2633       break;
2634     case PROP_HANDLE_EVENTS:
2635       g_value_set_boolean (value, xvimagesink->handle_events);
2636       break;
2637     case PROP_DEVICE:
2638     {
2639       char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2640
2641       g_value_set_string (value, adaptor_no_s);
2642       g_free (adaptor_no_s);
2643       break;
2644     }
2645     case PROP_DEVICE_NAME:
2646       if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2647         g_value_set_string (value,
2648             xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2649       } else {
2650         g_value_set_string (value, NULL);
2651       }
2652       break;
2653     case PROP_HANDLE_EXPOSE:
2654       g_value_set_boolean (value, xvimagesink->handle_expose);
2655       break;
2656     case PROP_DOUBLE_BUFFER:
2657       g_value_set_boolean (value, xvimagesink->double_buffer);
2658       break;
2659     case PROP_AUTOPAINT_COLORKEY:
2660       g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2661       break;
2662     case PROP_COLORKEY:
2663       g_value_set_int (value, xvimagesink->colorkey);
2664       break;
2665     case PROP_DRAW_BORDERS:
2666       g_value_set_boolean (value, xvimagesink->draw_borders);
2667       break;
2668     case PROP_WINDOW_WIDTH:
2669       if (xvimagesink->xwindow)
2670         g_value_set_uint64 (value, xvimagesink->xwindow->width);
2671       else
2672         g_value_set_uint64 (value, 0);
2673       break;
2674     case PROP_WINDOW_HEIGHT:
2675       if (xvimagesink->xwindow)
2676         g_value_set_uint64 (value, xvimagesink->xwindow->height);
2677       else
2678         g_value_set_uint64 (value, 0);
2679       break;
2680     default:
2681       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2682       break;
2683   }
2684 }
2685
2686 static void
2687 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2688 {
2689   GThread *thread;
2690
2691   GST_OBJECT_LOCK (xvimagesink);
2692   xvimagesink->running = FALSE;
2693   /* grab thread and mark it as NULL */
2694   thread = xvimagesink->event_thread;
2695   xvimagesink->event_thread = NULL;
2696   GST_OBJECT_UNLOCK (xvimagesink);
2697
2698   /* Wait for our event thread to finish before we clean up our stuff. */
2699   if (thread)
2700     g_thread_join (thread);
2701
2702   if (xvimagesink->cur_image) {
2703     gst_buffer_unref (xvimagesink->cur_image);
2704     xvimagesink->cur_image = NULL;
2705   }
2706
2707   g_mutex_lock (xvimagesink->flow_lock);
2708
2709   if (xvimagesink->pool) {
2710     gst_object_unref (xvimagesink->pool);
2711     xvimagesink->pool = NULL;
2712   }
2713
2714   if (xvimagesink->xwindow) {
2715     gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2716     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2717     xvimagesink->xwindow = NULL;
2718   }
2719   g_mutex_unlock (xvimagesink->flow_lock);
2720
2721   xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2722       xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2723   xvimagesink->have_render_rect = FALSE;
2724
2725   gst_xvimagesink_xcontext_clear (xvimagesink);
2726 }
2727
2728 /* Finalize is called only once, dispose can be called multiple times.
2729  * We use mutexes and don't reset stuff to NULL here so let's register
2730  * as a finalize. */
2731 static void
2732 gst_xvimagesink_finalize (GObject * object)
2733 {
2734   GstXvImageSink *xvimagesink;
2735
2736   xvimagesink = GST_XVIMAGESINK (object);
2737
2738   gst_xvimagesink_reset (xvimagesink);
2739
2740   if (xvimagesink->display_name) {
2741     g_free (xvimagesink->display_name);
2742     xvimagesink->display_name = NULL;
2743   }
2744
2745   if (xvimagesink->par) {
2746     g_free (xvimagesink->par);
2747     xvimagesink->par = NULL;
2748   }
2749   if (xvimagesink->x_lock) {
2750     g_mutex_free (xvimagesink->x_lock);
2751     xvimagesink->x_lock = NULL;
2752   }
2753   if (xvimagesink->flow_lock) {
2754     g_mutex_free (xvimagesink->flow_lock);
2755     xvimagesink->flow_lock = NULL;
2756   }
2757
2758   g_free (xvimagesink->media_title);
2759
2760   G_OBJECT_CLASS (parent_class)->finalize (object);
2761 }
2762
2763 static void
2764 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2765 {
2766   xvimagesink->display_name = NULL;
2767   xvimagesink->adaptor_no = 0;
2768   xvimagesink->xcontext = NULL;
2769   xvimagesink->xwindow = NULL;
2770   xvimagesink->cur_image = NULL;
2771
2772   xvimagesink->hue = xvimagesink->saturation = 0;
2773   xvimagesink->contrast = xvimagesink->brightness = 0;
2774   xvimagesink->cb_changed = FALSE;
2775
2776   xvimagesink->fps_n = 0;
2777   xvimagesink->fps_d = 0;
2778   xvimagesink->video_width = 0;
2779   xvimagesink->video_height = 0;
2780
2781   xvimagesink->x_lock = g_mutex_new ();
2782   xvimagesink->flow_lock = g_mutex_new ();
2783
2784   xvimagesink->pool = NULL;
2785
2786   xvimagesink->synchronous = FALSE;
2787   xvimagesink->double_buffer = TRUE;
2788   xvimagesink->running = FALSE;
2789   xvimagesink->keep_aspect = TRUE;
2790   xvimagesink->handle_events = TRUE;
2791   xvimagesink->par = NULL;
2792   xvimagesink->handle_expose = TRUE;
2793   xvimagesink->autopaint_colorkey = TRUE;
2794
2795   /* on 16bit displays this becomes r,g,b = 1,2,3
2796    * on 24bit displays this becomes r,g,b = 8,8,16
2797    * as a port atom value
2798    */
2799   xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2800   xvimagesink->draw_borders = TRUE;
2801 }
2802
2803 static void
2804 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2805 {
2806   GObjectClass *gobject_class;
2807   GstElementClass *gstelement_class;
2808   GstBaseSinkClass *gstbasesink_class;
2809   GstVideoSinkClass *videosink_class;
2810
2811   gobject_class = (GObjectClass *) klass;
2812   gstelement_class = (GstElementClass *) klass;
2813   gstbasesink_class = (GstBaseSinkClass *) klass;
2814   videosink_class = (GstVideoSinkClass *) klass;
2815
2816   parent_class = g_type_class_peek_parent (klass);
2817
2818   gobject_class->set_property = gst_xvimagesink_set_property;
2819   gobject_class->get_property = gst_xvimagesink_get_property;
2820
2821   g_object_class_install_property (gobject_class, PROP_CONTRAST,
2822       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2823           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2825       g_param_spec_int ("brightness", "Brightness",
2826           "The brightness of the video", -1000, 1000, 0,
2827           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2828   g_object_class_install_property (gobject_class, PROP_HUE,
2829       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2830           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831   g_object_class_install_property (gobject_class, PROP_SATURATION,
2832       g_param_spec_int ("saturation", "Saturation",
2833           "The saturation of the video", -1000, 1000, 0,
2834           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2835   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2836       g_param_spec_string ("display", "Display", "X Display name", NULL,
2837           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2838   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2839       g_param_spec_boolean ("synchronous", "Synchronous",
2840           "When enabled, runs the X display in synchronous mode. "
2841           "(unrelated to A/V sync, used only for debugging)", FALSE,
2842           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2844       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2845           "The pixel aspect ratio of the device", "1/1",
2846           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2847   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2848       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2849           "When enabled, scaling will respect original aspect ratio", TRUE,
2850           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2851   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2852       g_param_spec_boolean ("handle-events", "Handle XEvents",
2853           "When enabled, XEvents will be selected and handled", TRUE,
2854           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2855   g_object_class_install_property (gobject_class, PROP_DEVICE,
2856       g_param_spec_string ("device", "Adaptor number",
2857           "The number of the video adaptor", "0",
2858           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2859   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2860       g_param_spec_string ("device-name", "Adaptor name",
2861           "The name of the video adaptor", NULL,
2862           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2863   /**
2864    * GstXvImageSink:handle-expose
2865    *
2866    * When enabled, the current frame will always be drawn in response to X
2867    * Expose.
2868    *
2869    * Since: 0.10.14
2870    */
2871   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2872       g_param_spec_boolean ("handle-expose", "Handle expose",
2873           "When enabled, "
2874           "the current frame will always be drawn in response to X Expose "
2875           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2876
2877   /**
2878    * GstXvImageSink:double-buffer
2879    *
2880    * Whether to double-buffer the output.
2881    *
2882    * Since: 0.10.14
2883    */
2884   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2885       g_param_spec_boolean ("double-buffer", "Double-buffer",
2886           "Whether to double-buffer the output", TRUE,
2887           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2888   /**
2889    * GstXvImageSink:autopaint-colorkey
2890    *
2891    * Whether to autofill overlay with colorkey
2892    *
2893    * Since: 0.10.21
2894    */
2895   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2896       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2897           "Whether to autofill overlay with colorkey", TRUE,
2898           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2899   /**
2900    * GstXvImageSink:colorkey
2901    *
2902    * Color to use for the overlay mask.
2903    *
2904    * Since: 0.10.21
2905    */
2906   g_object_class_install_property (gobject_class, PROP_COLORKEY,
2907       g_param_spec_int ("colorkey", "Colorkey",
2908           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2909           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2910
2911   /**
2912    * GstXvImageSink:draw-borders
2913    *
2914    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2915    * unused parts of the video area.
2916    *
2917    * Since: 0.10.21
2918    */
2919   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2920       g_param_spec_boolean ("draw-borders", "Colorkey",
2921           "Draw black borders to fill unused area in force-aspect-ratio mode",
2922           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2923
2924   /**
2925    * GstXvImageSink:window-width
2926    *
2927    * Actual width of the video window.
2928    *
2929    * Since: 0.10.32
2930    */
2931   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2932       g_param_spec_uint64 ("window-width", "window-width",
2933           "Width of the window", 0, G_MAXUINT64, 0,
2934           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2935
2936   /**
2937    * GstXvImageSink:window-height
2938    *
2939    * Actual height of the video window.
2940    *
2941    * Since: 0.10.32
2942    */
2943   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2944       g_param_spec_uint64 ("window-height", "window-height",
2945           "Height of the window", 0, G_MAXUINT64, 0,
2946           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2947
2948   gobject_class->finalize = gst_xvimagesink_finalize;
2949
2950   gst_element_class_set_static_metadata (gstelement_class,
2951       "Video sink", "Sink/Video",
2952       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2953
2954   gst_element_class_add_pad_template (gstelement_class,
2955       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2956
2957   gstelement_class->change_state =
2958       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2959
2960   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2961   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2962   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2963   gstbasesink_class->propose_allocation =
2964       GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2965   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2966
2967   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
2968 }