gst: Set alignment at the correct place of GstAllocationParams
[platform/upstream/gstreamer.git] / sys / xvimage / xvimagesink.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *               <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-xvimagesink
23  *
24  * XvImageSink renders video frames to a drawable (XWindow) on a local display
25  * using the XVideo extension. Rendering to a remote display is theoretically
26  * possible but i doubt that the XVideo extension is actually available when
27  * connecting to a remote display. This element can receive a Window ID from the
28  * application through the XOverlay interface and will then render video frames
29  * in this drawable. If no Window ID was provided by the application, the
30  * element will create its own internal window and render into it.
31  *
32  * <refsect2>
33  * <title>Scaling</title>
34  * <para>
35  * The XVideo extension, when it's available, handles hardware accelerated
36  * scaling of video frames. This means that the element will just accept
37  * incoming video frames no matter their geometry and will then put them to the
38  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39  * property it is possible to enforce scaling with a constant aspect ratio,
40  * which means drawing black borders around the video frame.
41  * </para>
42  * </refsect2>
43  * <refsect2>
44  * <title>Events</title>
45  * <para>
46  * XvImageSink creates a thread to handle events coming from the drawable. There
47  * are several kind of events that can be grouped in 2 big categories: input
48  * events and window state related events. Input events will be translated to
49  * navigation events and pushed upstream for other elements to react on them.
50  * This includes events such as pointer moves, key press/release, clicks etc...
51  * Other events are used to handle the drawable appearance even when the data
52  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53  * paused, it will receive expose events from the drawable and draw the latest
54  * frame with correct borders/aspect-ratio.
55  * </para>
56  * </refsect2>
57  * <refsect2>
58  * <title>Pixel aspect ratio</title>
59  * <para>
60  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61  * the display specified in the #GstXvImageSink:display property or the
62  * default display if nothing specified. Once this connection is open it will
63  * inspect the display configuration including the physical display geometry and
64  * then calculate the pixel aspect ratio. When receiving video frames with a
65  * different pixel aspect ratio, XvImageSink will use hardware scaling to
66  * display the video frames correctly on display's pixel aspect ratio.
67  * Sometimes the calculated pixel aspect ratio can be wrong, it is
68  * then possible to enforce a specific pixel aspect ratio using the
69  * #GstXvImageSink:pixel-aspect-ratio property.
70  * </para>
71  * </refsect2>
72  * <refsect2>
73  * <title>Examples</title>
74  * |[
75  * gst-launch -v videotestsrc ! xvimagesink
76  * ]| A pipeline to test hardware scaling.
77  * When the test video signal appears you can resize the window and see that
78  * video frames are scaled through hardware (no extra CPU cost).
79  * |[
80  * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81  * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82  * You can observe the borders drawn around the scaled image respecting aspect
83  * ratio.
84  * |[
85  * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86  * ]| A pipeline to test navigation events.
87  * While moving the mouse pointer over the test signal you will see a black box
88  * following the mouse pointer. If you press the mouse button somewhere on the
89  * video and release it somewhere else a green box will appear where you pressed
90  * the button and a red one where you released it. (The navigationtest element
91  * is part of gst-plugins-good.) You can observe here that even if the images
92  * are scaled through hardware the pointer coordinates are converted back to the
93  * original video frame geometry so that the box can be drawn to the correct
94  * position. This also handles borders correctly, limiting coordinates to the
95  * image area
96  * |[
97  * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99  * videotestsrc, in most cases the pixel aspect ratio of the display will be
100  * 1/1. This means that XvImageSink will have to do the scaling to convert
101  * incoming frames to a size that will match the display pixel aspect ratio
102  * (from 320x240 to 320x180 in this case). Note that you might have to escape
103  * some characters for your shell like '\(fraction\)'.
104  * |[
105  * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106  * ]| Demonstrates how to use the colorbalance interface.
107  * </refsect2>
108  */
109
110 /* for developers: there are two useful tools : xvinfo and xvattr */
111
112 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
113  * with newer GLib versions (>= 2.31.0) */
114 #define GLIB_DISABLE_DEPRECATION_WARNINGS
115
116 #ifdef HAVE_CONFIG_H
117 #include "config.h"
118 #endif
119
120 /* Our interfaces */
121 #include <gst/video/navigation.h>
122 #include <gst/video/videooverlay.h>
123 #include <gst/video/colorbalance.h>
124 /* Helper functions */
125 #include <gst/video/gstvideometa.h>
126
127 /* Object header */
128 #include "xvimagesink.h"
129
130 /* Debugging category */
131 #include <gst/gstinfo.h>
132
133 #include "gst/glib-compat-private.h"
134
135 /* for XkbKeycodeToKeysym */
136 #include <X11/XKBlib.h>
137
138 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
139 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
140 #define GST_CAT_DEFAULT gst_debug_xvimagesink
141
142 typedef struct
143 {
144   unsigned long flags;
145   unsigned long functions;
146   unsigned long decorations;
147   long input_mode;
148   unsigned long status;
149 }
150 MotifWmHints, MwmHints;
151
152 #define MWM_HINTS_DECORATIONS   (1L << 1)
153
154 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
155 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
156     xvimagesink);
157 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
158
159 /* Default template - initiated with class struct to allow gst-register to work
160    without X running */
161 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
162 GST_STATIC_PAD_TEMPLATE ("sink",
163     GST_PAD_SINK,
164     GST_PAD_ALWAYS,
165     GST_STATIC_CAPS ("video/x-raw, "
166         "framerate = (fraction) [ 0, MAX ], "
167         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
168     );
169
170 enum
171 {
172   PROP_0,
173   PROP_CONTRAST,
174   PROP_BRIGHTNESS,
175   PROP_HUE,
176   PROP_SATURATION,
177   PROP_DISPLAY,
178   PROP_SYNCHRONOUS,
179   PROP_PIXEL_ASPECT_RATIO,
180   PROP_FORCE_ASPECT_RATIO,
181   PROP_HANDLE_EVENTS,
182   PROP_DEVICE,
183   PROP_DEVICE_NAME,
184   PROP_HANDLE_EXPOSE,
185   PROP_DOUBLE_BUFFER,
186   PROP_AUTOPAINT_COLORKEY,
187   PROP_COLORKEY,
188   PROP_DRAW_BORDERS,
189   PROP_WINDOW_WIDTH,
190   PROP_WINDOW_HEIGHT
191 };
192
193 /* ============================================================= */
194 /*                                                               */
195 /*                       Public Methods                          */
196 /*                                                               */
197 /* ============================================================= */
198
199 /* =========================================== */
200 /*                                             */
201 /*          Object typing & Creation           */
202 /*                                             */
203 /* =========================================== */
204 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
205 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
206     iface);
207 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
208     iface);
209 #define gst_xvimagesink_parent_class parent_class
210 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
211     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
212         gst_xvimagesink_navigation_init);
213     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
214         gst_xvimagesink_video_overlay_init);
215     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
216         gst_xvimagesink_colorbalance_init));
217
218
219 /* ============================================================= */
220 /*                                                               */
221 /*                       Private Methods                         */
222 /*                                                               */
223 /* ============================================================= */
224
225
226 /* We are called with the x_lock taken */
227 static void
228 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
229     GstXWindow * xwindow, GstVideoRectangle rect)
230 {
231   gint t1, t2;
232
233   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
234   g_return_if_fail (xwindow != NULL);
235
236   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
237       xvimagesink->xcontext->black);
238
239   /* Left border */
240   if (rect.x > xvimagesink->render_rect.x) {
241     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
242         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
243         rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
244   }
245
246   /* Right border */
247   t1 = rect.x + rect.w;
248   t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
249   if (t1 < t2) {
250     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
251         t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
252   }
253
254   /* Top border */
255   if (rect.y > xvimagesink->render_rect.y) {
256     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
257         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
258         xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
259   }
260
261   /* Bottom border */
262   t1 = rect.y + rect.h;
263   t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
264   if (t1 < t2) {
265     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
266         xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
267   }
268 }
269
270 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
271  * if no window was available  */
272 static gboolean
273 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
274 {
275   GstXvImageMeta *meta;
276   GstVideoCropMeta *crop;
277   GstVideoRectangle result;
278   gboolean draw_border = FALSE;
279   GstVideoRectangle src, dst;
280
281   /* We take the flow_lock. If expose is in there we don't want to run
282      concurrently from the data flow thread */
283   g_mutex_lock (xvimagesink->flow_lock);
284
285   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
286     g_mutex_unlock (xvimagesink->flow_lock);
287     return FALSE;
288   }
289
290   /* Draw borders when displaying the first frame. After this
291      draw borders only on expose event or after a size change. */
292   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
293     draw_border = TRUE;
294   }
295
296   /* Store a reference to the last image we put, lose the previous one */
297   if (xvimage && xvimagesink->cur_image != xvimage) {
298     if (xvimagesink->cur_image) {
299       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
300       gst_buffer_unref (xvimagesink->cur_image);
301     }
302     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
303     xvimagesink->cur_image = gst_buffer_ref (xvimage);
304   }
305
306   /* Expose sends a NULL image, we take the latest frame */
307   if (!xvimage) {
308     if (xvimagesink->cur_image) {
309       draw_border = TRUE;
310       xvimage = xvimagesink->cur_image;
311     } else {
312       g_mutex_unlock (xvimagesink->flow_lock);
313       return TRUE;
314     }
315   }
316
317   meta = gst_buffer_get_xvimage_meta (xvimage);
318
319   crop = gst_buffer_get_video_crop_meta (xvimage);
320
321   if (crop) {
322     src.x = crop->x + meta->x;
323     src.y = crop->y + meta->y;
324     src.w = crop->width;
325     src.h = crop->height;
326     GST_LOG_OBJECT (xvimagesink,
327         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
328   } else {
329     src.x = meta->x;
330     src.y = meta->y;
331     src.w = meta->width;
332     src.h = meta->height;
333   }
334
335   if (xvimagesink->keep_aspect) {
336     GstVideoRectangle s;
337
338     /* We take the size of the source material as it was negotiated and
339      * corrected for DAR. This size can be different from the cropped size in
340      * which case the image will be scaled to fit the negotiated size. */
341     s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
342     s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
343     dst.w = xvimagesink->render_rect.w;
344     dst.h = xvimagesink->render_rect.h;
345
346     gst_video_sink_center_rect (s, dst, &result, TRUE);
347     result.x += xvimagesink->render_rect.x;
348     result.y += xvimagesink->render_rect.y;
349   } else {
350     memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
351   }
352
353   g_mutex_lock (xvimagesink->x_lock);
354
355   if (draw_border && xvimagesink->draw_borders) {
356     gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
357         result);
358     xvimagesink->redraw_border = FALSE;
359   }
360 #ifdef HAVE_XSHM
361   if (xvimagesink->xcontext->use_xshm) {
362     GST_LOG_OBJECT (xvimagesink,
363         "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
364         GST_PTR_FORMAT, meta->width, meta->height,
365         xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
366
367     XvShmPutImage (xvimagesink->xcontext->disp,
368         xvimagesink->xcontext->xv_port_id,
369         xvimagesink->xwindow->win,
370         xvimagesink->xwindow->gc, meta->xvimage,
371         src.x, src.y, src.w, src.h,
372         result.x, result.y, result.w, result.h, FALSE);
373   } else
374 #endif /* HAVE_XSHM */
375   {
376     XvPutImage (xvimagesink->xcontext->disp,
377         xvimagesink->xcontext->xv_port_id,
378         xvimagesink->xwindow->win,
379         xvimagesink->xwindow->gc, meta->xvimage,
380         src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
381   }
382
383   XSync (xvimagesink->xcontext->disp, FALSE);
384
385   g_mutex_unlock (xvimagesink->x_lock);
386
387   g_mutex_unlock (xvimagesink->flow_lock);
388
389   return TRUE;
390 }
391
392 static gboolean
393 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
394     GstXWindow * window)
395 {
396   Atom hints_atom = None;
397   MotifWmHints *hints;
398
399   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
400   g_return_val_if_fail (window != NULL, FALSE);
401
402   g_mutex_lock (xvimagesink->x_lock);
403
404   hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
405       True);
406   if (hints_atom == None) {
407     g_mutex_unlock (xvimagesink->x_lock);
408     return FALSE;
409   }
410
411   hints = g_malloc0 (sizeof (MotifWmHints));
412
413   hints->flags |= MWM_HINTS_DECORATIONS;
414   hints->decorations = 1 << 0;
415
416   XChangeProperty (xvimagesink->xcontext->disp, window->win,
417       hints_atom, hints_atom, 32, PropModeReplace,
418       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
419
420   XSync (xvimagesink->xcontext->disp, FALSE);
421
422   g_mutex_unlock (xvimagesink->x_lock);
423
424   g_free (hints);
425
426   return TRUE;
427 }
428
429 static void
430 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
431     GstXWindow * xwindow, const gchar * media_title)
432 {
433   if (media_title) {
434     g_free (xvimagesink->media_title);
435     xvimagesink->media_title = g_strdup (media_title);
436   }
437   if (xwindow) {
438     /* we have a window */
439     if (xwindow->internal) {
440       XTextProperty xproperty;
441       const gchar *app_name;
442       const gchar *title = NULL;
443       gchar *title_mem = NULL;
444
445       /* set application name as a title */
446       app_name = g_get_application_name ();
447
448       if (app_name && xvimagesink->media_title) {
449         title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
450             app_name, NULL);
451       } else if (app_name) {
452         title = app_name;
453       } else if (xvimagesink->media_title) {
454         title = xvimagesink->media_title;
455       }
456
457       if (title) {
458         if ((XStringListToTextProperty (((char **) &title), 1,
459                     &xproperty)) != 0) {
460           XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
461           XFree (xproperty.value);
462         }
463
464         g_free (title_mem);
465       }
466     }
467   }
468 }
469
470 /* This function handles a GstXWindow creation
471  * The width and height are the actual pixel size on the display */
472 static GstXWindow *
473 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
474     gint width, gint height)
475 {
476   GstXWindow *xwindow = NULL;
477   XGCValues values;
478
479   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
480
481   xwindow = g_new0 (GstXWindow, 1);
482
483   xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
484   xvimagesink->render_rect.w = width;
485   xvimagesink->render_rect.h = height;
486
487   xwindow->width = width;
488   xwindow->height = height;
489   xwindow->internal = TRUE;
490
491   g_mutex_lock (xvimagesink->x_lock);
492
493   xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
494       xvimagesink->xcontext->root,
495       0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
496
497   /* We have to do that to prevent X from redrawing the background on
498    * ConfigureNotify. This takes away flickering of video when resizing. */
499   XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
500
501   /* set application name as a title */
502   gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
503
504   if (xvimagesink->handle_events) {
505     Atom wm_delete;
506
507     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
508         StructureNotifyMask | PointerMotionMask | KeyPressMask |
509         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
510
511     /* Tell the window manager we'd like delete client messages instead of
512      * being killed */
513     wm_delete = XInternAtom (xvimagesink->xcontext->disp,
514         "WM_DELETE_WINDOW", True);
515     if (wm_delete != None) {
516       (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
517           &wm_delete, 1);
518     }
519   }
520
521   xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
522       xwindow->win, 0, &values);
523
524   XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
525
526   XSync (xvimagesink->xcontext->disp, FALSE);
527
528   g_mutex_unlock (xvimagesink->x_lock);
529
530   gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
531
532   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
533       xwindow->win);
534
535   return xwindow;
536 }
537
538 /* This function destroys a GstXWindow */
539 static void
540 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
541     GstXWindow * xwindow)
542 {
543   g_return_if_fail (xwindow != NULL);
544   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
545
546   g_mutex_lock (xvimagesink->x_lock);
547
548   /* If we did not create that window we just free the GC and let it live */
549   if (xwindow->internal)
550     XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
551   else
552     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
553
554   XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
555
556   XSync (xvimagesink->xcontext->disp, FALSE);
557
558   g_mutex_unlock (xvimagesink->x_lock);
559
560   g_free (xwindow);
561 }
562
563 static void
564 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
565 {
566   XWindowAttributes attr;
567
568   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
569
570   /* Update the window geometry */
571   g_mutex_lock (xvimagesink->x_lock);
572   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
573     g_mutex_unlock (xvimagesink->x_lock);
574     return;
575   }
576
577   XGetWindowAttributes (xvimagesink->xcontext->disp,
578       xvimagesink->xwindow->win, &attr);
579
580   xvimagesink->xwindow->width = attr.width;
581   xvimagesink->xwindow->height = attr.height;
582
583   if (!xvimagesink->have_render_rect) {
584     xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
585     xvimagesink->render_rect.w = attr.width;
586     xvimagesink->render_rect.h = attr.height;
587   }
588
589   g_mutex_unlock (xvimagesink->x_lock);
590 }
591
592 static void
593 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
594     GstXWindow * xwindow)
595 {
596   g_return_if_fail (xwindow != NULL);
597   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
598
599   g_mutex_lock (xvimagesink->x_lock);
600
601   XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
602       xwindow->win);
603
604   XSync (xvimagesink->xcontext->disp, FALSE);
605
606   g_mutex_unlock (xvimagesink->x_lock);
607 }
608
609 /* This function commits our internal colorbalance settings to our grabbed Xv
610    port. If the xcontext is not initialized yet it simply returns */
611 static void
612 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
613 {
614   GList *channels = NULL;
615
616   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
617
618   /* If we haven't initialized the X context we can't update anything */
619   if (xvimagesink->xcontext == NULL)
620     return;
621
622   /* Don't set the attributes if they haven't been changed, to avoid
623    * rounding errors changing the values */
624   if (!xvimagesink->cb_changed)
625     return;
626
627   /* For each channel of the colorbalance we calculate the correct value
628      doing range conversion and then set the Xv port attribute to match our
629      values. */
630   channels = xvimagesink->xcontext->channels_list;
631
632   while (channels) {
633     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
634       GstColorBalanceChannel *channel = NULL;
635       Atom prop_atom;
636       gint value = 0;
637       gdouble convert_coef;
638
639       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
640       g_object_ref (channel);
641
642       /* Our range conversion coef */
643       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
644
645       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
646         value = xvimagesink->hue;
647       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
648         value = xvimagesink->saturation;
649       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
650         value = xvimagesink->contrast;
651       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
652         value = xvimagesink->brightness;
653       } else {
654         g_warning ("got an unknown channel %s", channel->label);
655         g_object_unref (channel);
656         return;
657       }
658
659       /* Committing to Xv port */
660       g_mutex_lock (xvimagesink->x_lock);
661       prop_atom =
662           XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
663       if (prop_atom != None) {
664         int xv_value;
665         xv_value =
666             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
667         XvSetPortAttribute (xvimagesink->xcontext->disp,
668             xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
669       }
670       g_mutex_unlock (xvimagesink->x_lock);
671
672       g_object_unref (channel);
673     }
674     channels = g_list_next (channels);
675   }
676 }
677
678 /* This function handles XEvents that might be in the queue. It generates
679    GstEvent that will be sent upstream in the pipeline to handle interactivity
680    and navigation. It will also listen for configure events on the window to
681    trigger caps renegotiation so on the fly software scaling can work. */
682 static void
683 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
684 {
685   XEvent e;
686   guint pointer_x = 0, pointer_y = 0;
687   gboolean pointer_moved = FALSE;
688   gboolean exposed = FALSE, configured = FALSE;
689
690   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
691
692   /* Handle Interaction, produces navigation events */
693
694   /* We get all pointer motion events, only the last position is
695      interesting. */
696   g_mutex_lock (xvimagesink->flow_lock);
697   g_mutex_lock (xvimagesink->x_lock);
698   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
699           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
700     g_mutex_unlock (xvimagesink->x_lock);
701     g_mutex_unlock (xvimagesink->flow_lock);
702
703     switch (e.type) {
704       case MotionNotify:
705         pointer_x = e.xmotion.x;
706         pointer_y = e.xmotion.y;
707         pointer_moved = TRUE;
708         break;
709       default:
710         break;
711     }
712     g_mutex_lock (xvimagesink->flow_lock);
713     g_mutex_lock (xvimagesink->x_lock);
714   }
715
716   if (pointer_moved) {
717     g_mutex_unlock (xvimagesink->x_lock);
718     g_mutex_unlock (xvimagesink->flow_lock);
719
720     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
721         pointer_x, pointer_y);
722     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
723         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
724
725     g_mutex_lock (xvimagesink->flow_lock);
726     g_mutex_lock (xvimagesink->x_lock);
727   }
728
729   /* We get all events on our window to throw them upstream */
730   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
731           xvimagesink->xwindow->win,
732           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
733           &e)) {
734     KeySym keysym;
735     const char *key_str = NULL;
736
737     /* We lock only for the X function call */
738     g_mutex_unlock (xvimagesink->x_lock);
739     g_mutex_unlock (xvimagesink->flow_lock);
740
741     switch (e.type) {
742       case ButtonPress:
743         /* Mouse button pressed over our window. We send upstream
744            events for interactivity/navigation */
745         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
746             e.xbutton.button, e.xbutton.x, e.xbutton.y);
747         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
748             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
749         break;
750       case ButtonRelease:
751         /* Mouse button released over our window. We send upstream
752            events for interactivity/navigation */
753         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
754             e.xbutton.button, e.xbutton.x, e.xbutton.y);
755         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
756             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
757         break;
758       case KeyPress:
759       case KeyRelease:
760         /* Key pressed/released over our window. We send upstream
761            events for interactivity/navigation */
762         g_mutex_lock (xvimagesink->x_lock);
763         keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
764             e.xkey.keycode, 0, 0);
765         if (keysym != NoSymbol) {
766           key_str = XKeysymToString (keysym);
767         } else {
768           key_str = "unknown";
769         }
770         g_mutex_unlock (xvimagesink->x_lock);
771         GST_DEBUG_OBJECT (xvimagesink,
772             "key %d pressed over window at %d,%d (%s)",
773             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
774         gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
775             e.type == KeyPress ? "key-press" : "key-release", key_str);
776         break;
777       default:
778         GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
779             e.type);
780     }
781     g_mutex_lock (xvimagesink->flow_lock);
782     g_mutex_lock (xvimagesink->x_lock);
783   }
784
785   /* Handle Expose */
786   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
787           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
788     switch (e.type) {
789       case Expose:
790         exposed = TRUE;
791         break;
792       case ConfigureNotify:
793         g_mutex_unlock (xvimagesink->x_lock);
794         gst_xvimagesink_xwindow_update_geometry (xvimagesink);
795         g_mutex_lock (xvimagesink->x_lock);
796         configured = TRUE;
797         break;
798       default:
799         break;
800     }
801   }
802
803   if (xvimagesink->handle_expose && (exposed || configured)) {
804     g_mutex_unlock (xvimagesink->x_lock);
805     g_mutex_unlock (xvimagesink->flow_lock);
806
807     gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
808
809     g_mutex_lock (xvimagesink->flow_lock);
810     g_mutex_lock (xvimagesink->x_lock);
811   }
812
813   /* Handle Display events */
814   while (XPending (xvimagesink->xcontext->disp)) {
815     XNextEvent (xvimagesink->xcontext->disp, &e);
816
817     switch (e.type) {
818       case ClientMessage:{
819         Atom wm_delete;
820
821         wm_delete = XInternAtom (xvimagesink->xcontext->disp,
822             "WM_DELETE_WINDOW", True);
823         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
824           /* Handle window deletion by posting an error on the bus */
825           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
826               ("Output window was closed"), (NULL));
827
828           g_mutex_unlock (xvimagesink->x_lock);
829           gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
830           xvimagesink->xwindow = NULL;
831           g_mutex_lock (xvimagesink->x_lock);
832         }
833         break;
834       }
835       default:
836         break;
837     }
838   }
839
840   g_mutex_unlock (xvimagesink->x_lock);
841   g_mutex_unlock (xvimagesink->flow_lock);
842 }
843
844 static void
845 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
846     XvAdaptorInfo * adaptors, int adaptor_no)
847 {
848   gint j;
849   gint res;
850
851   /* Do we support XvImageMask ? */
852   if (!(adaptors[adaptor_no].type & XvImageMask)) {
853     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
854         adaptors[adaptor_no].name);
855     return;
856   }
857
858   /* We found such an adaptor, looking for an available port */
859   for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
860     /* We try to grab the port */
861     res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
862     if (Success == res) {
863       xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
864       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
865           adaptors[adaptor_no].num_ports);
866     } else {
867       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
868           adaptors[adaptor_no].name, res);
869     }
870   }
871 }
872
873 /* This function generates a caps with all supported format by the first
874    Xv grabable port we find. We store each one of the supported formats in a
875    format list and append the format to a newly created caps that we return
876    If this function does not return NULL because of an error, it also grabs
877    the port via XvGrabPort */
878 static GstCaps *
879 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
880     GstXContext * xcontext)
881 {
882   gint i;
883   XvAdaptorInfo *adaptors;
884   gint nb_formats;
885   XvImageFormatValues *formats = NULL;
886   guint nb_encodings;
887   XvEncodingInfo *encodings = NULL;
888   gulong max_w = G_MAXINT, max_h = G_MAXINT;
889   GstCaps *caps = NULL;
890   GstCaps *rgb_caps = NULL;
891
892   g_return_val_if_fail (xcontext != NULL, NULL);
893
894   /* First let's check that XVideo extension is available */
895   if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
896     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
897         ("Could not initialise Xv output"),
898         ("XVideo extension is not available"));
899     return NULL;
900   }
901
902   /* Then we get adaptors list */
903   if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
904           &xcontext->nb_adaptors, &adaptors)) {
905     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
906         ("Could not initialise Xv output"),
907         ("Failed getting XV adaptors list"));
908     return NULL;
909   }
910
911   xcontext->xv_port_id = 0;
912
913   GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
914
915   xcontext->adaptors =
916       (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
917
918   /* Now fill up our adaptor name array */
919   for (i = 0; i < xcontext->nb_adaptors; i++) {
920     xcontext->adaptors[i] = g_strdup (adaptors[i].name);
921   }
922
923   if (xvimagesink->adaptor_no != -1 &&
924       xvimagesink->adaptor_no < xcontext->nb_adaptors) {
925     /* Find xv port from user defined adaptor */
926     gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
927         xvimagesink->adaptor_no);
928   }
929
930   if (!xcontext->xv_port_id) {
931     /* Now search for an adaptor that supports XvImageMask */
932     for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
933       gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
934       xvimagesink->adaptor_no = i;
935     }
936   }
937
938   XvFreeAdaptorInfo (adaptors);
939
940   if (!xcontext->xv_port_id) {
941     xvimagesink->adaptor_no = -1;
942     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
943         ("Could not initialise Xv output"), ("No port available"));
944     return NULL;
945   }
946
947   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
948   {
949     int count, todo = 3;
950     XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
951         xcontext->xv_port_id, &count);
952     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
953     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
954     static const char colorkey[] = "XV_COLORKEY";
955
956     GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
957
958     xvimagesink->have_autopaint_colorkey = FALSE;
959     xvimagesink->have_double_buffer = FALSE;
960     xvimagesink->have_colorkey = FALSE;
961
962     for (i = 0; ((i < count) && todo); i++)
963       if (!strcmp (attr[i].name, autopaint)) {
964         const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
965
966         /* turn on autopaint colorkey */
967         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
968             (xvimagesink->autopaint_colorkey ? 1 : 0));
969         todo--;
970         xvimagesink->have_autopaint_colorkey = TRUE;
971       } else if (!strcmp (attr[i].name, dbl_buffer)) {
972         const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
973
974         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
975             (xvimagesink->double_buffer ? 1 : 0));
976         todo--;
977         xvimagesink->have_double_buffer = TRUE;
978       } else if (!strcmp (attr[i].name, colorkey)) {
979         /* Set the colorkey, default is something that is dark but hopefully
980          * won't randomly appear on the screen elsewhere (ie not black or greys)
981          * can be overridden by setting "colorkey" property
982          */
983         const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
984         guint32 ckey = 0;
985         gboolean set_attr = TRUE;
986         guint cr, cg, cb;
987
988         /* set a colorkey in the right format RGB565/RGB888
989          * We only handle these 2 cases, because they're the only types of
990          * devices we've encountered. If we don't recognise it, leave it alone
991          */
992         cr = (xvimagesink->colorkey >> 16);
993         cg = (xvimagesink->colorkey >> 8) & 0xFF;
994         cb = (xvimagesink->colorkey) & 0xFF;
995         switch (xcontext->depth) {
996           case 16:             /* RGB 565 */
997             cr >>= 3;
998             cg >>= 2;
999             cb >>= 3;
1000             ckey = (cr << 11) | (cg << 5) | cb;
1001             break;
1002           case 24:
1003           case 32:             /* RGB 888 / ARGB 8888 */
1004             ckey = (cr << 16) | (cg << 8) | cb;
1005             break;
1006           default:
1007             GST_DEBUG_OBJECT (xvimagesink,
1008                 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1009                 xcontext->depth);
1010             set_attr = FALSE;
1011             break;
1012         }
1013
1014         if (set_attr) {
1015           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1016               (guint32) attr[i].max_value);
1017           GST_LOG_OBJECT (xvimagesink,
1018               "Setting color key for display depth %d to 0x%x",
1019               xcontext->depth, ckey);
1020
1021           XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1022               (gint) ckey);
1023         }
1024         todo--;
1025         xvimagesink->have_colorkey = TRUE;
1026       }
1027
1028     XFree (attr);
1029   }
1030
1031   /* Get the list of encodings supported by the adapter and look for the
1032    * XV_IMAGE encoding so we can determine the maximum width and height
1033    * supported */
1034   XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1035       &encodings);
1036
1037   for (i = 0; i < nb_encodings; i++) {
1038     GST_LOG_OBJECT (xvimagesink,
1039         "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1040         i, encodings[i].name, encodings[i].width, encodings[i].height,
1041         encodings[i].rate.numerator, encodings[i].rate.denominator);
1042     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1043       max_w = encodings[i].width;
1044       max_h = encodings[i].height;
1045     }
1046   }
1047
1048   XvFreeEncodingInfo (encodings);
1049
1050   /* We get all image formats supported by our port */
1051   formats = XvListImageFormats (xcontext->disp,
1052       xcontext->xv_port_id, &nb_formats);
1053   caps = gst_caps_new_empty ();
1054   for (i = 0; i < nb_formats; i++) {
1055     GstCaps *format_caps = NULL;
1056     gboolean is_rgb_format = FALSE;
1057     GstVideoFormat vformat;
1058
1059     /* We set the image format of the xcontext to an existing one. This
1060        is just some valid image format for making our xshm calls check before
1061        caps negotiation really happens. */
1062     xcontext->im_format = formats[i].id;
1063
1064     switch (formats[i].type) {
1065       case XvRGB:
1066       {
1067         XvImageFormatValues *fmt = &(formats[i]);
1068         gint endianness;
1069
1070         endianness =
1071             (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1072
1073         vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1074             endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1075         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1076           break;
1077
1078         format_caps = gst_caps_new_simple ("video/x-raw",
1079             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1080             "width", GST_TYPE_INT_RANGE, 1, max_w,
1081             "height", GST_TYPE_INT_RANGE, 1, max_h,
1082             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1083
1084         is_rgb_format = TRUE;
1085         break;
1086       }
1087       case XvYUV:
1088       {
1089         vformat = gst_video_format_from_fourcc (formats[i].id);
1090         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1091           break;
1092
1093         format_caps = gst_caps_new_simple ("video/x-raw",
1094             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1095             "width", GST_TYPE_INT_RANGE, 1, max_w,
1096             "height", GST_TYPE_INT_RANGE, 1, max_h,
1097             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1098         break;
1099       }
1100       default:
1101         vformat = GST_VIDEO_FORMAT_UNKNOWN;
1102         g_assert_not_reached ();
1103         break;
1104     }
1105
1106     if (format_caps) {
1107       GstXvImageFormat *format = NULL;
1108
1109       format = g_new0 (GstXvImageFormat, 1);
1110       if (format) {
1111         format->format = formats[i].id;
1112         format->vformat = vformat;
1113         format->caps = gst_caps_copy (format_caps);
1114         xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1115       }
1116
1117       if (is_rgb_format) {
1118         if (rgb_caps == NULL)
1119           rgb_caps = format_caps;
1120         else
1121           gst_caps_append (rgb_caps, format_caps);
1122       } else
1123         gst_caps_append (caps, format_caps);
1124     }
1125   }
1126
1127   /* Collected all caps into either the caps or rgb_caps structures.
1128    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1129   if (rgb_caps)
1130     gst_caps_append (caps, rgb_caps);
1131
1132   if (formats)
1133     XFree (formats);
1134
1135   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1136
1137   if (gst_caps_is_empty (caps)) {
1138     gst_caps_unref (caps);
1139     XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1140     GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1141         ("No supported format found"));
1142     return NULL;
1143   }
1144
1145   return caps;
1146 }
1147
1148 static gpointer
1149 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1150 {
1151   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1152
1153   GST_OBJECT_LOCK (xvimagesink);
1154   while (xvimagesink->running) {
1155     GST_OBJECT_UNLOCK (xvimagesink);
1156
1157     if (xvimagesink->xwindow) {
1158       gst_xvimagesink_handle_xevents (xvimagesink);
1159     }
1160     /* FIXME: do we want to align this with the framerate or anything else? */
1161     g_usleep (G_USEC_PER_SEC / 20);
1162
1163     GST_OBJECT_LOCK (xvimagesink);
1164   }
1165   GST_OBJECT_UNLOCK (xvimagesink);
1166
1167   return NULL;
1168 }
1169
1170 static void
1171 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1172 {
1173   GThread *thread = NULL;
1174
1175   /* don't start the thread too early */
1176   if (xvimagesink->xcontext == NULL) {
1177     return;
1178   }
1179
1180   GST_OBJECT_LOCK (xvimagesink);
1181   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1182     if (!xvimagesink->event_thread) {
1183       /* Setup our event listening thread */
1184       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1185           xvimagesink->handle_expose, xvimagesink->handle_events);
1186       xvimagesink->running = TRUE;
1187       xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1188           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1189     }
1190   } else {
1191     if (xvimagesink->event_thread) {
1192       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1193           xvimagesink->handle_expose, xvimagesink->handle_events);
1194       xvimagesink->running = FALSE;
1195       /* grab thread and mark it as NULL */
1196       thread = xvimagesink->event_thread;
1197       xvimagesink->event_thread = NULL;
1198     }
1199   }
1200   GST_OBJECT_UNLOCK (xvimagesink);
1201
1202   /* Wait for our event thread to finish */
1203   if (thread)
1204     g_thread_join (thread);
1205
1206 }
1207
1208
1209 /* This function calculates the pixel aspect ratio based on the properties
1210  * in the xcontext structure and stores it there. */
1211 static void
1212 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1213 {
1214   static const gint par[][2] = {
1215     {1, 1},                     /* regular screen */
1216     {16, 15},                   /* PAL TV */
1217     {11, 10},                   /* 525 line Rec.601 video */
1218     {54, 59},                   /* 625 line Rec.601 video */
1219     {64, 45},                   /* 1280x1024 on 16:9 display */
1220     {5, 3},                     /* 1280x1024 on 4:3 display */
1221     {4, 3}                      /*  800x600 on 16:9 display */
1222   };
1223   gint i;
1224   gint index;
1225   gdouble ratio;
1226   gdouble delta;
1227
1228 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1229
1230   /* first calculate the "real" ratio based on the X values;
1231    * which is the "physical" w/h divided by the w/h in pixels of the display */
1232   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1233       / (xcontext->heightmm * xcontext->width);
1234
1235   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1236    * override here */
1237   if (xcontext->width == 720 && xcontext->height == 576) {
1238     ratio = 4.0 * 576 / (3.0 * 720);
1239   }
1240   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1241
1242   /* now find the one from par[][2] with the lowest delta to the real one */
1243   delta = DELTA (0);
1244   index = 0;
1245
1246   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1247     gdouble this_delta = DELTA (i);
1248
1249     if (this_delta < delta) {
1250       index = i;
1251       delta = this_delta;
1252     }
1253   }
1254
1255   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1256       par[index][0], par[index][1]);
1257
1258   g_free (xcontext->par);
1259   xcontext->par = g_new0 (GValue, 1);
1260   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1261   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1262   GST_DEBUG ("set xcontext PAR to %d/%d",
1263       gst_value_get_fraction_numerator (xcontext->par),
1264       gst_value_get_fraction_denominator (xcontext->par));
1265 }
1266
1267 /* This function gets the X Display and global info about it. Everything is
1268    stored in our object and will be cleaned when the object is disposed. Note
1269    here that caps for supported format are generated without any window or
1270    image creation */
1271 static GstXContext *
1272 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1273 {
1274   GstXContext *xcontext = NULL;
1275   XPixmapFormatValues *px_formats = NULL;
1276   gint nb_formats = 0, i, j, N_attr;
1277   XvAttribute *xv_attr;
1278   Atom prop_atom;
1279   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1280     "XV_BRIGHTNESS", "XV_CONTRAST"
1281   };
1282
1283   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1284
1285   xcontext = g_new0 (GstXContext, 1);
1286   xcontext->im_format = 0;
1287
1288   g_mutex_lock (xvimagesink->x_lock);
1289
1290   xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1291
1292   if (!xcontext->disp) {
1293     g_mutex_unlock (xvimagesink->x_lock);
1294     g_free (xcontext);
1295     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1296         ("Could not initialise Xv output"), ("Could not open display"));
1297     return NULL;
1298   }
1299
1300   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1301   xcontext->screen_num = DefaultScreen (xcontext->disp);
1302   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1303   xcontext->root = DefaultRootWindow (xcontext->disp);
1304   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1305   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1306   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1307
1308   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1309   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1310   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1311   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1312
1313   GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1314       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1315
1316   gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1317   /* We get supported pixmap formats at supported depth */
1318   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1319
1320   if (!px_formats) {
1321     XCloseDisplay (xcontext->disp);
1322     g_mutex_unlock (xvimagesink->x_lock);
1323     g_free (xcontext->par);
1324     g_free (xcontext);
1325     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1326         ("Could not initialise Xv output"), ("Could not get pixel formats"));
1327     return NULL;
1328   }
1329
1330   /* We get bpp value corresponding to our running depth */
1331   for (i = 0; i < nb_formats; i++) {
1332     if (px_formats[i].depth == xcontext->depth)
1333       xcontext->bpp = px_formats[i].bits_per_pixel;
1334   }
1335
1336   XFree (px_formats);
1337
1338   xcontext->endianness =
1339       (ImageByteOrder (xcontext->disp) ==
1340       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1341
1342   /* our caps system handles 24/32bpp RGB as big-endian. */
1343   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1344       xcontext->endianness == G_LITTLE_ENDIAN) {
1345     xcontext->endianness = G_BIG_ENDIAN;
1346     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1347     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1348     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1349     if (xcontext->bpp == 24) {
1350       xcontext->visual->red_mask >>= 8;
1351       xcontext->visual->green_mask >>= 8;
1352       xcontext->visual->blue_mask >>= 8;
1353     }
1354   }
1355
1356   xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1357
1358   /* Search for XShm extension support */
1359 #ifdef HAVE_XSHM
1360   if (XShmQueryExtension (xcontext->disp) &&
1361       gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1362     xcontext->use_xshm = TRUE;
1363     GST_DEBUG ("xvimagesink is using XShm extension");
1364   } else
1365 #endif /* HAVE_XSHM */
1366   {
1367     xcontext->use_xshm = FALSE;
1368     GST_DEBUG ("xvimagesink is not using XShm extension");
1369   }
1370
1371   if (!xcontext->caps) {
1372     XCloseDisplay (xcontext->disp);
1373     g_mutex_unlock (xvimagesink->x_lock);
1374     g_free (xcontext->par);
1375     g_free (xcontext);
1376     /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1377     return NULL;
1378   }
1379
1380   xv_attr = XvQueryPortAttributes (xcontext->disp,
1381       xcontext->xv_port_id, &N_attr);
1382
1383
1384   /* Generate the channels list */
1385   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1386     XvAttribute *matching_attr = NULL;
1387
1388     /* Retrieve the property atom if it exists. If it doesn't exist,
1389      * the attribute itself must not either, so we can skip */
1390     prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1391     if (prop_atom == None)
1392       continue;
1393
1394     if (xv_attr != NULL) {
1395       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1396         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1397           matching_attr = xv_attr + j;
1398     }
1399
1400     if (matching_attr) {
1401       GstColorBalanceChannel *channel;
1402
1403       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1404       channel->label = g_strdup (channels[i]);
1405       channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1406       channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1407
1408       xcontext->channels_list = g_list_append (xcontext->channels_list,
1409           channel);
1410
1411       /* If the colorbalance settings have not been touched we get Xv values
1412          as defaults and update our internal variables */
1413       if (!xvimagesink->cb_changed) {
1414         gint val;
1415
1416         XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1417             prop_atom, &val);
1418         /* Normalize val to [-1000, 1000] */
1419         val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1420             (double) (channel->max_value - channel->min_value));
1421
1422         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1423           xvimagesink->hue = val;
1424         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1425           xvimagesink->saturation = val;
1426         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1427           xvimagesink->brightness = val;
1428         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1429           xvimagesink->contrast = val;
1430       }
1431     }
1432   }
1433
1434   if (xv_attr)
1435     XFree (xv_attr);
1436
1437   g_mutex_unlock (xvimagesink->x_lock);
1438
1439   return xcontext;
1440 }
1441
1442 /* This function cleans the X context. Closing the Display, releasing the XV
1443    port and unrefing the caps for supported formats. */
1444 static void
1445 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1446 {
1447   GList *formats_list, *channels_list;
1448   GstXContext *xcontext;
1449   gint i = 0;
1450
1451   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1452
1453   GST_OBJECT_LOCK (xvimagesink);
1454   if (xvimagesink->xcontext == NULL) {
1455     GST_OBJECT_UNLOCK (xvimagesink);
1456     return;
1457   }
1458
1459   /* Take the XContext from the sink and clean it up */
1460   xcontext = xvimagesink->xcontext;
1461   xvimagesink->xcontext = NULL;
1462
1463   GST_OBJECT_UNLOCK (xvimagesink);
1464
1465
1466   formats_list = xcontext->formats_list;
1467
1468   while (formats_list) {
1469     GstXvImageFormat *format = formats_list->data;
1470
1471     gst_caps_unref (format->caps);
1472     g_free (format);
1473     formats_list = g_list_next (formats_list);
1474   }
1475
1476   if (xcontext->formats_list)
1477     g_list_free (xcontext->formats_list);
1478
1479   channels_list = xcontext->channels_list;
1480
1481   while (channels_list) {
1482     GstColorBalanceChannel *channel = channels_list->data;
1483
1484     g_object_unref (channel);
1485     channels_list = g_list_next (channels_list);
1486   }
1487
1488   if (xcontext->channels_list)
1489     g_list_free (xcontext->channels_list);
1490
1491   gst_caps_unref (xcontext->caps);
1492   if (xcontext->last_caps)
1493     gst_caps_replace (&xcontext->last_caps, NULL);
1494
1495   for (i = 0; i < xcontext->nb_adaptors; i++) {
1496     g_free (xcontext->adaptors[i]);
1497   }
1498
1499   g_free (xcontext->adaptors);
1500
1501   g_free (xcontext->par);
1502
1503   g_mutex_lock (xvimagesink->x_lock);
1504
1505   GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1506
1507   XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1508
1509   XCloseDisplay (xcontext->disp);
1510
1511   g_mutex_unlock (xvimagesink->x_lock);
1512
1513   g_free (xcontext);
1514 }
1515
1516 /* Element stuff */
1517
1518 static GstCaps *
1519 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1520 {
1521   GstXvImageSink *xvimagesink;
1522   GstCaps *caps;
1523
1524   xvimagesink = GST_XVIMAGESINK (bsink);
1525
1526   if (xvimagesink->xcontext) {
1527     if (filter)
1528       return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1529           GST_CAPS_INTERSECT_FIRST);
1530     else
1531       return gst_caps_ref (xvimagesink->xcontext->caps);
1532   }
1533
1534   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1535   if (filter) {
1536     GstCaps *intersection;
1537
1538     intersection =
1539         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1540     gst_caps_unref (caps);
1541     caps = intersection;
1542   }
1543   return caps;
1544 }
1545
1546 static gboolean
1547 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1548 {
1549   GstXvImageSink *xvimagesink;
1550   GstStructure *structure;
1551   GstBufferPool *newpool, *oldpool;
1552   GstVideoInfo info;
1553   guint32 im_format = 0;
1554   gint video_par_n, video_par_d;        /* video's PAR */
1555   gint display_par_n, display_par_d;    /* display's PAR */
1556   guint num, den;
1557   gint size;
1558   static GstAllocationParams params = { 0, 15, 0, 0, };
1559
1560   xvimagesink = GST_XVIMAGESINK (bsink);
1561
1562   GST_DEBUG_OBJECT (xvimagesink,
1563       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1564       GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1565
1566   if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1567     goto incompatible_caps;
1568
1569   if (!gst_video_info_from_caps (&info, caps))
1570     goto invalid_format;
1571
1572   xvimagesink->fps_n = info.fps_n;
1573   xvimagesink->fps_d = info.fps_d;
1574
1575   xvimagesink->video_width = info.width;
1576   xvimagesink->video_height = info.height;
1577
1578   im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1579   if (im_format == -1)
1580     goto invalid_format;
1581
1582   size = info.size;
1583
1584   /* get aspect ratio from caps if it's present, and
1585    * convert video width and height to a display width and height
1586    * using wd / hd = wv / hv * PARv / PARd */
1587
1588   /* get video's PAR */
1589   video_par_n = info.par_n;
1590   video_par_d = info.par_d;
1591
1592   /* get display's PAR */
1593   if (xvimagesink->par) {
1594     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1595     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1596   } else {
1597     display_par_n = 1;
1598     display_par_d = 1;
1599   }
1600
1601   if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1602           info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1603     goto no_disp_ratio;
1604
1605   GST_DEBUG_OBJECT (xvimagesink,
1606       "video width/height: %dx%d, calculated display ratio: %d/%d",
1607       info.width, info.height, num, den);
1608
1609   /* now find a width x height that respects this display ratio.
1610    * prefer those that have one of w/h the same as the incoming video
1611    * using wd / hd = num / den */
1612
1613   /* start with same height, because of interlaced video */
1614   /* check hd / den is an integer scale factor, and scale wd with the PAR */
1615   if (info.height % den == 0) {
1616     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1617     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1618         gst_util_uint64_scale_int (info.height, num, den);
1619     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1620   } else if (info.width % num == 0) {
1621     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1622     GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1623     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1624         gst_util_uint64_scale_int (info.width, den, num);
1625   } else {
1626     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1627     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1628         gst_util_uint64_scale_int (info.height, num, den);
1629     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1630   }
1631   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1632       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1633
1634   /* Notify application to set xwindow id now */
1635   g_mutex_lock (xvimagesink->flow_lock);
1636   if (!xvimagesink->xwindow) {
1637     g_mutex_unlock (xvimagesink->flow_lock);
1638     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1639   } else {
1640     g_mutex_unlock (xvimagesink->flow_lock);
1641   }
1642
1643   /* Creating our window and our image with the display size in pixels */
1644   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1645       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1646     goto no_display_size;
1647
1648   g_mutex_lock (xvimagesink->flow_lock);
1649   if (!xvimagesink->xwindow) {
1650     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1651         GST_VIDEO_SINK_WIDTH (xvimagesink),
1652         GST_VIDEO_SINK_HEIGHT (xvimagesink));
1653   }
1654
1655   xvimagesink->info = info;
1656
1657   /* After a resize, we want to redraw the borders in case the new frame size
1658    * doesn't cover the same area */
1659   xvimagesink->redraw_border = TRUE;
1660
1661   /* create a new pool for the new configuration */
1662   newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1663
1664   structure = gst_buffer_pool_get_config (newpool);
1665   gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1666   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
1667   if (!gst_buffer_pool_set_config (newpool, structure))
1668     goto config_failed;
1669
1670   oldpool = xvimagesink->pool;
1671   /* we don't activate the pool yet, this will be done by downstream after it
1672    * has configured the pool. If downstream does not want our pool we will
1673    * activate it when we render into it */
1674   xvimagesink->pool = newpool;
1675   g_mutex_unlock (xvimagesink->flow_lock);
1676
1677   /* unref the old sink */
1678   if (oldpool) {
1679     /* we don't deactivate, some elements might still be using it, it will
1680      * be deactivated when the last ref is gone */
1681     gst_object_unref (oldpool);
1682   }
1683
1684   return TRUE;
1685
1686   /* ERRORS */
1687 incompatible_caps:
1688   {
1689     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1690     return FALSE;
1691   }
1692 invalid_format:
1693   {
1694     GST_DEBUG_OBJECT (xvimagesink,
1695         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1696     return FALSE;
1697   }
1698 no_disp_ratio:
1699   {
1700     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1701         ("Error calculating the output display ratio of the video."));
1702     return FALSE;
1703   }
1704 no_display_size:
1705   {
1706     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1707         ("Error calculating the output display ratio of the video."));
1708     return FALSE;
1709   }
1710 config_failed:
1711   {
1712     GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1713     g_mutex_unlock (xvimagesink->flow_lock);
1714     return FALSE;
1715   }
1716 }
1717
1718 static GstStateChangeReturn
1719 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1720 {
1721   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1722   GstXvImageSink *xvimagesink;
1723   GstXContext *xcontext = NULL;
1724
1725   xvimagesink = GST_XVIMAGESINK (element);
1726
1727   switch (transition) {
1728     case GST_STATE_CHANGE_NULL_TO_READY:
1729       /* Initializing the XContext */
1730       if (xvimagesink->xcontext == NULL) {
1731         xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1732         if (xcontext == NULL) {
1733           ret = GST_STATE_CHANGE_FAILURE;
1734           goto beach;
1735         }
1736         GST_OBJECT_LOCK (xvimagesink);
1737         if (xcontext)
1738           xvimagesink->xcontext = xcontext;
1739         GST_OBJECT_UNLOCK (xvimagesink);
1740       }
1741
1742       /* update object's par with calculated one if not set yet */
1743       if (!xvimagesink->par) {
1744         xvimagesink->par = g_new0 (GValue, 1);
1745         gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1746         GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1747       }
1748       /* call XSynchronize with the current value of synchronous */
1749       GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1750           xvimagesink->synchronous ? "TRUE" : "FALSE");
1751       XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1752       gst_xvimagesink_update_colorbalance (xvimagesink);
1753       gst_xvimagesink_manage_event_thread (xvimagesink);
1754       break;
1755     case GST_STATE_CHANGE_READY_TO_PAUSED:
1756       break;
1757     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1758       break;
1759     case GST_STATE_CHANGE_PAUSED_TO_READY:
1760       break;
1761     default:
1762       break;
1763   }
1764
1765   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1766
1767   switch (transition) {
1768     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1769       break;
1770     case GST_STATE_CHANGE_PAUSED_TO_READY:
1771       xvimagesink->fps_n = 0;
1772       xvimagesink->fps_d = 1;
1773       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1774       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1775       g_mutex_lock (xvimagesink->flow_lock);
1776       if (xvimagesink->pool)
1777         gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1778       g_mutex_unlock (xvimagesink->flow_lock);
1779       break;
1780     case GST_STATE_CHANGE_READY_TO_NULL:
1781       gst_xvimagesink_reset (xvimagesink);
1782       break;
1783     default:
1784       break;
1785   }
1786
1787 beach:
1788   return ret;
1789 }
1790
1791 static void
1792 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1793     GstClockTime * start, GstClockTime * end)
1794 {
1795   GstXvImageSink *xvimagesink;
1796
1797   xvimagesink = GST_XVIMAGESINK (bsink);
1798
1799   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1800     *start = GST_BUFFER_TIMESTAMP (buf);
1801     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1802       *end = *start + GST_BUFFER_DURATION (buf);
1803     } else {
1804       if (xvimagesink->fps_n > 0) {
1805         *end = *start +
1806             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1807             xvimagesink->fps_n);
1808       }
1809     }
1810   }
1811 }
1812
1813 static GstFlowReturn
1814 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1815 {
1816   GstFlowReturn res;
1817   GstXvImageSink *xvimagesink;
1818   GstXvImageMeta *meta;
1819   GstBuffer *to_put;
1820
1821   xvimagesink = GST_XVIMAGESINK (vsink);
1822
1823   meta = gst_buffer_get_xvimage_meta (buf);
1824
1825   if (meta && meta->sink == xvimagesink) {
1826     /* If this buffer has been allocated using our buffer management we simply
1827        put the ximage which is in the PRIVATE pointer */
1828     GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1829         buf);
1830     to_put = buf;
1831     res = GST_FLOW_OK;
1832   } else {
1833     GstVideoFrame src, dest;
1834
1835     /* Else we have to copy the data into our private image, */
1836     /* if we have one... */
1837     GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1838
1839     /* we should have a pool, configured in setcaps */
1840     if (xvimagesink->pool == NULL)
1841       goto no_pool;
1842
1843     if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1844       goto activate_failed;
1845
1846     /* take a buffer from our pool */
1847     res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
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 res;
1890   }
1891 invalid_buffer:
1892   {
1893     /* No Window available to put our image into */
1894     GST_WARNING_OBJECT (xvimagesink, "could 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   if (xvimagesink->x_lock) {
2746     g_mutex_free (xvimagesink->x_lock);
2747     xvimagesink->x_lock = NULL;
2748   }
2749   if (xvimagesink->flow_lock) {
2750     g_mutex_free (xvimagesink->flow_lock);
2751     xvimagesink->flow_lock = NULL;
2752   }
2753
2754   g_free (xvimagesink->media_title);
2755
2756   G_OBJECT_CLASS (parent_class)->finalize (object);
2757 }
2758
2759 static void
2760 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2761 {
2762   xvimagesink->display_name = NULL;
2763   xvimagesink->adaptor_no = 0;
2764   xvimagesink->xcontext = NULL;
2765   xvimagesink->xwindow = NULL;
2766   xvimagesink->cur_image = NULL;
2767
2768   xvimagesink->hue = xvimagesink->saturation = 0;
2769   xvimagesink->contrast = xvimagesink->brightness = 0;
2770   xvimagesink->cb_changed = FALSE;
2771
2772   xvimagesink->fps_n = 0;
2773   xvimagesink->fps_d = 0;
2774   xvimagesink->video_width = 0;
2775   xvimagesink->video_height = 0;
2776
2777   xvimagesink->x_lock = g_mutex_new ();
2778   xvimagesink->flow_lock = g_mutex_new ();
2779
2780   xvimagesink->pool = NULL;
2781
2782   xvimagesink->synchronous = FALSE;
2783   xvimagesink->double_buffer = TRUE;
2784   xvimagesink->running = FALSE;
2785   xvimagesink->keep_aspect = TRUE;
2786   xvimagesink->handle_events = TRUE;
2787   xvimagesink->par = NULL;
2788   xvimagesink->handle_expose = TRUE;
2789   xvimagesink->autopaint_colorkey = TRUE;
2790
2791   /* on 16bit displays this becomes r,g,b = 1,2,3
2792    * on 24bit displays this becomes r,g,b = 8,8,16
2793    * as a port atom value
2794    */
2795   xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2796   xvimagesink->draw_borders = TRUE;
2797 }
2798
2799 static void
2800 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2801 {
2802   GObjectClass *gobject_class;
2803   GstElementClass *gstelement_class;
2804   GstBaseSinkClass *gstbasesink_class;
2805   GstVideoSinkClass *videosink_class;
2806
2807   gobject_class = (GObjectClass *) klass;
2808   gstelement_class = (GstElementClass *) klass;
2809   gstbasesink_class = (GstBaseSinkClass *) klass;
2810   videosink_class = (GstVideoSinkClass *) klass;
2811
2812   parent_class = g_type_class_peek_parent (klass);
2813
2814   gobject_class->set_property = gst_xvimagesink_set_property;
2815   gobject_class->get_property = gst_xvimagesink_get_property;
2816
2817   g_object_class_install_property (gobject_class, PROP_CONTRAST,
2818       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2819           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2820   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2821       g_param_spec_int ("brightness", "Brightness",
2822           "The brightness of the video", -1000, 1000, 0,
2823           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824   g_object_class_install_property (gobject_class, PROP_HUE,
2825       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2826           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2827   g_object_class_install_property (gobject_class, PROP_SATURATION,
2828       g_param_spec_int ("saturation", "Saturation",
2829           "The saturation of the video", -1000, 1000, 0,
2830           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2832       g_param_spec_string ("display", "Display", "X Display name", NULL,
2833           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2834   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2835       g_param_spec_boolean ("synchronous", "Synchronous",
2836           "When enabled, runs the X display in synchronous mode. "
2837           "(unrelated to A/V sync, used only for debugging)", FALSE,
2838           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2839   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2840       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2841           "The pixel aspect ratio of the device", "1/1",
2842           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2844       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2845           "When enabled, scaling will respect original aspect ratio", TRUE,
2846           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2847   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2848       g_param_spec_boolean ("handle-events", "Handle XEvents",
2849           "When enabled, XEvents will be selected and handled", TRUE,
2850           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2851   g_object_class_install_property (gobject_class, PROP_DEVICE,
2852       g_param_spec_string ("device", "Adaptor number",
2853           "The number of the video adaptor", "0",
2854           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2855   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2856       g_param_spec_string ("device-name", "Adaptor name",
2857           "The name of the video adaptor", NULL,
2858           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2859   /**
2860    * GstXvImageSink:handle-expose
2861    *
2862    * When enabled, the current frame will always be drawn in response to X
2863    * Expose.
2864    *
2865    * Since: 0.10.14
2866    */
2867   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2868       g_param_spec_boolean ("handle-expose", "Handle expose",
2869           "When enabled, "
2870           "the current frame will always be drawn in response to X Expose "
2871           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2872
2873   /**
2874    * GstXvImageSink:double-buffer
2875    *
2876    * Whether to double-buffer the output.
2877    *
2878    * Since: 0.10.14
2879    */
2880   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2881       g_param_spec_boolean ("double-buffer", "Double-buffer",
2882           "Whether to double-buffer the output", TRUE,
2883           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2884   /**
2885    * GstXvImageSink:autopaint-colorkey
2886    *
2887    * Whether to autofill overlay with colorkey
2888    *
2889    * Since: 0.10.21
2890    */
2891   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2892       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2893           "Whether to autofill overlay with colorkey", TRUE,
2894           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2895   /**
2896    * GstXvImageSink:colorkey
2897    *
2898    * Color to use for the overlay mask.
2899    *
2900    * Since: 0.10.21
2901    */
2902   g_object_class_install_property (gobject_class, PROP_COLORKEY,
2903       g_param_spec_int ("colorkey", "Colorkey",
2904           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2905           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2906
2907   /**
2908    * GstXvImageSink:draw-borders
2909    *
2910    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2911    * unused parts of the video area.
2912    *
2913    * Since: 0.10.21
2914    */
2915   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2916       g_param_spec_boolean ("draw-borders", "Colorkey",
2917           "Draw black borders to fill unused area in force-aspect-ratio mode",
2918           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2919
2920   /**
2921    * GstXvImageSink:window-width
2922    *
2923    * Actual width of the video window.
2924    *
2925    * Since: 0.10.32
2926    */
2927   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2928       g_param_spec_uint64 ("window-width", "window-width",
2929           "Width of the window", 0, G_MAXUINT64, 0,
2930           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2931
2932   /**
2933    * GstXvImageSink:window-height
2934    *
2935    * Actual height of the video window.
2936    *
2937    * Since: 0.10.32
2938    */
2939   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2940       g_param_spec_uint64 ("window-height", "window-height",
2941           "Height of the window", 0, G_MAXUINT64, 0,
2942           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2943
2944   gobject_class->finalize = gst_xvimagesink_finalize;
2945
2946   gst_element_class_set_static_metadata (gstelement_class,
2947       "Video sink", "Sink/Video",
2948       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2949
2950   gst_element_class_add_pad_template (gstelement_class,
2951       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2952
2953   gstelement_class->change_state =
2954       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2955
2956   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2957   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2958   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2959   gstbasesink_class->propose_allocation =
2960       GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2961   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2962
2963   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
2964 }