remove buffer_alloc
[platform/upstream/gstreamer.git] / sys / ximage / ximagesink.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-ximagesink
22  *
23  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24  * display. This element can receive a Window ID from the application through
25  * the XOverlay interface and will then render video frames in this drawable.
26  * If no Window ID was provided by the application, the element will create its
27  * own internal window and render into it.
28  *
29  * <refsect2>
30  * <title>Scaling</title>
31  * <para>
32  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33  * reverse caps negotiation to try to get scaled video frames for the drawable.
34  * This is accomplished by asking the peer pad if it accepts some different caps
35  * which in most cases implies that there is a scaling element in the pipeline,
36  * or that an element generating the video frames can generate them with a 
37  * different geometry. This mechanism is handled during buffer allocations, for
38  * each allocation request the video sink will check the drawable geometry, look
39  * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40  * desired video frames and then check that the peer pad accept those new caps.
41  * If it does it will then allocate a buffer in video memory with this new
42  * geometry and return it with the new caps.
43  * </para>
44  * </refsect2>
45  * <refsect2>
46  * <title>Events</title>
47  * <para>
48  * XImageSink creates a thread to handle events coming from the drawable. There
49  * are several kind of events that can be grouped in 2 big categories: input 
50  * events and window state related events. Input events will be translated to
51  * navigation events and pushed upstream for other elements to react on them.
52  * This includes events such as pointer moves, key press/release, clicks etc...
53  * Other events are used to handle the drawable appearance even when the data
54  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55  * paused, it will receive expose events from the drawable and draw the latest
56  * frame with correct borders/aspect-ratio.
57  * </para>
58  * </refsect2>
59  * <refsect2>
60  * <title>Pixel aspect ratio</title>
61  * <para>
62  * When changing state to GST_STATE_READY, XImageSink will open a connection to
63  * the display specified in the #GstXImageSink:display property or the default
64  * display if nothing specified. Once this connection is open it will inspect 
65  * the display configuration including the physical display geometry and 
66  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67  * video sink will set the calculated pixel aspect ratio on the caps to make 
68  * sure that incoming video frames will have the correct pixel aspect ratio for
69  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70  * then possible to enforce a specific pixel aspect ratio using the
71  * #GstXImageSink:pixel-aspect-ratio property.
72  * </para>
73  * </refsect2>
74  * <refsect2>
75  * <title>Examples</title>
76  * |[
77  * gst-launch -v videotestsrc ! queue ! ximagesink
78  * ]| A pipeline to test reverse negotiation. When the test video signal appears
79  * you can resize the window and see that scaled buffers of the desired size are
80  * going to arrive with a short delay. This illustrates how buffers of desired
81  * size are allocated along the way. If you take away the queue, scaling will
82  * happen almost immediately.
83  * |[
84  * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85  * ]| A pipeline to test navigation events.
86  * While moving the mouse pointer over the test signal you will see a black box
87  * following the mouse pointer. If you press the mouse button somewhere on the 
88  * video and release it somewhere else a green box will appear where you pressed
89  * the button and a red one where you released it. (The navigationtest element
90  * is part of gst-plugins-good.)
91  * |[
92  * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94  * videotestsrc, in most cases the pixel aspect ratio of the display will be
95  * 1/1. This means that videoscale will have to do the scaling to convert 
96  * incoming frames to a size that will match the display pixel aspect ratio
97  * (from 320x240 to 320x180 in this case). Note that you might have to escape 
98  * some characters for your shell like '\(fraction\)'.
99  * </refsect2>
100  */
101
102 #ifdef HAVE_CONFIG_H
103 #include "config.h"
104 #endif
105
106 /* Our interfaces */
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
109
110 /* Object header */
111 #include "ximagesink.h"
112
113 /* Debugging category */
114 #include <gst/gstinfo.h>
115
116 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117 #define GST_CAT_DEFAULT gst_debug_ximagesink
118
119 typedef struct
120 {
121   unsigned long flags;
122   unsigned long functions;
123   unsigned long decorations;
124   long input_mode;
125   unsigned long status;
126 }
127 MotifWmHints, MwmHints;
128
129 #define MWM_HINTS_DECORATIONS   (1L << 1)
130
131 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
133 static void gst_ximagesink_expose (GstXOverlay * overlay);
134
135 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
136 GST_STATIC_PAD_TEMPLATE ("sink",
137     GST_PAD_SINK,
138     GST_PAD_ALWAYS,
139     GST_STATIC_CAPS ("video/x-raw-rgb, "
140         "framerate = (fraction) [ 0, MAX ], "
141         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
142     );
143
144 enum
145 {
146   PROP_0,
147   PROP_DISPLAY,
148   PROP_SYNCHRONOUS,
149   PROP_PIXEL_ASPECT_RATIO,
150   PROP_FORCE_ASPECT_RATIO,
151   PROP_HANDLE_EVENTS,
152   PROP_HANDLE_EXPOSE,
153   PROP_WINDOW_WIDTH,
154   PROP_WINDOW_HEIGHT
155 };
156
157 /* ============================================================= */
158 /*                                                               */
159 /*                       Public Methods                          */
160 /*                                                               */
161 /* ============================================================= */
162
163 /* =========================================== */
164 /*                                             */
165 /*          Object typing & Creation           */
166 /*                                             */
167 /* =========================================== */
168 static void gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass);
169 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
170 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
171 #define gst_ximagesink_parent_class parent_class
172 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
173     G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
174         gst_ximagesink_interface_init);
175     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
176     G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
177
178 /* ============================================================= */
179 /*                                                               */
180 /*                       Private Methods                         */
181 /*                                                               */
182 /* ============================================================= */
183
184 /* X11 stuff */
185
186 /* We are called with the x_lock taken */
187 static void
188 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
189     GstXWindow * xwindow, GstVideoRectangle rect)
190 {
191   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
192   g_return_if_fail (xwindow != NULL);
193
194   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
195       ximagesink->xcontext->black);
196
197   /* Left border */
198   if (rect.x > 0) {
199     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
200         0, 0, rect.x, xwindow->height);
201   }
202
203   /* Right border */
204   if ((rect.x + rect.w) < xwindow->width) {
205     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
206         rect.x + rect.w, 0, xwindow->width, xwindow->height);
207   }
208
209   /* Top border */
210   if (rect.y > 0) {
211     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
212         0, 0, xwindow->width, rect.y);
213   }
214
215   /* Bottom border */
216   if ((rect.y + rect.h) < xwindow->height) {
217     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
218         0, rect.y + rect.h, xwindow->width, xwindow->height);
219   }
220 }
221
222 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
223 static gboolean
224 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
225 {
226   GstMetaXImage *meta;
227   GstVideoRectangle src, dst, result;
228   gboolean draw_border = FALSE;
229
230   /* We take the flow_lock. If expose is in there we don't want to run
231      concurrently from the data flow thread */
232   g_mutex_lock (ximagesink->flow_lock);
233
234   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
235     g_mutex_unlock (ximagesink->flow_lock);
236     return FALSE;
237   }
238
239   /* Draw borders when displaying the first frame. After this
240      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
241   if (!ximagesink->cur_image || ximagesink->draw_border) {
242     draw_border = TRUE;
243   }
244
245   /* Store a reference to the last image we put, lose the previous one */
246   if (ximage && ximagesink->cur_image != ximage) {
247     if (ximagesink->cur_image) {
248       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
249       gst_buffer_unref (ximagesink->cur_image);
250     }
251     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
252     ximagesink->cur_image = gst_buffer_ref (ximage);
253   }
254
255   /* Expose sends a NULL image, we take the latest frame */
256   if (!ximage) {
257     draw_border = TRUE;
258     if (ximagesink->cur_image) {
259       ximage = ximagesink->cur_image;
260     } else {
261       g_mutex_unlock (ximagesink->flow_lock);
262       return TRUE;
263     }
264   }
265
266   meta = gst_buffer_get_meta_ximage (ximage);
267
268   src.w = meta->width;
269   src.h = meta->height;
270   dst.w = ximagesink->xwindow->width;
271   dst.h = ximagesink->xwindow->height;
272
273   gst_video_sink_center_rect (src, dst, &result, FALSE);
274
275   g_mutex_lock (ximagesink->x_lock);
276
277   if (draw_border) {
278     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
279         result);
280     ximagesink->draw_border = FALSE;
281   }
282 #ifdef HAVE_XSHM
283   if (ximagesink->xcontext->use_xshm) {
284     GST_LOG_OBJECT (ximagesink,
285         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
286         ximage, 0, 0, result.x, result.y, result.w, result.h,
287         ximagesink->xwindow->width, ximagesink->xwindow->height);
288     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
289         ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
290         result.w, result.h, FALSE);
291   } else
292 #endif /* HAVE_XSHM */
293   {
294     GST_LOG_OBJECT (ximagesink,
295         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
296         ximage, 0, 0, result.x, result.y, result.w, result.h,
297         ximagesink->xwindow->width, ximagesink->xwindow->height);
298     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
299         ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
300         result.w, result.h);
301   }
302
303   XSync (ximagesink->xcontext->disp, FALSE);
304
305   g_mutex_unlock (ximagesink->x_lock);
306
307   g_mutex_unlock (ximagesink->flow_lock);
308
309   return TRUE;
310 }
311
312 static gboolean
313 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
314     GstXWindow * window)
315 {
316   Atom hints_atom = None;
317   MotifWmHints *hints;
318
319   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
320   g_return_val_if_fail (window != NULL, FALSE);
321
322   g_mutex_lock (ximagesink->x_lock);
323
324   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
325       True);
326   if (hints_atom == None) {
327     g_mutex_unlock (ximagesink->x_lock);
328     return FALSE;
329   }
330
331   hints = g_malloc0 (sizeof (MotifWmHints));
332
333   hints->flags |= MWM_HINTS_DECORATIONS;
334   hints->decorations = 1 << 0;
335
336   XChangeProperty (ximagesink->xcontext->disp, window->win,
337       hints_atom, hints_atom, 32, PropModeReplace,
338       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
339
340   XSync (ximagesink->xcontext->disp, FALSE);
341
342   g_mutex_unlock (ximagesink->x_lock);
343
344   g_free (hints);
345
346   return TRUE;
347 }
348
349 static void
350 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
351     GstXWindow * xwindow, const gchar * media_title)
352 {
353   if (media_title) {
354     g_free (ximagesink->media_title);
355     ximagesink->media_title = g_strdup (media_title);
356   }
357   if (xwindow) {
358     /* we have a window */
359     if (xwindow->internal) {
360       XTextProperty xproperty;
361       const gchar *app_name;
362       const gchar *title = NULL;
363       gchar *title_mem = NULL;
364
365       /* set application name as a title */
366       app_name = g_get_application_name ();
367
368       if (app_name && ximagesink->media_title) {
369         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
370             app_name, NULL);
371       } else if (app_name) {
372         title = app_name;
373       } else if (ximagesink->media_title) {
374         title = ximagesink->media_title;
375       }
376
377       if (title) {
378         if ((XStringListToTextProperty (((char **) &title), 1,
379                     &xproperty)) != 0) {
380           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
381           XFree (xproperty.value);
382         }
383
384         g_free (title_mem);
385       }
386     }
387   }
388 }
389
390 /* This function handles a GstXWindow creation */
391 static GstXWindow *
392 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
393 {
394   GstXWindow *xwindow = NULL;
395   XGCValues values;
396
397   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
398
399   xwindow = g_new0 (GstXWindow, 1);
400
401   xwindow->width = width;
402   xwindow->height = height;
403   xwindow->internal = TRUE;
404
405   g_mutex_lock (ximagesink->x_lock);
406
407   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
408       ximagesink->xcontext->root,
409       0, 0, width, height, 0, 0, ximagesink->xcontext->black);
410
411   /* We have to do that to prevent X from redrawing the background on 
412      ConfigureNotify. This takes away flickering of video when resizing. */
413   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
414
415   /* set application name as a title */
416   gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
417
418   if (ximagesink->handle_events) {
419     Atom wm_delete;
420
421     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
422         StructureNotifyMask | PointerMotionMask | KeyPressMask |
423         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
424
425     /* Tell the window manager we'd like delete client messages instead of
426      * being killed */
427     wm_delete = XInternAtom (ximagesink->xcontext->disp,
428         "WM_DELETE_WINDOW", False);
429     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
430         &wm_delete, 1);
431   }
432
433   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
434       0, &values);
435
436   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
437
438   XSync (ximagesink->xcontext->disp, FALSE);
439
440   g_mutex_unlock (ximagesink->x_lock);
441
442   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
443
444   gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
445
446   return xwindow;
447 }
448
449 /* This function destroys a GstXWindow */
450 static void
451 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
452     GstXWindow * xwindow)
453 {
454   g_return_if_fail (xwindow != NULL);
455   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
456
457   g_mutex_lock (ximagesink->x_lock);
458
459   /* If we did not create that window we just free the GC and let it live */
460   if (xwindow->internal)
461     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
462   else
463     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
464
465   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
466
467   XSync (ximagesink->xcontext->disp, FALSE);
468
469   g_mutex_unlock (ximagesink->x_lock);
470
471   g_free (xwindow);
472 }
473
474 static void
475 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
476 {
477   XWindowAttributes attr;
478
479   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
480
481   /* Update the window geometry */
482   g_mutex_lock (ximagesink->x_lock);
483   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
484     g_mutex_unlock (ximagesink->x_lock);
485     return;
486   }
487
488   XGetWindowAttributes (ximagesink->xcontext->disp,
489       ximagesink->xwindow->win, &attr);
490
491   ximagesink->xwindow->width = attr.width;
492   ximagesink->xwindow->height = attr.height;
493
494   g_mutex_unlock (ximagesink->x_lock);
495 }
496
497 static void
498 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
499 {
500   g_return_if_fail (xwindow != NULL);
501   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
502
503   g_mutex_lock (ximagesink->x_lock);
504
505   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
506       ximagesink->xcontext->black);
507
508   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
509       0, 0, xwindow->width, xwindow->height);
510
511   XSync (ximagesink->xcontext->disp, FALSE);
512
513   g_mutex_unlock (ximagesink->x_lock);
514 }
515
516 /* This function handles XEvents that might be in the queue. It generates
517    GstEvent that will be sent upstream in the pipeline to handle interactivity
518    and navigation.*/
519 static void
520 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
521 {
522   XEvent e;
523   guint pointer_x = 0, pointer_y = 0;
524   gboolean pointer_moved = FALSE;
525   gboolean exposed = FALSE, configured = FALSE;
526
527   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
528
529   /* Then we get all pointer motion events, only the last position is
530      interesting. */
531   g_mutex_lock (ximagesink->flow_lock);
532   g_mutex_lock (ximagesink->x_lock);
533   while (XCheckWindowEvent (ximagesink->xcontext->disp,
534           ximagesink->xwindow->win, PointerMotionMask, &e)) {
535     g_mutex_unlock (ximagesink->x_lock);
536     g_mutex_unlock (ximagesink->flow_lock);
537
538     switch (e.type) {
539       case MotionNotify:
540         pointer_x = e.xmotion.x;
541         pointer_y = e.xmotion.y;
542         pointer_moved = TRUE;
543         break;
544       default:
545         break;
546     }
547     g_mutex_lock (ximagesink->flow_lock);
548     g_mutex_lock (ximagesink->x_lock);
549   }
550
551   if (pointer_moved) {
552     g_mutex_unlock (ximagesink->x_lock);
553     g_mutex_unlock (ximagesink->flow_lock);
554
555     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
556         pointer_x, pointer_y);
557     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
558         "mouse-move", 0, pointer_x, pointer_y);
559
560     g_mutex_lock (ximagesink->flow_lock);
561     g_mutex_lock (ximagesink->x_lock);
562   }
563
564   /* We get all remaining events on our window to throw them upstream */
565   while (XCheckWindowEvent (ximagesink->xcontext->disp,
566           ximagesink->xwindow->win,
567           KeyPressMask | KeyReleaseMask |
568           ButtonPressMask | ButtonReleaseMask, &e)) {
569     KeySym keysym;
570
571     /* We lock only for the X function call */
572     g_mutex_unlock (ximagesink->x_lock);
573     g_mutex_unlock (ximagesink->flow_lock);
574
575     switch (e.type) {
576       case ButtonPress:
577         /* Mouse button pressed/released over our window. We send upstream
578            events for interactivity/navigation */
579         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
580             e.xbutton.button, e.xbutton.x, e.xbutton.x);
581         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
582             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
583         break;
584       case ButtonRelease:
585         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
586             e.xbutton.button, e.xbutton.x, e.xbutton.x);
587         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
588             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
589         break;
590       case KeyPress:
591       case KeyRelease:
592         /* Key pressed/released over our window. We send upstream
593            events for interactivity/navigation */
594         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
595             e.xkey.keycode, e.xkey.x, e.xkey.x);
596         g_mutex_lock (ximagesink->x_lock);
597         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
598             e.xkey.keycode, 0);
599         g_mutex_unlock (ximagesink->x_lock);
600         if (keysym != NoSymbol) {
601           char *key_str = NULL;
602
603           g_mutex_lock (ximagesink->x_lock);
604           key_str = XKeysymToString (keysym);
605           g_mutex_unlock (ximagesink->x_lock);
606           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
607               e.type == KeyPress ? "key-press" : "key-release", key_str);
608         } else {
609           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
610               e.type == KeyPress ? "key-press" : "key-release", "unknown");
611         }
612         break;
613       default:
614         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
615             e.type);
616     }
617     g_mutex_lock (ximagesink->flow_lock);
618     g_mutex_lock (ximagesink->x_lock);
619   }
620
621   /* Handle Expose */
622   while (XCheckWindowEvent (ximagesink->xcontext->disp,
623           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
624     switch (e.type) {
625       case Expose:
626         exposed = TRUE;
627         break;
628       case ConfigureNotify:
629         g_mutex_unlock (ximagesink->x_lock);
630         gst_ximagesink_xwindow_update_geometry (ximagesink);
631         g_mutex_lock (ximagesink->x_lock);
632         configured = TRUE;
633         break;
634       default:
635         break;
636     }
637   }
638
639   if (ximagesink->handle_expose && (exposed || configured)) {
640     g_mutex_unlock (ximagesink->x_lock);
641     g_mutex_unlock (ximagesink->flow_lock);
642
643     gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
644
645     g_mutex_lock (ximagesink->flow_lock);
646     g_mutex_lock (ximagesink->x_lock);
647   }
648
649   /* Handle Display events */
650   while (XPending (ximagesink->xcontext->disp)) {
651     XNextEvent (ximagesink->xcontext->disp, &e);
652
653     switch (e.type) {
654       case ClientMessage:{
655         Atom wm_delete;
656
657         wm_delete = XInternAtom (ximagesink->xcontext->disp,
658             "WM_DELETE_WINDOW", False);
659         if (wm_delete == (Atom) e.xclient.data.l[0]) {
660           /* Handle window deletion by posting an error on the bus */
661           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
662               ("Output window was closed"), (NULL));
663
664           g_mutex_unlock (ximagesink->x_lock);
665           gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
666           ximagesink->xwindow = NULL;
667           g_mutex_lock (ximagesink->x_lock);
668         }
669         break;
670       }
671       default:
672         break;
673     }
674   }
675
676   g_mutex_unlock (ximagesink->x_lock);
677   g_mutex_unlock (ximagesink->flow_lock);
678 }
679
680 static gpointer
681 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
682 {
683   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
684
685   GST_OBJECT_LOCK (ximagesink);
686   while (ximagesink->running) {
687     GST_OBJECT_UNLOCK (ximagesink);
688
689     if (ximagesink->xwindow) {
690       gst_ximagesink_handle_xevents (ximagesink);
691     }
692     /* FIXME: do we want to align this with the framerate or anything else? */
693     g_usleep (G_USEC_PER_SEC / 20);
694
695     GST_OBJECT_LOCK (ximagesink);
696   }
697   GST_OBJECT_UNLOCK (ximagesink);
698
699   return NULL;
700 }
701
702 static void
703 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
704 {
705   GThread *thread = NULL;
706
707   /* don't start the thread too early */
708   if (ximagesink->xcontext == NULL) {
709     return;
710   }
711
712   GST_OBJECT_LOCK (ximagesink);
713   if (ximagesink->handle_expose || ximagesink->handle_events) {
714     if (!ximagesink->event_thread) {
715       /* Setup our event listening thread */
716       GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
717           ximagesink->handle_expose, ximagesink->handle_events);
718       ximagesink->running = TRUE;
719       ximagesink->event_thread = g_thread_create (
720           (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
721     }
722   } else {
723     if (ximagesink->event_thread) {
724       GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
725           ximagesink->handle_expose, ximagesink->handle_events);
726       ximagesink->running = FALSE;
727       /* grab thread and mark it as NULL */
728       thread = ximagesink->event_thread;
729       ximagesink->event_thread = NULL;
730     }
731   }
732   GST_OBJECT_UNLOCK (ximagesink);
733
734   /* Wait for our event thread to finish */
735   if (thread)
736     g_thread_join (thread);
737
738 }
739
740
741 /* This function calculates the pixel aspect ratio based on the properties
742  * in the xcontext structure and stores it there. */
743 static void
744 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
745 {
746   static const gint par[][2] = {
747     {1, 1},                     /* regular screen */
748     {16, 15},                   /* PAL TV */
749     {11, 10},                   /* 525 line Rec.601 video */
750     {54, 59},                   /* 625 line Rec.601 video */
751     {64, 45},                   /* 1280x1024 on 16:9 display */
752     {5, 3},                     /* 1280x1024 on 4:3 display */
753     {4, 3}                      /*  800x600 on 16:9 display */
754   };
755   gint i;
756   gint index;
757   gdouble ratio;
758   gdouble delta;
759
760 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
761
762   /* first calculate the "real" ratio based on the X values;
763    * which is the "physical" w/h divided by the w/h in pixels of the display */
764   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
765       / (xcontext->heightmm * xcontext->width);
766
767   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
768    * override here */
769   if (xcontext->width == 720 && xcontext->height == 576) {
770     ratio = 4.0 * 576 / (3.0 * 720);
771   }
772   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
773
774   /* now find the one from par[][2] with the lowest delta to the real one */
775   delta = DELTA (0);
776   index = 0;
777
778   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
779     gdouble this_delta = DELTA (i);
780
781     if (this_delta < delta) {
782       index = i;
783       delta = this_delta;
784     }
785   }
786
787   GST_DEBUG ("Decided on index %d (%d/%d)", index,
788       par[index][0], par[index][1]);
789
790   g_free (xcontext->par);
791   xcontext->par = g_new0 (GValue, 1);
792   g_value_init (xcontext->par, GST_TYPE_FRACTION);
793   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
794   GST_DEBUG ("set xcontext PAR to %d/%d",
795       gst_value_get_fraction_numerator (xcontext->par),
796       gst_value_get_fraction_denominator (xcontext->par));
797 }
798
799 /* This function gets the X Display and global info about it. Everything is
800    stored in our object and will be cleaned when the object is disposed. Note
801    here that caps for supported format are generated without any window or
802    image creation */
803 static GstXContext *
804 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
805 {
806   GstXContext *xcontext = NULL;
807   XPixmapFormatValues *px_formats = NULL;
808   gint nb_formats = 0, i;
809
810   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
811
812   xcontext = g_new0 (GstXContext, 1);
813
814   g_mutex_lock (ximagesink->x_lock);
815
816   xcontext->disp = XOpenDisplay (ximagesink->display_name);
817
818   if (!xcontext->disp) {
819     g_mutex_unlock (ximagesink->x_lock);
820     g_free (xcontext);
821     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
822         ("Could not initialise X output"), ("Could not open display"));
823     return NULL;
824   }
825
826   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
827   xcontext->screen_num = DefaultScreen (xcontext->disp);
828   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
829   xcontext->root = DefaultRootWindow (xcontext->disp);
830   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
831   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
832   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
833
834   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
835   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
836   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
837   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
838
839   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
840       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
841
842   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
843
844   /* We get supported pixmap formats at supported depth */
845   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
846
847   if (!px_formats) {
848     XCloseDisplay (xcontext->disp);
849     g_mutex_unlock (ximagesink->x_lock);
850     g_free (xcontext->par);
851     g_free (xcontext);
852     GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
853         ("Could not get supported pixmap formats"), (NULL));
854     return NULL;
855   }
856
857   /* We get bpp value corresponding to our running depth */
858   for (i = 0; i < nb_formats; i++) {
859     if (px_formats[i].depth == xcontext->depth)
860       xcontext->bpp = px_formats[i].bits_per_pixel;
861   }
862
863   XFree (px_formats);
864
865   xcontext->endianness =
866       (ImageByteOrder (xcontext->disp) ==
867       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
868
869   /* Search for XShm extension support */
870 #ifdef HAVE_XSHM
871   if (XShmQueryExtension (xcontext->disp) &&
872       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
873     xcontext->use_xshm = TRUE;
874     GST_DEBUG ("ximagesink is using XShm extension");
875   } else
876 #endif /* HAVE_XSHM */
877   {
878     xcontext->use_xshm = FALSE;
879     GST_DEBUG ("ximagesink is not using XShm extension");
880   }
881
882   /* our caps system handles 24/32bpp RGB as big-endian. */
883   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
884       xcontext->endianness == G_LITTLE_ENDIAN) {
885     xcontext->endianness = G_BIG_ENDIAN;
886     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
887     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
888     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
889     if (xcontext->bpp == 24) {
890       xcontext->visual->red_mask >>= 8;
891       xcontext->visual->green_mask >>= 8;
892       xcontext->visual->blue_mask >>= 8;
893     }
894   }
895
896   /* update object's par with calculated one if not set yet */
897   if (!ximagesink->par) {
898     ximagesink->par = g_new0 (GValue, 1);
899     gst_value_init_and_copy (ximagesink->par, xcontext->par);
900     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
901   }
902   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
903       "bpp", G_TYPE_INT, xcontext->bpp,
904       "depth", G_TYPE_INT, xcontext->depth,
905       "endianness", G_TYPE_INT, xcontext->endianness,
906       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
907       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
908       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
909       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
910       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
911       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
912   if (ximagesink->par) {
913     int nom, den;
914
915     nom = gst_value_get_fraction_numerator (ximagesink->par);
916     den = gst_value_get_fraction_denominator (ximagesink->par);
917     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
918         GST_TYPE_FRACTION, nom, den, NULL);
919   }
920
921   g_mutex_unlock (ximagesink->x_lock);
922
923   return xcontext;
924 }
925
926 /* This function cleans the X context. Closing the Display and unrefing the
927    caps for supported formats. */
928 static void
929 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
930 {
931   GstXContext *xcontext;
932
933   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
934
935   GST_OBJECT_LOCK (ximagesink);
936   if (ximagesink->xcontext == NULL) {
937     GST_OBJECT_UNLOCK (ximagesink);
938     return;
939   }
940
941   /* Take the xcontext reference and NULL it while we
942    * clean it up, so that any buffer-alloced buffers 
943    * arriving after this will be freed correctly */
944   xcontext = ximagesink->xcontext;
945   ximagesink->xcontext = NULL;
946
947   GST_OBJECT_UNLOCK (ximagesink);
948
949   gst_caps_unref (xcontext->caps);
950   g_free (xcontext->par);
951   g_free (ximagesink->par);
952   ximagesink->par = NULL;
953
954   if (xcontext->last_caps)
955     gst_caps_replace (&xcontext->last_caps, NULL);
956
957   g_mutex_lock (ximagesink->x_lock);
958
959   XCloseDisplay (xcontext->disp);
960
961   g_mutex_unlock (ximagesink->x_lock);
962
963   g_free (xcontext);
964 }
965
966 /* Element stuff */
967
968 static GstCaps *
969 gst_ximagesink_getcaps (GstBaseSink * bsink)
970 {
971   GstXImageSink *ximagesink;
972   GstCaps *caps;
973   int i;
974
975   ximagesink = GST_XIMAGESINK (bsink);
976
977   if (ximagesink->xcontext)
978     return gst_caps_ref (ximagesink->xcontext->caps);
979
980   /* get a template copy and add the pixel aspect ratio */
981   caps =
982       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
983           (ximagesink)->sinkpad));
984   for (i = 0; i < gst_caps_get_size (caps); ++i) {
985     GstStructure *structure = gst_caps_get_structure (caps, i);
986
987     if (ximagesink->par) {
988       int nom, den;
989
990       nom = gst_value_get_fraction_numerator (ximagesink->par);
991       den = gst_value_get_fraction_denominator (ximagesink->par);
992       gst_structure_set (structure, "pixel-aspect-ratio",
993           GST_TYPE_FRACTION, nom, den, NULL);
994     }
995   }
996
997   return caps;
998 }
999
1000 static gboolean
1001 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1002 {
1003   GstXImageSink *ximagesink;
1004   GstStructure *structure;
1005   GstBufferPool *newpool, *oldpool;
1006   gboolean ret = TRUE;
1007   const GValue *par;
1008   gint new_width, new_height;
1009   const GValue *fps;
1010
1011   ximagesink = GST_XIMAGESINK (bsink);
1012
1013   if (!ximagesink->xcontext)
1014     return FALSE;
1015
1016   GST_DEBUG_OBJECT (ximagesink,
1017       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1018       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1019
1020   /* We intersect those caps with our template to make sure they are correct */
1021   if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1022     goto incompatible_caps;
1023
1024   structure = gst_caps_get_structure (caps, 0);
1025
1026   ret &= gst_structure_get_int (structure, "width", &new_width);
1027   ret &= gst_structure_get_int (structure, "height", &new_height);
1028   fps = gst_structure_get_value (structure, "framerate");
1029   ret &= (fps != NULL);
1030
1031   if (!ret)
1032     return FALSE;
1033
1034   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1035    * otherwise linking should fail */
1036   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1037   if (par) {
1038     if (ximagesink->par) {
1039       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1040         goto wrong_aspect;
1041       }
1042     } else if (ximagesink->xcontext->par) {
1043       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1044         goto wrong_aspect;
1045       }
1046     }
1047   }
1048
1049   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1050   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1051   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1052   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1053
1054   /* Notify application to set xwindow id now */
1055   g_mutex_lock (ximagesink->flow_lock);
1056   if (!ximagesink->xwindow) {
1057     g_mutex_unlock (ximagesink->flow_lock);
1058     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1059   } else {
1060     g_mutex_unlock (ximagesink->flow_lock);
1061   }
1062
1063   /* Creating our window and our image */
1064   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1065       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1066     goto invalid_size;
1067
1068   g_mutex_lock (ximagesink->flow_lock);
1069   if (!ximagesink->xwindow) {
1070     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1071         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1072   }
1073   /* Remember to draw borders for next frame */
1074   ximagesink->draw_border = TRUE;
1075
1076   /* create a new pool for the new configuration */
1077   newpool = gst_ximage_buffer_pool_new (ximagesink);
1078
1079   structure = gst_buffer_pool_get_config (newpool);
1080   gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
1081   if (!gst_buffer_pool_set_config (newpool, structure))
1082     goto config_failed;
1083
1084   if (!gst_buffer_pool_set_active (newpool, TRUE))
1085     goto activate_failed;
1086
1087   oldpool = ximagesink->pool;
1088   ximagesink->pool = newpool;
1089
1090   /* unref the old sink */
1091   if (oldpool) {
1092     /* deactivate */
1093     gst_buffer_pool_set_active (oldpool, FALSE);
1094     gst_object_unref (oldpool);
1095   }
1096   g_mutex_unlock (ximagesink->flow_lock);
1097
1098   return TRUE;
1099
1100   /* ERRORS */
1101 incompatible_caps:
1102   {
1103     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1104     return FALSE;
1105   }
1106 wrong_aspect:
1107   {
1108     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1109     return FALSE;
1110   }
1111 invalid_size:
1112   {
1113     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1114         ("Invalid image size."));
1115     return FALSE;
1116   }
1117 config_failed:
1118   {
1119     GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1120     g_mutex_unlock (ximagesink->flow_lock);
1121     return FALSE;
1122   }
1123 activate_failed:
1124   {
1125     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1126     g_mutex_unlock (ximagesink->flow_lock);
1127     return FALSE;
1128   }
1129 }
1130
1131 static GstStateChangeReturn
1132 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1133 {
1134   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1135   GstXImageSink *ximagesink;
1136   GstXContext *xcontext = NULL;
1137
1138   ximagesink = GST_XIMAGESINK (element);
1139
1140   switch (transition) {
1141     case GST_STATE_CHANGE_NULL_TO_READY:
1142       /* Initializing the XContext */
1143       if (ximagesink->xcontext == NULL) {
1144         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1145         if (xcontext == NULL) {
1146           ret = GST_STATE_CHANGE_FAILURE;
1147           goto beach;
1148         }
1149         GST_OBJECT_LOCK (ximagesink);
1150         if (xcontext)
1151           ximagesink->xcontext = xcontext;
1152         GST_OBJECT_UNLOCK (ximagesink);
1153       }
1154
1155       /* call XSynchronize with the current value of synchronous */
1156       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1157           ximagesink->synchronous ? "TRUE" : "FALSE");
1158       g_mutex_lock (ximagesink->x_lock);
1159       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1160       g_mutex_unlock (ximagesink->x_lock);
1161       gst_ximagesink_manage_event_thread (ximagesink);
1162       break;
1163     case GST_STATE_CHANGE_READY_TO_PAUSED:
1164       g_mutex_lock (ximagesink->flow_lock);
1165       if (ximagesink->xwindow)
1166         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1167       g_mutex_unlock (ximagesink->flow_lock);
1168       break;
1169     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1170       break;
1171     default:
1172       break;
1173   }
1174
1175   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1176
1177   switch (transition) {
1178     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1179       break;
1180     case GST_STATE_CHANGE_PAUSED_TO_READY:
1181       ximagesink->fps_n = 0;
1182       ximagesink->fps_d = 1;
1183       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1184       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1185       g_mutex_lock (ximagesink->flow_lock);
1186       if (ximagesink->pool)
1187         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1188       g_mutex_unlock (ximagesink->flow_lock);
1189       break;
1190     case GST_STATE_CHANGE_READY_TO_NULL:
1191       gst_ximagesink_reset (ximagesink);
1192       break;
1193     default:
1194       break;
1195   }
1196
1197 beach:
1198   return ret;
1199 }
1200
1201 static void
1202 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1203     GstClockTime * start, GstClockTime * end)
1204 {
1205   GstXImageSink *ximagesink;
1206
1207   ximagesink = GST_XIMAGESINK (bsink);
1208
1209   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1210     *start = GST_BUFFER_TIMESTAMP (buf);
1211     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1212       *end = *start + GST_BUFFER_DURATION (buf);
1213     } else {
1214       if (ximagesink->fps_n > 0) {
1215         *end = *start +
1216             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1217             ximagesink->fps_n);
1218       }
1219     }
1220   }
1221 }
1222
1223 static GstFlowReturn
1224 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1225 {
1226   GstFlowReturn res;
1227   GstXImageSink *ximagesink;
1228   GstMetaXImage *meta;
1229   GstBuffer *temp;
1230   gboolean unref;
1231
1232   ximagesink = GST_XIMAGESINK (vsink);
1233
1234   meta = gst_buffer_get_meta_ximage (buf);
1235
1236   if (meta) {
1237     /* If this buffer has been allocated using our buffer management we simply
1238        put the ximage which is in the PRIVATE pointer */
1239     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1240     res = GST_FLOW_OK;
1241     unref = FALSE;
1242   } else {
1243     guint8 *data;
1244     gsize size;
1245
1246     /* Else we have to copy the data into our private image, */
1247     /* if we have one... */
1248     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1249
1250     /* we should have a pool, configured in setcaps */
1251     if (ximagesink->pool == NULL)
1252       goto no_pool;
1253
1254     /* take a buffer form our pool */
1255     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1256     if (res != GST_FLOW_OK)
1257       goto no_buffer;
1258
1259     unref = TRUE;
1260
1261     if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1262       goto wrong_size;
1263
1264     data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1265     gst_buffer_extract (buf, 0, data, size);
1266     gst_buffer_unmap (temp, data, size);
1267
1268     buf = temp;
1269   }
1270
1271   if (!gst_ximagesink_ximage_put (ximagesink, buf))
1272     goto no_window;
1273
1274 done:
1275   if (unref)
1276     gst_buffer_unref (buf);
1277
1278   return res;
1279
1280   /* ERRORS */
1281 no_pool:
1282   {
1283     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1284         ("Internal error: can't allocate images"),
1285         ("We don't have a bufferpool negotiated"));
1286     return GST_FLOW_ERROR;
1287   }
1288 no_buffer:
1289   {
1290     /* No image available. That's very bad ! */
1291     GST_WARNING_OBJECT (ximagesink, "could not create image");
1292     return res;
1293   }
1294 wrong_size:
1295   {
1296     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1297         ("Failed to create output image buffer"),
1298         ("XServer allocated buffer size did not match input buffer %"
1299             G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1300             gst_buffer_get_size (buf)));
1301     res = GST_FLOW_ERROR;
1302     goto done;
1303   }
1304 no_window:
1305   {
1306     /* No Window available to put our image into */
1307     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1308     res = GST_FLOW_ERROR;
1309     goto done;
1310   }
1311 }
1312
1313 static gboolean
1314 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1315 {
1316   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1317
1318   switch (GST_EVENT_TYPE (event)) {
1319     case GST_EVENT_TAG:{
1320       GstTagList *l;
1321       gchar *title = NULL;
1322
1323       gst_event_parse_tag (event, &l);
1324       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1325
1326       if (title) {
1327         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1328         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1329             title);
1330
1331         g_free (title);
1332       }
1333       break;
1334     }
1335     default:
1336       break;
1337   }
1338   if (GST_BASE_SINK_CLASS (parent_class)->event)
1339     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1340   else
1341     return TRUE;
1342 }
1343
1344 /* Interfaces stuff */
1345
1346 static gboolean
1347 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1348 {
1349   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1350   return TRUE;
1351 }
1352
1353 static void
1354 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1355 {
1356   klass->supported = gst_ximagesink_interface_supported;
1357 }
1358
1359 static void
1360 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1361     GstStructure * structure)
1362 {
1363   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1364   GstEvent *event;
1365   gint x_offset, y_offset;
1366   gdouble x, y;
1367   GstPad *pad = NULL;
1368
1369   event = gst_event_new_navigation (structure);
1370
1371   /* We are not converting the pointer coordinates as there's no hardware
1372      scaling done here. The only possible scaling is done by videoscale and
1373      videoscale will have to catch those events and tranform the coordinates
1374      to match the applied scaling. So here we just add the offset if the image
1375      is centered in the window.  */
1376
1377   /* We take the flow_lock while we look at the window */
1378   g_mutex_lock (ximagesink->flow_lock);
1379
1380   if (!ximagesink->xwindow) {
1381     g_mutex_unlock (ximagesink->flow_lock);
1382     return;
1383   }
1384
1385   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1386   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1387
1388   g_mutex_unlock (ximagesink->flow_lock);
1389
1390   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1391     x -= x_offset / 2;
1392     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1393   }
1394   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1395     y -= y_offset / 2;
1396     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1397   }
1398
1399   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1400
1401   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1402     gst_pad_send_event (pad, event);
1403
1404     gst_object_unref (pad);
1405   }
1406 }
1407
1408 static void
1409 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1410 {
1411   iface->send_event = gst_ximagesink_navigation_send_event;
1412 }
1413
1414 static void
1415 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1416 {
1417   XID xwindow_id = id;
1418   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1419   GstXWindow *xwindow = NULL;
1420   XWindowAttributes attr;
1421
1422   /* We acquire the stream lock while setting this window in the element.
1423      We are basically cleaning tons of stuff replacing the old window, putting
1424      images while we do that would surely crash */
1425   g_mutex_lock (ximagesink->flow_lock);
1426
1427   /* If we already use that window return */
1428   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1429     g_mutex_unlock (ximagesink->flow_lock);
1430     return;
1431   }
1432
1433   /* If the element has not initialized the X11 context try to do so */
1434   if (!ximagesink->xcontext &&
1435       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1436     g_mutex_unlock (ximagesink->flow_lock);
1437     /* we have thrown a GST_ELEMENT_ERROR now */
1438     return;
1439   }
1440
1441   /* If a window is there already we destroy it */
1442   if (ximagesink->xwindow) {
1443     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1444     ximagesink->xwindow = NULL;
1445   }
1446
1447   /* If the xid is 0 we go back to an internal window */
1448   if (xwindow_id == 0) {
1449     /* If no width/height caps nego did not happen window will be created
1450        during caps nego then */
1451     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1452       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1453           GST_VIDEO_SINK_WIDTH (ximagesink),
1454           GST_VIDEO_SINK_HEIGHT (ximagesink));
1455     }
1456   } else {
1457     xwindow = g_new0 (GstXWindow, 1);
1458
1459     xwindow->win = xwindow_id;
1460
1461     /* We get window geometry, set the event we want to receive,
1462        and create a GC */
1463     g_mutex_lock (ximagesink->x_lock);
1464     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1465     xwindow->width = attr.width;
1466     xwindow->height = attr.height;
1467     xwindow->internal = FALSE;
1468     if (ximagesink->handle_events) {
1469       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1470           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1471           KeyReleaseMask);
1472     }
1473
1474     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1475     g_mutex_unlock (ximagesink->x_lock);
1476   }
1477
1478   if (xwindow)
1479     ximagesink->xwindow = xwindow;
1480
1481   g_mutex_unlock (ximagesink->flow_lock);
1482 }
1483
1484 static void
1485 gst_ximagesink_expose (GstXOverlay * overlay)
1486 {
1487   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1488
1489   gst_ximagesink_xwindow_update_geometry (ximagesink);
1490   gst_ximagesink_ximage_put (ximagesink, NULL);
1491 }
1492
1493 static void
1494 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1495     gboolean handle_events)
1496 {
1497   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1498
1499   ximagesink->handle_events = handle_events;
1500
1501   g_mutex_lock (ximagesink->flow_lock);
1502
1503   if (G_UNLIKELY (!ximagesink->xwindow)) {
1504     g_mutex_unlock (ximagesink->flow_lock);
1505     return;
1506   }
1507
1508   g_mutex_lock (ximagesink->x_lock);
1509
1510   if (handle_events) {
1511     if (ximagesink->xwindow->internal) {
1512       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1513           ExposureMask | StructureNotifyMask | PointerMotionMask |
1514           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1515     } else {
1516       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1517           ExposureMask | StructureNotifyMask | PointerMotionMask |
1518           KeyPressMask | KeyReleaseMask);
1519     }
1520   } else {
1521     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1522   }
1523
1524   g_mutex_unlock (ximagesink->x_lock);
1525
1526   g_mutex_unlock (ximagesink->flow_lock);
1527 }
1528
1529 static void
1530 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1531 {
1532   iface->set_window_handle = gst_ximagesink_set_window_handle;
1533   iface->expose = gst_ximagesink_expose;
1534   iface->handle_events = gst_ximagesink_set_event_handling;
1535 }
1536
1537 /* =========================================== */
1538 /*                                             */
1539 /*              Init & Class init              */
1540 /*                                             */
1541 /* =========================================== */
1542
1543 static void
1544 gst_ximagesink_set_property (GObject * object, guint prop_id,
1545     const GValue * value, GParamSpec * pspec)
1546 {
1547   GstXImageSink *ximagesink;
1548
1549   g_return_if_fail (GST_IS_XIMAGESINK (object));
1550
1551   ximagesink = GST_XIMAGESINK (object);
1552
1553   switch (prop_id) {
1554     case PROP_DISPLAY:
1555       ximagesink->display_name = g_strdup (g_value_get_string (value));
1556       break;
1557     case PROP_SYNCHRONOUS:
1558       ximagesink->synchronous = g_value_get_boolean (value);
1559       if (ximagesink->xcontext) {
1560         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1561             ximagesink->synchronous ? "TRUE" : "FALSE");
1562         g_mutex_lock (ximagesink->x_lock);
1563         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1564         g_mutex_unlock (ximagesink->x_lock);
1565       }
1566       break;
1567     case PROP_FORCE_ASPECT_RATIO:
1568       ximagesink->keep_aspect = g_value_get_boolean (value);
1569       break;
1570     case PROP_PIXEL_ASPECT_RATIO:
1571     {
1572       GValue *tmp;
1573
1574       tmp = g_new0 (GValue, 1);
1575       g_value_init (tmp, GST_TYPE_FRACTION);
1576
1577       if (!g_value_transform (value, tmp)) {
1578         GST_WARNING_OBJECT (ximagesink,
1579             "Could not transform string to aspect ratio");
1580         g_free (tmp);
1581       } else {
1582         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1583             gst_value_get_fraction_numerator (tmp),
1584             gst_value_get_fraction_denominator (tmp));
1585         g_free (ximagesink->par);
1586         ximagesink->par = tmp;
1587       }
1588     }
1589       break;
1590     case PROP_HANDLE_EVENTS:
1591       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1592           g_value_get_boolean (value));
1593       gst_ximagesink_manage_event_thread (ximagesink);
1594       break;
1595     case PROP_HANDLE_EXPOSE:
1596       ximagesink->handle_expose = g_value_get_boolean (value);
1597       gst_ximagesink_manage_event_thread (ximagesink);
1598       break;
1599     default:
1600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1601       break;
1602   }
1603 }
1604
1605 static void
1606 gst_ximagesink_get_property (GObject * object, guint prop_id,
1607     GValue * value, GParamSpec * pspec)
1608 {
1609   GstXImageSink *ximagesink;
1610
1611   g_return_if_fail (GST_IS_XIMAGESINK (object));
1612
1613   ximagesink = GST_XIMAGESINK (object);
1614
1615   switch (prop_id) {
1616     case PROP_DISPLAY:
1617       g_value_set_string (value, ximagesink->display_name);
1618       break;
1619     case PROP_SYNCHRONOUS:
1620       g_value_set_boolean (value, ximagesink->synchronous);
1621       break;
1622     case PROP_FORCE_ASPECT_RATIO:
1623       g_value_set_boolean (value, ximagesink->keep_aspect);
1624       break;
1625     case PROP_PIXEL_ASPECT_RATIO:
1626       if (ximagesink->par)
1627         g_value_transform (ximagesink->par, value);
1628       break;
1629     case PROP_HANDLE_EVENTS:
1630       g_value_set_boolean (value, ximagesink->handle_events);
1631       break;
1632     case PROP_HANDLE_EXPOSE:
1633       g_value_set_boolean (value, ximagesink->handle_expose);
1634       break;
1635     case PROP_WINDOW_WIDTH:
1636       if (ximagesink->xwindow)
1637         g_value_set_uint64 (value, ximagesink->xwindow->width);
1638       else
1639         g_value_set_uint64 (value, 0);
1640       break;
1641     case PROP_WINDOW_HEIGHT:
1642       if (ximagesink->xwindow)
1643         g_value_set_uint64 (value, ximagesink->xwindow->height);
1644       else
1645         g_value_set_uint64 (value, 0);
1646       break;
1647     default:
1648       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1649       break;
1650   }
1651 }
1652
1653 static void
1654 gst_ximagesink_reset (GstXImageSink * ximagesink)
1655 {
1656   GThread *thread;
1657
1658   GST_OBJECT_LOCK (ximagesink);
1659   ximagesink->running = FALSE;
1660   /* grab thread and mark it as NULL */
1661   thread = ximagesink->event_thread;
1662   ximagesink->event_thread = NULL;
1663   GST_OBJECT_UNLOCK (ximagesink);
1664
1665   /* Wait for our event thread to finish before we clean up our stuff. */
1666   if (thread)
1667     g_thread_join (thread);
1668
1669   if (ximagesink->cur_image) {
1670     gst_buffer_unref (ximagesink->cur_image);
1671     ximagesink->cur_image = NULL;
1672   }
1673
1674   g_mutex_lock (ximagesink->flow_lock);
1675
1676   if (ximagesink->pool) {
1677     gst_object_unref (ximagesink->pool);
1678     ximagesink->pool = NULL;
1679   }
1680
1681   if (ximagesink->xwindow) {
1682     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1683     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1684     ximagesink->xwindow = NULL;
1685   }
1686   g_mutex_unlock (ximagesink->flow_lock);
1687
1688   gst_ximagesink_xcontext_clear (ximagesink);
1689 }
1690
1691 static void
1692 gst_ximagesink_finalize (GObject * object)
1693 {
1694   GstXImageSink *ximagesink;
1695
1696   ximagesink = GST_XIMAGESINK (object);
1697
1698   gst_ximagesink_reset (ximagesink);
1699
1700   if (ximagesink->display_name) {
1701     g_free (ximagesink->display_name);
1702     ximagesink->display_name = NULL;
1703   }
1704   if (ximagesink->par) {
1705     g_free (ximagesink->par);
1706     ximagesink->par = NULL;
1707   }
1708   if (ximagesink->x_lock) {
1709     g_mutex_free (ximagesink->x_lock);
1710     ximagesink->x_lock = NULL;
1711   }
1712   if (ximagesink->flow_lock) {
1713     g_mutex_free (ximagesink->flow_lock);
1714     ximagesink->flow_lock = NULL;
1715   }
1716
1717   g_free (ximagesink->media_title);
1718
1719   G_OBJECT_CLASS (parent_class)->finalize (object);
1720 }
1721
1722 static void
1723 gst_ximagesink_init (GstXImageSink * ximagesink)
1724 {
1725   ximagesink->display_name = NULL;
1726   ximagesink->xcontext = NULL;
1727   ximagesink->xwindow = NULL;
1728   ximagesink->cur_image = NULL;
1729
1730   ximagesink->event_thread = NULL;
1731   ximagesink->running = FALSE;
1732
1733   ximagesink->fps_n = 0;
1734   ximagesink->fps_d = 1;
1735
1736   ximagesink->x_lock = g_mutex_new ();
1737   ximagesink->flow_lock = g_mutex_new ();
1738
1739   ximagesink->par = NULL;
1740
1741   ximagesink->pool = NULL;
1742
1743   ximagesink->synchronous = FALSE;
1744   ximagesink->keep_aspect = FALSE;
1745   ximagesink->handle_events = TRUE;
1746   ximagesink->handle_expose = TRUE;
1747 }
1748
1749 static void
1750 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1751 {
1752   GObjectClass *gobject_class;
1753   GstElementClass *gstelement_class;
1754   GstBaseSinkClass *gstbasesink_class;
1755   GstVideoSinkClass *videosink_class;
1756
1757   gobject_class = (GObjectClass *) klass;
1758   gstelement_class = (GstElementClass *) klass;
1759   gstbasesink_class = (GstBaseSinkClass *) klass;
1760   videosink_class = (GstVideoSinkClass *) klass;
1761
1762   gobject_class->finalize = gst_ximagesink_finalize;
1763   gobject_class->set_property = gst_ximagesink_set_property;
1764   gobject_class->get_property = gst_ximagesink_get_property;
1765
1766   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1767       g_param_spec_string ("display", "Display", "X Display name",
1768           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1769   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1770       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1771           "the X display in synchronous mode. (used only for debugging)", FALSE,
1772           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1773   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1774       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1775           "When enabled, reverse caps negotiation (scaling) will respect "
1776           "original aspect ratio", FALSE,
1777           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1778   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1779       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1780           "The pixel aspect ratio of the device", "1/1",
1781           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1782   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1783       g_param_spec_boolean ("handle-events", "Handle XEvents",
1784           "When enabled, XEvents will be selected and handled", TRUE,
1785           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1786   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1787       g_param_spec_boolean ("handle-expose", "Handle expose",
1788           "When enabled, "
1789           "the current frame will always be drawn in response to X Expose "
1790           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1791
1792   /**
1793    * GstXImageSink:window-width
1794    *
1795    * Actual width of the video window.
1796    *
1797    * Since: 0.10.32
1798    */
1799   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1800       g_param_spec_uint64 ("window-width", "window-width",
1801           "Width of the window", 0, G_MAXUINT64, 0,
1802           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1803
1804   /**
1805    * GstXImageSink:window-height
1806    *
1807    * Actual height of the video window.
1808    *
1809    * Since: 0.10.32
1810    */
1811   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1812       g_param_spec_uint64 ("window-height", "window-height",
1813           "Height of the window", 0, G_MAXUINT64, 0,
1814           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1815
1816   gst_element_class_set_details_simple (gstelement_class,
1817       "Video sink", "Sink/Video",
1818       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1819
1820   gst_element_class_add_pad_template (gstelement_class,
1821       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1822
1823   gstelement_class->change_state = gst_ximagesink_change_state;
1824
1825   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1826   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1827   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1828   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1829
1830   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1831 }