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