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