xvimagesink: fix leak when images are freed after the X context
[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-yuv, 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 #ifdef HAVE_CONFIG_H
113 #include "config.h"
114 #endif
115
116 /* Our interfaces */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
123
124 /* Object header */
125 #include "xvimagesink.h"
126
127 /* Debugging category */
128 #include <gst/gstinfo.h>
129
130 #include "gst/glib-compat-private.h"
131
132 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
133 #define GST_CAT_DEFAULT gst_debug_xvimagesink
134 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
135
136 typedef struct
137 {
138   unsigned long flags;
139   unsigned long functions;
140   unsigned long decorations;
141   long input_mode;
142   unsigned long status;
143 }
144 MotifWmHints, MwmHints;
145
146 #define MWM_HINTS_DECORATIONS   (1L << 1)
147
148 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
149
150 static GstBufferClass *xvimage_buffer_parent_class = NULL;
151 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
152
153 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
154     xvimagesink);
155 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
156     GstCaps * caps);
157 static void gst_xvimagesink_expose (GstXOverlay * 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-rgb, "
166         "framerate = (fraction) [ 0, MAX ], "
167         "width = (int) [ 1, MAX ], "
168         "height = (int) [ 1, MAX ]; "
169         "video/x-raw-yuv, "
170         "framerate = (fraction) [ 0, MAX ], "
171         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
172     );
173
174 enum
175 {
176   PROP_0,
177   PROP_CONTRAST,
178   PROP_BRIGHTNESS,
179   PROP_HUE,
180   PROP_SATURATION,
181   PROP_DISPLAY,
182   PROP_SYNCHRONOUS,
183   PROP_PIXEL_ASPECT_RATIO,
184   PROP_FORCE_ASPECT_RATIO,
185   PROP_HANDLE_EVENTS,
186   PROP_DEVICE,
187   PROP_DEVICE_NAME,
188   PROP_HANDLE_EXPOSE,
189   PROP_DOUBLE_BUFFER,
190   PROP_AUTOPAINT_COLORKEY,
191   PROP_COLORKEY,
192   PROP_DRAW_BORDERS,
193   PROP_WINDOW_WIDTH,
194   PROP_WINDOW_HEIGHT
195 };
196
197 static void gst_xvimagesink_init_interfaces (GType type);
198
199 GST_BOILERPLATE_FULL (GstXvImageSink, gst_xvimagesink, GstVideoSink,
200     GST_TYPE_VIDEO_SINK, gst_xvimagesink_init_interfaces);
201
202
203 /* ============================================================= */
204 /*                                                               */
205 /*                       Private Methods                         */
206 /*                                                               */
207 /* ============================================================= */
208
209 /* xvimage buffers */
210
211 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
212
213 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
214 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
215 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
216
217 /* This function destroys a GstXvImage handling XShm availability */
218 static void
219 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
220 {
221   GstXvImageSink *xvimagesink;
222
223   GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
224
225   xvimagesink = xvimage->xvimagesink;
226   if (G_UNLIKELY (xvimagesink == NULL))
227     goto no_sink;
228
229   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
230
231   GST_OBJECT_LOCK (xvimagesink);
232
233   /* If the destroyed image is the current one we destroy our reference too */
234   if (xvimagesink->cur_image == xvimage)
235     xvimagesink->cur_image = NULL;
236
237   /* We might have some buffers destroyed after changing state to NULL */
238   if (xvimagesink->xcontext == NULL) {
239     GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
240 #ifdef HAVE_XSHM
241     /* Need to free the shared memory segment even if the x context
242      * was already cleaned up */
243     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
244       shmdt (xvimage->SHMInfo.shmaddr);
245     }
246 #endif
247     if (xvimage->xvimage)
248       XFree (xvimage->xvimage);
249     goto beach;
250   }
251
252   g_mutex_lock (xvimagesink->x_lock);
253
254 #ifdef HAVE_XSHM
255   if (xvimagesink->xcontext->use_xshm) {
256     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
257       GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
258           xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
259       XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
260       XSync (xvimagesink->xcontext->disp, FALSE);
261
262       shmdt (xvimage->SHMInfo.shmaddr);
263     }
264     if (xvimage->xvimage)
265       XFree (xvimage->xvimage);
266   } else
267 #endif /* HAVE_XSHM */
268   {
269     if (xvimage->xvimage) {
270       if (xvimage->xvimage->data) {
271         g_free (xvimage->xvimage->data);
272       }
273       XFree (xvimage->xvimage);
274     }
275   }
276
277   XSync (xvimagesink->xcontext->disp, FALSE);
278
279   g_mutex_unlock (xvimagesink->x_lock);
280
281 beach:
282   GST_OBJECT_UNLOCK (xvimagesink);
283   xvimage->xvimagesink = NULL;
284   gst_object_unref (xvimagesink);
285
286   GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
287       (xvimage));
288
289   return;
290
291 no_sink:
292   {
293     GST_WARNING ("no sink found");
294     return;
295   }
296 }
297
298 static void
299 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
300 {
301   GstXvImageSink *xvimagesink;
302   gboolean running;
303
304   xvimagesink = xvimage->xvimagesink;
305   if (G_UNLIKELY (xvimagesink == NULL))
306     goto no_sink;
307
308   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
309
310   GST_OBJECT_LOCK (xvimagesink);
311   running = xvimagesink->running;
312   GST_OBJECT_UNLOCK (xvimagesink);
313
314   /* If our geometry changed we can't reuse that image. */
315   if (running == FALSE) {
316     GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
317     gst_xvimage_buffer_destroy (xvimage);
318   } else if ((xvimage->width != xvimagesink->video_width) ||
319       (xvimage->height != xvimagesink->video_height)) {
320     GST_LOG_OBJECT (xvimage,
321         "destroy image as its size changed %dx%d vs current %dx%d",
322         xvimage->width, xvimage->height,
323         xvimagesink->video_width, xvimagesink->video_height);
324     gst_xvimage_buffer_destroy (xvimage);
325   } else {
326     /* In that case we can reuse the image and add it to our image pool. */
327     GST_LOG_OBJECT (xvimage, "recycling image in pool");
328     /* need to increment the refcount again to recycle */
329     gst_buffer_ref (GST_BUFFER_CAST (xvimage));
330     g_mutex_lock (xvimagesink->pool_lock);
331     xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
332         xvimage);
333     g_mutex_unlock (xvimagesink->pool_lock);
334   }
335   return;
336
337 no_sink:
338   {
339     GST_WARNING ("no sink found");
340     return;
341   }
342 }
343
344 static void
345 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
346 {
347   /* make sure it is not recycled */
348   xvimage->width = -1;
349   xvimage->height = -1;
350   gst_buffer_unref (GST_BUFFER (xvimage));
351 }
352
353 static void
354 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
355 {
356 #ifdef HAVE_XSHM
357   xvimage->SHMInfo.shmaddr = ((void *) -1);
358   xvimage->SHMInfo.shmid = -1;
359 #endif
360 }
361
362 static void
363 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
364 {
365   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
366
367   xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
368
369   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
370       gst_xvimage_buffer_finalize;
371 }
372
373 static GType
374 gst_xvimage_buffer_get_type (void)
375 {
376   static GType _gst_xvimage_buffer_type;
377
378   if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
379     static const GTypeInfo xvimage_buffer_info = {
380       sizeof (GstBufferClass),
381       NULL,
382       NULL,
383       gst_xvimage_buffer_class_init,
384       NULL,
385       NULL,
386       sizeof (GstXvImageBuffer),
387       0,
388       (GInstanceInitFunc) gst_xvimage_buffer_init,
389       NULL
390     };
391     _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
392         "GstXvImageBuffer", &xvimage_buffer_info, 0);
393   }
394   return _gst_xvimage_buffer_type;
395 }
396
397 /* X11 stuff */
398
399 static gboolean error_caught = FALSE;
400
401 static int
402 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
403 {
404   char error_msg[1024];
405
406   XGetErrorText (display, xevent->error_code, error_msg, 1024);
407   GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
408   error_caught = TRUE;
409   return 0;
410 }
411
412 #ifdef HAVE_XSHM
413 /* This function checks that it is actually really possible to create an image
414    using XShm */
415 static gboolean
416 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
417 {
418   XvImage *xvimage;
419   XShmSegmentInfo SHMInfo;
420   gint size;
421   int (*handler) (Display *, XErrorEvent *);
422   gboolean result = FALSE;
423   gboolean did_attach = FALSE;
424
425   g_return_val_if_fail (xcontext != NULL, FALSE);
426
427   /* Sync to ensure any older errors are already processed */
428   XSync (xcontext->disp, FALSE);
429
430   /* Set defaults so we don't free these later unnecessarily */
431   SHMInfo.shmaddr = ((void *) -1);
432   SHMInfo.shmid = -1;
433
434   /* Setting an error handler to catch failure */
435   error_caught = FALSE;
436   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
437
438   /* Trying to create a 1x1 picture */
439   GST_DEBUG ("XvShmCreateImage of 1x1");
440   xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
441       xcontext->im_format, NULL, 1, 1, &SHMInfo);
442
443   /* Might cause an error, sync to ensure it is noticed */
444   XSync (xcontext->disp, FALSE);
445   if (!xvimage || error_caught) {
446     GST_WARNING ("could not XvShmCreateImage a 1x1 image");
447     goto beach;
448   }
449   size = xvimage->data_size;
450
451   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
452   if (SHMInfo.shmid == -1) {
453     GST_WARNING ("could not get shared memory of %d bytes", size);
454     goto beach;
455   }
456
457   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
458   if (SHMInfo.shmaddr == ((void *) -1)) {
459     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
460     /* Clean up the shared memory segment */
461     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
462     goto beach;
463   }
464
465   xvimage->data = SHMInfo.shmaddr;
466   SHMInfo.readOnly = FALSE;
467
468   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
469     GST_WARNING ("Failed to XShmAttach");
470     /* Clean up the shared memory segment */
471     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
472     goto beach;
473   }
474
475   /* Sync to ensure we see any errors we caused */
476   XSync (xcontext->disp, FALSE);
477
478   /* Delete the shared memory segment as soon as everyone is attached.
479    * This way, it will be deleted as soon as we detach later, and not
480    * leaked if we crash. */
481   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
482
483   if (!error_caught) {
484     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
485         SHMInfo.shmseg);
486
487     did_attach = TRUE;
488     /* store whether we succeeded in result */
489     result = TRUE;
490   } else {
491     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
492         "Not using shared memory.");
493   }
494
495 beach:
496   /* Sync to ensure we swallow any errors we caused and reset error_caught */
497   XSync (xcontext->disp, FALSE);
498
499   error_caught = FALSE;
500   XSetErrorHandler (handler);
501
502   if (did_attach) {
503     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
504         SHMInfo.shmid, SHMInfo.shmseg);
505     XShmDetach (xcontext->disp, &SHMInfo);
506     XSync (xcontext->disp, FALSE);
507   }
508   if (SHMInfo.shmaddr != ((void *) -1))
509     shmdt (SHMInfo.shmaddr);
510   if (xvimage)
511     XFree (xvimage);
512   return result;
513 }
514 #endif /* HAVE_XSHM */
515
516 /* This function handles GstXvImage creation depending on XShm availability */
517 static GstXvImageBuffer *
518 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
519 {
520   GstXvImageBuffer *xvimage = NULL;
521   GstStructure *structure = NULL;
522   gboolean succeeded = FALSE;
523   int (*handler) (Display *, XErrorEvent *);
524
525   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
526
527   if (caps == NULL)
528     return NULL;
529
530   xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
531   GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
532
533   structure = gst_caps_get_structure (caps, 0);
534
535   if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
536       !gst_structure_get_int (structure, "height", &xvimage->height)) {
537     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
538   }
539
540   GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
541       xvimage->height);
542
543   xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
544   if (xvimage->im_format == -1) {
545     GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
546         GST_PTR_FORMAT, caps);
547     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
548         ("Failed to create output image buffer of %dx%d pixels",
549             xvimage->width, xvimage->height), ("Invalid input caps"));
550     goto beach_unlocked;
551   }
552   xvimage->xvimagesink = gst_object_ref (xvimagesink);
553
554   g_mutex_lock (xvimagesink->x_lock);
555
556   /* Setting an error handler to catch failure */
557   error_caught = FALSE;
558   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
559
560 #ifdef HAVE_XSHM
561   if (xvimagesink->xcontext->use_xshm) {
562     int expected_size;
563
564     xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
565         xvimagesink->xcontext->xv_port_id,
566         xvimage->im_format, NULL,
567         xvimage->width, xvimage->height, &xvimage->SHMInfo);
568     if (!xvimage->xvimage || error_caught) {
569       g_mutex_unlock (xvimagesink->x_lock);
570
571       /* Reset error flag */
572       error_caught = FALSE;
573
574       /* Push a warning */
575       GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
576           ("Failed to create output image buffer of %dx%d pixels",
577               xvimage->width, xvimage->height),
578           ("could not XvShmCreateImage a %dx%d image",
579               xvimage->width, xvimage->height));
580
581       /* Retry without XShm */
582       xvimagesink->xcontext->use_xshm = FALSE;
583
584       /* Hold X mutex again to try without XShm */
585       g_mutex_lock (xvimagesink->x_lock);
586       goto no_xshm;
587     }
588
589     /* we have to use the returned data_size for our shm size */
590     xvimage->size = xvimage->xvimage->data_size;
591     GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
592         xvimage->size);
593
594     /* calculate the expected size.  This is only for sanity checking the
595      * number we get from X. */
596     switch (xvimage->im_format) {
597       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
598       case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
599       {
600         gint pitches[3];
601         gint offsets[3];
602         guint plane;
603
604         offsets[0] = 0;
605         pitches[0] = GST_ROUND_UP_4 (xvimage->width);
606         offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
607         pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
608         offsets[2] =
609             offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
610         pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
611
612         expected_size =
613             offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
614
615         for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
616           GST_DEBUG_OBJECT (xvimagesink,
617               "Plane %u has a expected pitch of %d bytes, " "offset of %d",
618               plane, pitches[plane], offsets[plane]);
619         }
620         break;
621       }
622       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
623       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
624         expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
625         break;
626       default:
627         expected_size = 0;
628         break;
629     }
630     if (expected_size != 0 && xvimage->size != expected_size) {
631       GST_WARNING_OBJECT (xvimagesink,
632           "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
633           xvimage->size, expected_size);
634     }
635
636     /* Be verbose about our XvImage stride */
637     {
638       guint plane;
639
640       for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
641         GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
642             "offset of %d", plane, xvimage->xvimage->pitches[plane],
643             xvimage->xvimage->offsets[plane]);
644       }
645     }
646
647     xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
648         IPC_CREAT | 0777);
649     if (xvimage->SHMInfo.shmid == -1) {
650       g_mutex_unlock (xvimagesink->x_lock);
651       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
652           ("Failed to create output image buffer of %dx%d pixels",
653               xvimage->width, xvimage->height),
654           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
655               xvimage->size));
656       goto beach_unlocked;
657     }
658
659     xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
660     if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
661       g_mutex_unlock (xvimagesink->x_lock);
662       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
663           ("Failed to create output image buffer of %dx%d pixels",
664               xvimage->width, xvimage->height),
665           ("Failed to shmat: %s", g_strerror (errno)));
666       /* Clean up the shared memory segment */
667       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
668       goto beach_unlocked;
669     }
670
671     xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
672     xvimage->SHMInfo.readOnly = FALSE;
673
674     if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
675       /* Clean up the shared memory segment */
676       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
677
678       g_mutex_unlock (xvimagesink->x_lock);
679       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
680           ("Failed to create output image buffer of %dx%d pixels",
681               xvimage->width, xvimage->height), ("Failed to XShmAttach"));
682       goto beach_unlocked;
683     }
684
685     XSync (xvimagesink->xcontext->disp, FALSE);
686
687     /* Delete the shared memory segment as soon as we everyone is attached.
688      * This way, it will be deleted as soon as we detach later, and not
689      * leaked if we crash. */
690     shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
691
692     GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
693         xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
694   } else
695   no_xshm:
696 #endif /* HAVE_XSHM */
697   {
698     xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
699         xvimagesink->xcontext->xv_port_id,
700         xvimage->im_format, NULL, xvimage->width, xvimage->height);
701     if (!xvimage->xvimage || error_caught) {
702       g_mutex_unlock (xvimagesink->x_lock);
703       /* Reset error handler */
704       error_caught = FALSE;
705       XSetErrorHandler (handler);
706       /* Push an error */
707       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
708           ("Failed to create outputimage buffer of %dx%d pixels",
709               xvimage->width, xvimage->height),
710           ("could not XvCreateImage a %dx%d image",
711               xvimage->width, xvimage->height));
712       goto beach_unlocked;
713     }
714
715     /* we have to use the returned data_size for our image size */
716     xvimage->size = xvimage->xvimage->data_size;
717     xvimage->xvimage->data = g_malloc (xvimage->size);
718
719     XSync (xvimagesink->xcontext->disp, FALSE);
720   }
721
722   /* Reset error handler */
723   error_caught = FALSE;
724   XSetErrorHandler (handler);
725
726   succeeded = TRUE;
727
728   GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
729   GST_BUFFER_SIZE (xvimage) = xvimage->size;
730
731   g_mutex_unlock (xvimagesink->x_lock);
732
733 beach_unlocked:
734   if (!succeeded) {
735     gst_xvimage_buffer_free (xvimage);
736     xvimage = NULL;
737   }
738
739   return xvimage;
740 }
741
742 /* We are called with the x_lock taken */
743 static void
744 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
745     GstXWindow * xwindow, GstVideoRectangle rect)
746 {
747   gint t1, t2;
748
749   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
750   g_return_if_fail (xwindow != NULL);
751
752   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
753       xvimagesink->xcontext->black);
754
755   /* Left border */
756   if (rect.x > xvimagesink->render_rect.x) {
757     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
758         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
759         rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
760   }
761
762   /* Right border */
763   t1 = rect.x + rect.w;
764   t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
765   if (t1 < t2) {
766     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
767         t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
768   }
769
770   /* Top border */
771   if (rect.y > xvimagesink->render_rect.y) {
772     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
773         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
774         xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
775   }
776
777   /* Bottom border */
778   t1 = rect.y + rect.h;
779   t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
780   if (t1 < t2) {
781     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
782         xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
783   }
784 }
785
786 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
787  * if no window was available  */
788 static gboolean
789 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
790     GstXvImageBuffer * xvimage)
791 {
792   GstVideoRectangle result;
793   gboolean draw_border = FALSE;
794
795   /* We take the flow_lock. If expose is in there we don't want to run
796      concurrently from the data flow thread */
797   g_mutex_lock (xvimagesink->flow_lock);
798
799   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
800     g_mutex_unlock (xvimagesink->flow_lock);
801     return FALSE;
802   }
803
804   /* Draw borders when displaying the first frame. After this
805      draw borders only on expose event or after a size change. */
806   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
807     draw_border = TRUE;
808   }
809
810   /* Store a reference to the last image we put, lose the previous one */
811   if (xvimage && xvimagesink->cur_image != xvimage) {
812     if (xvimagesink->cur_image) {
813       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
814       gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
815     }
816     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
817     xvimagesink->cur_image =
818         GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
819   }
820
821   /* Expose sends a NULL image, we take the latest frame */
822   if (!xvimage) {
823     if (xvimagesink->cur_image) {
824       draw_border = TRUE;
825       xvimage = xvimagesink->cur_image;
826     } else {
827       g_mutex_unlock (xvimagesink->flow_lock);
828       return TRUE;
829     }
830   }
831
832   if (xvimagesink->keep_aspect) {
833     GstVideoRectangle src, dst;
834
835     /* We use the calculated geometry from _setcaps as a source to respect
836        source and screen pixel aspect ratios. */
837     src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
838     src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
839     dst.w = xvimagesink->render_rect.w;
840     dst.h = xvimagesink->render_rect.h;
841
842     gst_video_sink_center_rect (src, dst, &result, TRUE);
843     result.x += xvimagesink->render_rect.x;
844     result.y += xvimagesink->render_rect.y;
845   } else {
846     memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
847   }
848
849   g_mutex_lock (xvimagesink->x_lock);
850
851   if (draw_border && xvimagesink->draw_borders) {
852     gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
853         result);
854     xvimagesink->redraw_border = FALSE;
855   }
856
857   /* We scale to the window's geometry */
858 #ifdef HAVE_XSHM
859   if (xvimagesink->xcontext->use_xshm) {
860     GST_LOG_OBJECT (xvimagesink,
861         "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
862         GST_PTR_FORMAT,
863         xvimage->width, xvimage->height,
864         xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
865
866     XvShmPutImage (xvimagesink->xcontext->disp,
867         xvimagesink->xcontext->xv_port_id,
868         xvimagesink->xwindow->win,
869         xvimagesink->xwindow->gc, xvimage->xvimage,
870         xvimagesink->disp_x, xvimagesink->disp_y,
871         xvimagesink->disp_width, xvimagesink->disp_height,
872         result.x, result.y, result.w, result.h, FALSE);
873   } else
874 #endif /* HAVE_XSHM */
875   {
876     XvPutImage (xvimagesink->xcontext->disp,
877         xvimagesink->xcontext->xv_port_id,
878         xvimagesink->xwindow->win,
879         xvimagesink->xwindow->gc, xvimage->xvimage,
880         xvimagesink->disp_x, xvimagesink->disp_y,
881         xvimagesink->disp_width, xvimagesink->disp_height,
882         result.x, result.y, result.w, result.h);
883   }
884
885   XSync (xvimagesink->xcontext->disp, FALSE);
886
887   g_mutex_unlock (xvimagesink->x_lock);
888
889   g_mutex_unlock (xvimagesink->flow_lock);
890
891   return TRUE;
892 }
893
894 static gboolean
895 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
896     GstXWindow * window)
897 {
898   Atom hints_atom = None;
899   MotifWmHints *hints;
900
901   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
902   g_return_val_if_fail (window != NULL, FALSE);
903
904   g_mutex_lock (xvimagesink->x_lock);
905
906   hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
907       True);
908   if (hints_atom == None) {
909     g_mutex_unlock (xvimagesink->x_lock);
910     return FALSE;
911   }
912
913   hints = g_malloc0 (sizeof (MotifWmHints));
914
915   hints->flags |= MWM_HINTS_DECORATIONS;
916   hints->decorations = 1 << 0;
917
918   XChangeProperty (xvimagesink->xcontext->disp, window->win,
919       hints_atom, hints_atom, 32, PropModeReplace,
920       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
921
922   XSync (xvimagesink->xcontext->disp, FALSE);
923
924   g_mutex_unlock (xvimagesink->x_lock);
925
926   g_free (hints);
927
928   return TRUE;
929 }
930
931 static void
932 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
933     GstXWindow * xwindow, const gchar * media_title)
934 {
935   if (media_title) {
936     g_free (xvimagesink->media_title);
937     xvimagesink->media_title = g_strdup (media_title);
938   }
939   if (xwindow) {
940     /* we have a window */
941     if (xwindow->internal) {
942       XTextProperty xproperty;
943       const gchar *app_name;
944       const gchar *title = NULL;
945       gchar *title_mem = NULL;
946
947       /* set application name as a title */
948       app_name = g_get_application_name ();
949
950       if (app_name && xvimagesink->media_title) {
951         title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
952             app_name, NULL);
953       } else if (app_name) {
954         title = app_name;
955       } else if (xvimagesink->media_title) {
956         title = xvimagesink->media_title;
957       }
958
959       if (title) {
960         if ((XStringListToTextProperty (((char **) &title), 1,
961                     &xproperty)) != 0) {
962           XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
963           XFree (xproperty.value);
964         }
965
966         g_free (title_mem);
967       }
968     }
969   }
970 }
971
972 /* This function handles a GstXWindow creation
973  * The width and height are the actual pixel size on the display */
974 static GstXWindow *
975 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
976     gint width, gint height)
977 {
978   GstXWindow *xwindow = NULL;
979   XGCValues values;
980
981   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
982
983   xwindow = g_new0 (GstXWindow, 1);
984
985   xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
986   xvimagesink->render_rect.w = width;
987   xvimagesink->render_rect.h = height;
988
989   xwindow->width = width;
990   xwindow->height = height;
991   xwindow->internal = TRUE;
992
993   g_mutex_lock (xvimagesink->x_lock);
994
995   xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
996       xvimagesink->xcontext->root,
997       0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
998
999   /* We have to do that to prevent X from redrawing the background on
1000    * ConfigureNotify. This takes away flickering of video when resizing. */
1001   XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
1002
1003   /* set application name as a title */
1004   gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1005
1006   if (xvimagesink->handle_events) {
1007     Atom wm_delete;
1008
1009     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1010         StructureNotifyMask | PointerMotionMask | KeyPressMask |
1011         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1012
1013     /* Tell the window manager we'd like delete client messages instead of
1014      * being killed */
1015     wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1016         "WM_DELETE_WINDOW", True);
1017     if (wm_delete != None) {
1018       (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1019           &wm_delete, 1);
1020     }
1021   }
1022
1023   xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1024       xwindow->win, 0, &values);
1025
1026   XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1027
1028   XSync (xvimagesink->xcontext->disp, FALSE);
1029
1030   g_mutex_unlock (xvimagesink->x_lock);
1031
1032   gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1033
1034   gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1035
1036   return xwindow;
1037 }
1038
1039 /* This function destroys a GstXWindow */
1040 static void
1041 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1042     GstXWindow * xwindow)
1043 {
1044   g_return_if_fail (xwindow != NULL);
1045   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1046
1047   g_mutex_lock (xvimagesink->x_lock);
1048
1049   /* If we did not create that window we just free the GC and let it live */
1050   if (xwindow->internal)
1051     XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1052   else
1053     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1054
1055   XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1056
1057   XSync (xvimagesink->xcontext->disp, FALSE);
1058
1059   g_mutex_unlock (xvimagesink->x_lock);
1060
1061   g_free (xwindow);
1062 }
1063
1064 static void
1065 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1066 {
1067   XWindowAttributes attr;
1068
1069   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1070
1071   /* Update the window geometry */
1072   g_mutex_lock (xvimagesink->x_lock);
1073   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1074     g_mutex_unlock (xvimagesink->x_lock);
1075     return;
1076   }
1077
1078   XGetWindowAttributes (xvimagesink->xcontext->disp,
1079       xvimagesink->xwindow->win, &attr);
1080
1081   xvimagesink->xwindow->width = attr.width;
1082   xvimagesink->xwindow->height = attr.height;
1083
1084   if (!xvimagesink->have_render_rect) {
1085     xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1086     xvimagesink->render_rect.w = attr.width;
1087     xvimagesink->render_rect.h = attr.height;
1088   }
1089
1090   g_mutex_unlock (xvimagesink->x_lock);
1091 }
1092
1093 static void
1094 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1095     GstXWindow * xwindow)
1096 {
1097   g_return_if_fail (xwindow != NULL);
1098   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1099
1100   g_mutex_lock (xvimagesink->x_lock);
1101
1102   XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1103       xwindow->win);
1104
1105   XSync (xvimagesink->xcontext->disp, FALSE);
1106
1107   g_mutex_unlock (xvimagesink->x_lock);
1108 }
1109
1110 /* This function commits our internal colorbalance settings to our grabbed Xv
1111    port. If the xcontext is not initialized yet it simply returns */
1112 static void
1113 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1114 {
1115   GList *channels = NULL;
1116
1117   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1118
1119   /* If we haven't initialized the X context we can't update anything */
1120   if (xvimagesink->xcontext == NULL)
1121     return;
1122
1123   /* Don't set the attributes if they haven't been changed, to avoid
1124    * rounding errors changing the values */
1125   if (!xvimagesink->cb_changed)
1126     return;
1127
1128   /* For each channel of the colorbalance we calculate the correct value
1129      doing range conversion and then set the Xv port attribute to match our
1130      values. */
1131   channels = xvimagesink->xcontext->channels_list;
1132
1133   while (channels) {
1134     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1135       GstColorBalanceChannel *channel = NULL;
1136       Atom prop_atom;
1137       gint value = 0;
1138       gdouble convert_coef;
1139
1140       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1141       g_object_ref (channel);
1142
1143       /* Our range conversion coef */
1144       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1145
1146       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1147         value = xvimagesink->hue;
1148       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1149         value = xvimagesink->saturation;
1150       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1151         value = xvimagesink->contrast;
1152       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1153         value = xvimagesink->brightness;
1154       } else {
1155         g_warning ("got an unknown channel %s", channel->label);
1156         g_object_unref (channel);
1157         return;
1158       }
1159
1160       /* Committing to Xv port */
1161       g_mutex_lock (xvimagesink->x_lock);
1162       prop_atom =
1163           XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1164       if (prop_atom != None) {
1165         int xv_value;
1166         xv_value =
1167             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1168         XvSetPortAttribute (xvimagesink->xcontext->disp,
1169             xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1170       }
1171       g_mutex_unlock (xvimagesink->x_lock);
1172
1173       g_object_unref (channel);
1174     }
1175     channels = g_list_next (channels);
1176   }
1177 }
1178
1179 /* This function handles XEvents that might be in the queue. It generates
1180    GstEvent that will be sent upstream in the pipeline to handle interactivity
1181    and navigation. It will also listen for configure events on the window to
1182    trigger caps renegotiation so on the fly software scaling can work. */
1183 static void
1184 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1185 {
1186   XEvent e;
1187   guint pointer_x = 0, pointer_y = 0;
1188   gboolean pointer_moved = FALSE;
1189   gboolean exposed = FALSE, configured = FALSE;
1190
1191   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1192
1193   /* Handle Interaction, produces navigation events */
1194
1195   /* We get all pointer motion events, only the last position is
1196      interesting. */
1197   g_mutex_lock (xvimagesink->flow_lock);
1198   g_mutex_lock (xvimagesink->x_lock);
1199   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1200           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1201     g_mutex_unlock (xvimagesink->x_lock);
1202     g_mutex_unlock (xvimagesink->flow_lock);
1203
1204     switch (e.type) {
1205       case MotionNotify:
1206         pointer_x = e.xmotion.x;
1207         pointer_y = e.xmotion.y;
1208         pointer_moved = TRUE;
1209         break;
1210       default:
1211         break;
1212     }
1213     g_mutex_lock (xvimagesink->flow_lock);
1214     g_mutex_lock (xvimagesink->x_lock);
1215   }
1216   if (pointer_moved) {
1217     g_mutex_unlock (xvimagesink->x_lock);
1218     g_mutex_unlock (xvimagesink->flow_lock);
1219
1220     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1221         pointer_x, pointer_y);
1222     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1223         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1224
1225     g_mutex_lock (xvimagesink->flow_lock);
1226     g_mutex_lock (xvimagesink->x_lock);
1227   }
1228
1229   /* We get all events on our window to throw them upstream */
1230   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1231           xvimagesink->xwindow->win,
1232           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1233           &e)) {
1234     KeySym keysym;
1235
1236     /* We lock only for the X function call */
1237     g_mutex_unlock (xvimagesink->x_lock);
1238     g_mutex_unlock (xvimagesink->flow_lock);
1239
1240     switch (e.type) {
1241       case ButtonPress:
1242         /* Mouse button pressed over our window. We send upstream
1243            events for interactivity/navigation */
1244         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1245             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1246         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1247             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1248         break;
1249       case ButtonRelease:
1250         /* Mouse button released over our window. We send upstream
1251            events for interactivity/navigation */
1252         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1253             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1254         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1255             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1256         break;
1257       case KeyPress:
1258       case KeyRelease:
1259         /* Key pressed/released over our window. We send upstream
1260            events for interactivity/navigation */
1261         GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1262             e.xkey.keycode, e.xkey.x, e.xkey.y);
1263         g_mutex_lock (xvimagesink->x_lock);
1264         keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1265             e.xkey.keycode, 0);
1266         g_mutex_unlock (xvimagesink->x_lock);
1267         if (keysym != NoSymbol) {
1268           char *key_str = NULL;
1269
1270           g_mutex_lock (xvimagesink->x_lock);
1271           key_str = XKeysymToString (keysym);
1272           g_mutex_unlock (xvimagesink->x_lock);
1273           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1274               e.type == KeyPress ? "key-press" : "key-release", key_str);
1275         } else {
1276           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1277               e.type == KeyPress ? "key-press" : "key-release", "unknown");
1278         }
1279         break;
1280       default:
1281         GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1282     }
1283     g_mutex_lock (xvimagesink->flow_lock);
1284     g_mutex_lock (xvimagesink->x_lock);
1285   }
1286
1287   /* Handle Expose */
1288   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1289           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1290     switch (e.type) {
1291       case Expose:
1292         exposed = TRUE;
1293         break;
1294       case ConfigureNotify:
1295         g_mutex_unlock (xvimagesink->x_lock);
1296         gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1297         g_mutex_lock (xvimagesink->x_lock);
1298         configured = TRUE;
1299         break;
1300       default:
1301         break;
1302     }
1303   }
1304
1305   if (xvimagesink->handle_expose && (exposed || configured)) {
1306     g_mutex_unlock (xvimagesink->x_lock);
1307     g_mutex_unlock (xvimagesink->flow_lock);
1308
1309     gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1310
1311     g_mutex_lock (xvimagesink->flow_lock);
1312     g_mutex_lock (xvimagesink->x_lock);
1313   }
1314
1315   /* Handle Display events */
1316   while (XPending (xvimagesink->xcontext->disp)) {
1317     XNextEvent (xvimagesink->xcontext->disp, &e);
1318
1319     switch (e.type) {
1320       case ClientMessage:{
1321         Atom wm_delete;
1322
1323         wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1324             "WM_DELETE_WINDOW", True);
1325         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1326           /* Handle window deletion by posting an error on the bus */
1327           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1328               ("Output window was closed"), (NULL));
1329
1330           g_mutex_unlock (xvimagesink->x_lock);
1331           gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1332           xvimagesink->xwindow = NULL;
1333           g_mutex_lock (xvimagesink->x_lock);
1334         }
1335         break;
1336       }
1337       default:
1338         break;
1339     }
1340   }
1341
1342   g_mutex_unlock (xvimagesink->x_lock);
1343   g_mutex_unlock (xvimagesink->flow_lock);
1344 }
1345
1346 static void
1347 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1348     XvAdaptorInfo * adaptors, int adaptor_no)
1349 {
1350   gint j;
1351   gint res;
1352
1353   /* Do we support XvImageMask ? */
1354   if (!(adaptors[adaptor_no].type & XvImageMask)) {
1355     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1356         adaptors[adaptor_no].name);
1357     return;
1358   }
1359
1360   /* We found such an adaptor, looking for an available port */
1361   for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1362     /* We try to grab the port */
1363     res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1364     if (Success == res) {
1365       xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1366       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1367           adaptors[adaptor_no].num_ports);
1368     } else {
1369       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1370           adaptors[adaptor_no].name, res);
1371     }
1372   }
1373 }
1374
1375 /* This function generates a caps with all supported format by the first
1376    Xv grabable port we find. We store each one of the supported formats in a
1377    format list and append the format to a newly created caps that we return
1378    If this function does not return NULL because of an error, it also grabs
1379    the port via XvGrabPort */
1380 static GstCaps *
1381 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1382     GstXContext * xcontext)
1383 {
1384   gint i;
1385   XvAdaptorInfo *adaptors;
1386   gint nb_formats;
1387   XvImageFormatValues *formats = NULL;
1388   guint nb_encodings;
1389   XvEncodingInfo *encodings = NULL;
1390   gulong max_w = G_MAXINT, max_h = G_MAXINT;
1391   GstCaps *caps = NULL;
1392   GstCaps *rgb_caps = NULL;
1393
1394   g_return_val_if_fail (xcontext != NULL, NULL);
1395
1396   /* First let's check that XVideo extension is available */
1397   if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1398     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1399         ("Could not initialise Xv output"),
1400         ("XVideo extension is not available"));
1401     return NULL;
1402   }
1403
1404   /* Then we get adaptors list */
1405   if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1406           &xcontext->nb_adaptors, &adaptors)) {
1407     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1408         ("Could not initialise Xv output"),
1409         ("Failed getting XV adaptors list"));
1410     return NULL;
1411   }
1412
1413   xcontext->xv_port_id = 0;
1414
1415   GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1416
1417   xcontext->adaptors =
1418       (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1419
1420   /* Now fill up our adaptor name array */
1421   for (i = 0; i < xcontext->nb_adaptors; i++) {
1422     xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1423   }
1424
1425   if (xvimagesink->adaptor_no >= 0 &&
1426       xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1427     /* Find xv port from user defined adaptor */
1428     gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1429         xvimagesink->adaptor_no);
1430   }
1431
1432   if (!xcontext->xv_port_id) {
1433     /* Now search for an adaptor that supports XvImageMask */
1434     for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1435       gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1436       xvimagesink->adaptor_no = i;
1437     }
1438   }
1439
1440   XvFreeAdaptorInfo (adaptors);
1441
1442   if (!xcontext->xv_port_id) {
1443     xvimagesink->adaptor_no = -1;
1444     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1445         ("Could not initialise Xv output"), ("No port available"));
1446     return NULL;
1447   }
1448
1449   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1450   {
1451     int count, todo = 3;
1452     XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1453         xcontext->xv_port_id, &count);
1454     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1455     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1456     static const char colorkey[] = "XV_COLORKEY";
1457
1458     GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1459
1460     xvimagesink->have_autopaint_colorkey = FALSE;
1461     xvimagesink->have_double_buffer = FALSE;
1462     xvimagesink->have_colorkey = FALSE;
1463
1464     for (i = 0; ((i < count) && todo); i++)
1465       if (!strcmp (attr[i].name, autopaint)) {
1466         const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1467
1468         /* turn on autopaint colorkey */
1469         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1470             (xvimagesink->autopaint_colorkey ? 1 : 0));
1471         todo--;
1472         xvimagesink->have_autopaint_colorkey = TRUE;
1473       } else if (!strcmp (attr[i].name, dbl_buffer)) {
1474         const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1475
1476         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1477             (xvimagesink->double_buffer ? 1 : 0));
1478         todo--;
1479         xvimagesink->have_double_buffer = TRUE;
1480       } else if (!strcmp (attr[i].name, colorkey)) {
1481         /* Set the colorkey, default is something that is dark but hopefully
1482          * won't randomly appear on the screen elsewhere (ie not black or greys)
1483          * can be overridden by setting "colorkey" property
1484          */
1485         const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1486         guint32 ckey = 0;
1487         gboolean set_attr = TRUE;
1488         guint cr, cg, cb;
1489
1490         /* set a colorkey in the right format RGB565/RGB888
1491          * We only handle these 2 cases, because they're the only types of
1492          * devices we've encountered. If we don't recognise it, leave it alone
1493          */
1494         cr = (xvimagesink->colorkey >> 16);
1495         cg = (xvimagesink->colorkey >> 8) & 0xFF;
1496         cb = (xvimagesink->colorkey) & 0xFF;
1497         switch (xcontext->depth) {
1498           case 16:             /* RGB 565 */
1499             cr >>= 3;
1500             cg >>= 2;
1501             cb >>= 3;
1502             ckey = (cr << 11) | (cg << 5) | cb;
1503             break;
1504           case 24:
1505           case 32:             /* RGB 888 / ARGB 8888 */
1506             ckey = (cr << 16) | (cg << 8) | cb;
1507             break;
1508           default:
1509             GST_DEBUG_OBJECT (xvimagesink,
1510                 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1511                 xcontext->depth);
1512             set_attr = FALSE;
1513             break;
1514         }
1515
1516         if (set_attr) {
1517           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1518               (guint32) attr[i].max_value);
1519           GST_LOG_OBJECT (xvimagesink,
1520               "Setting color key for display depth %d to 0x%x",
1521               xcontext->depth, ckey);
1522
1523           XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1524               (gint) ckey);
1525         }
1526         todo--;
1527         xvimagesink->have_colorkey = TRUE;
1528       }
1529
1530     XFree (attr);
1531   }
1532
1533   /* Get the list of encodings supported by the adapter and look for the
1534    * XV_IMAGE encoding so we can determine the maximum width and height
1535    * supported */
1536   XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1537       &encodings);
1538
1539   for (i = 0; i < nb_encodings; i++) {
1540     GST_LOG_OBJECT (xvimagesink,
1541         "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1542         i, encodings[i].name, encodings[i].width, encodings[i].height,
1543         encodings[i].rate.numerator, encodings[i].rate.denominator);
1544     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1545       max_w = encodings[i].width;
1546       max_h = encodings[i].height;
1547     }
1548   }
1549
1550   XvFreeEncodingInfo (encodings);
1551
1552   /* We get all image formats supported by our port */
1553   formats = XvListImageFormats (xcontext->disp,
1554       xcontext->xv_port_id, &nb_formats);
1555   caps = gst_caps_new_empty ();
1556   for (i = 0; i < nb_formats; i++) {
1557     GstCaps *format_caps = NULL;
1558     gboolean is_rgb_format = FALSE;
1559
1560     /* We set the image format of the xcontext to an existing one. This
1561        is just some valid image format for making our xshm calls check before
1562        caps negotiation really happens. */
1563     xcontext->im_format = formats[i].id;
1564
1565     switch (formats[i].type) {
1566       case XvRGB:
1567       {
1568         XvImageFormatValues *fmt = &(formats[i]);
1569         gint endianness = G_BIG_ENDIAN;
1570
1571         if (fmt->byte_order == LSBFirst) {
1572           /* our caps system handles 24/32bpp RGB as big-endian. */
1573           if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1574             fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1575             fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1576             fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1577
1578             if (fmt->bits_per_pixel == 24) {
1579               fmt->red_mask >>= 8;
1580               fmt->green_mask >>= 8;
1581               fmt->blue_mask >>= 8;
1582             }
1583           } else
1584             endianness = G_LITTLE_ENDIAN;
1585         }
1586
1587         format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1588             "endianness", G_TYPE_INT, endianness,
1589             "depth", G_TYPE_INT, fmt->depth,
1590             "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1591             "red_mask", G_TYPE_INT, fmt->red_mask,
1592             "green_mask", G_TYPE_INT, fmt->green_mask,
1593             "blue_mask", G_TYPE_INT, fmt->blue_mask,
1594             "width", GST_TYPE_INT_RANGE, 1, max_w,
1595             "height", GST_TYPE_INT_RANGE, 1, max_h,
1596             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1597
1598         is_rgb_format = TRUE;
1599         break;
1600       }
1601       case XvYUV:
1602         format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1603             "format", GST_TYPE_FOURCC, formats[i].id,
1604             "width", GST_TYPE_INT_RANGE, 1, max_w,
1605             "height", GST_TYPE_INT_RANGE, 1, max_h,
1606             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1607         break;
1608       default:
1609         g_assert_not_reached ();
1610         break;
1611     }
1612
1613     if (format_caps) {
1614       GstXvImageFormat *format = NULL;
1615
1616       format = g_new0 (GstXvImageFormat, 1);
1617       if (format) {
1618         format->format = formats[i].id;
1619         format->caps = gst_caps_copy (format_caps);
1620         xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1621       }
1622
1623       if (is_rgb_format) {
1624         if (rgb_caps == NULL)
1625           rgb_caps = format_caps;
1626         else
1627           gst_caps_append (rgb_caps, format_caps);
1628       } else
1629         gst_caps_append (caps, format_caps);
1630     }
1631   }
1632
1633   /* Collected all caps into either the caps or rgb_caps structures.
1634    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1635   if (rgb_caps)
1636     gst_caps_append (caps, rgb_caps);
1637
1638   if (formats)
1639     XFree (formats);
1640
1641   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1642
1643   if (gst_caps_is_empty (caps)) {
1644     gst_caps_unref (caps);
1645     XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1646     GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1647         ("No supported format found"));
1648     return NULL;
1649   }
1650
1651   return caps;
1652 }
1653
1654 static gpointer
1655 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1656 {
1657   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1658
1659   GST_OBJECT_LOCK (xvimagesink);
1660   while (xvimagesink->running) {
1661     GST_OBJECT_UNLOCK (xvimagesink);
1662
1663     if (xvimagesink->xwindow) {
1664       gst_xvimagesink_handle_xevents (xvimagesink);
1665     }
1666     /* FIXME: do we want to align this with the framerate or anything else? */
1667     g_usleep (G_USEC_PER_SEC / 20);
1668
1669     GST_OBJECT_LOCK (xvimagesink);
1670   }
1671   GST_OBJECT_UNLOCK (xvimagesink);
1672
1673   return NULL;
1674 }
1675
1676 static void
1677 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1678 {
1679   GThread *thread = NULL;
1680
1681   /* don't start the thread too early */
1682   if (xvimagesink->xcontext == NULL) {
1683     return;
1684   }
1685
1686   GST_OBJECT_LOCK (xvimagesink);
1687   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1688     if (!xvimagesink->event_thread) {
1689       /* Setup our event listening thread */
1690       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1691           xvimagesink->handle_expose, xvimagesink->handle_events);
1692       xvimagesink->running = TRUE;
1693 #if !GLIB_CHECK_VERSION (2, 31, 0)
1694       xvimagesink->event_thread = g_thread_create (
1695           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1696 #else
1697       xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1698           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1699 #endif
1700     }
1701   } else {
1702     if (xvimagesink->event_thread) {
1703       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1704           xvimagesink->handle_expose, xvimagesink->handle_events);
1705       xvimagesink->running = FALSE;
1706       /* grab thread and mark it as NULL */
1707       thread = xvimagesink->event_thread;
1708       xvimagesink->event_thread = NULL;
1709     }
1710   }
1711   GST_OBJECT_UNLOCK (xvimagesink);
1712
1713   /* Wait for our event thread to finish */
1714   if (thread)
1715     g_thread_join (thread);
1716
1717 }
1718
1719
1720 /* This function calculates the pixel aspect ratio based on the properties
1721  * in the xcontext structure and stores it there. */
1722 static void
1723 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1724 {
1725   static const gint par[][2] = {
1726     {1, 1},                     /* regular screen */
1727     {16, 15},                   /* PAL TV */
1728     {11, 10},                   /* 525 line Rec.601 video */
1729     {54, 59},                   /* 625 line Rec.601 video */
1730     {64, 45},                   /* 1280x1024 on 16:9 display */
1731     {5, 3},                     /* 1280x1024 on 4:3 display */
1732     {4, 3}                      /*  800x600 on 16:9 display */
1733   };
1734   gint i;
1735   gint index;
1736   gdouble ratio;
1737   gdouble delta;
1738
1739 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1740
1741   /* first calculate the "real" ratio based on the X values;
1742    * which is the "physical" w/h divided by the w/h in pixels of the display */
1743   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1744       / (xcontext->heightmm * xcontext->width);
1745
1746   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1747    * override here */
1748   if (xcontext->width == 720 && xcontext->height == 576) {
1749     ratio = 4.0 * 576 / (3.0 * 720);
1750   }
1751   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1752   /* now find the one from par[][2] with the lowest delta to the real one */
1753   delta = DELTA (0);
1754   index = 0;
1755
1756   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1757     gdouble this_delta = DELTA (i);
1758
1759     if (this_delta < delta) {
1760       index = i;
1761       delta = this_delta;
1762     }
1763   }
1764
1765   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1766       par[index][0], par[index][1]);
1767
1768   g_free (xcontext->par);
1769   xcontext->par = g_new0 (GValue, 1);
1770   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1771   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1772   GST_DEBUG ("set xcontext PAR to %d/%d",
1773       gst_value_get_fraction_numerator (xcontext->par),
1774       gst_value_get_fraction_denominator (xcontext->par));
1775 }
1776
1777 /* This function gets the X Display and global info about it. Everything is
1778    stored in our object and will be cleaned when the object is disposed. Note
1779    here that caps for supported format are generated without any window or
1780    image creation */
1781 static GstXContext *
1782 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1783 {
1784   GstXContext *xcontext = NULL;
1785   XPixmapFormatValues *px_formats = NULL;
1786   gint nb_formats = 0, i, j, N_attr;
1787   XvAttribute *xv_attr;
1788   Atom prop_atom;
1789   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1790     "XV_BRIGHTNESS", "XV_CONTRAST"
1791   };
1792
1793   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1794
1795   xcontext = g_new0 (GstXContext, 1);
1796   xcontext->im_format = 0;
1797
1798   g_mutex_lock (xvimagesink->x_lock);
1799
1800   xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1801
1802   if (!xcontext->disp) {
1803     g_mutex_unlock (xvimagesink->x_lock);
1804     g_free (xcontext);
1805     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1806         ("Could not initialise Xv output"), ("Could not open display"));
1807     return NULL;
1808   }
1809
1810   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1811   xcontext->screen_num = DefaultScreen (xcontext->disp);
1812   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1813   xcontext->root = DefaultRootWindow (xcontext->disp);
1814   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1815   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1816   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1817
1818   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1819   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1820   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1821   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1822
1823   GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1824       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1825
1826   gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1827   /* We get supported pixmap formats at supported depth */
1828   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1829
1830   if (!px_formats) {
1831     XCloseDisplay (xcontext->disp);
1832     g_mutex_unlock (xvimagesink->x_lock);
1833     g_free (xcontext->par);
1834     g_free (xcontext);
1835     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1836         ("Could not initialise Xv output"), ("Could not get pixel formats"));
1837     return NULL;
1838   }
1839
1840   /* We get bpp value corresponding to our running depth */
1841   for (i = 0; i < nb_formats; i++) {
1842     if (px_formats[i].depth == xcontext->depth)
1843       xcontext->bpp = px_formats[i].bits_per_pixel;
1844   }
1845
1846   XFree (px_formats);
1847
1848   xcontext->endianness =
1849       (ImageByteOrder (xcontext->disp) ==
1850       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1851
1852   /* our caps system handles 24/32bpp RGB as big-endian. */
1853   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1854       xcontext->endianness == G_LITTLE_ENDIAN) {
1855     xcontext->endianness = G_BIG_ENDIAN;
1856     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1857     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1858     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1859     if (xcontext->bpp == 24) {
1860       xcontext->visual->red_mask >>= 8;
1861       xcontext->visual->green_mask >>= 8;
1862       xcontext->visual->blue_mask >>= 8;
1863     }
1864   }
1865
1866   xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1867
1868   if (!xcontext->caps) {
1869     XCloseDisplay (xcontext->disp);
1870     g_mutex_unlock (xvimagesink->x_lock);
1871     g_free (xcontext->par);
1872     g_free (xcontext);
1873     /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1874     return NULL;
1875   }
1876 #ifdef HAVE_XSHM
1877   /* Search for XShm extension support */
1878   if (XShmQueryExtension (xcontext->disp) &&
1879       gst_xvimagesink_check_xshm_calls (xcontext)) {
1880     xcontext->use_xshm = TRUE;
1881     GST_DEBUG ("xvimagesink is using XShm extension");
1882   } else
1883 #endif /* HAVE_XSHM */
1884   {
1885     xcontext->use_xshm = FALSE;
1886     GST_DEBUG ("xvimagesink is not using XShm extension");
1887   }
1888
1889   xv_attr = XvQueryPortAttributes (xcontext->disp,
1890       xcontext->xv_port_id, &N_attr);
1891
1892
1893   /* Generate the channels list */
1894   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1895     XvAttribute *matching_attr = NULL;
1896
1897     /* Retrieve the property atom if it exists. If it doesn't exist,
1898      * the attribute itself must not either, so we can skip */
1899     prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1900     if (prop_atom == None)
1901       continue;
1902
1903     if (xv_attr != NULL) {
1904       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1905         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1906           matching_attr = xv_attr + j;
1907     }
1908
1909     if (matching_attr) {
1910       GstColorBalanceChannel *channel;
1911
1912       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1913       channel->label = g_strdup (channels[i]);
1914       channel->min_value = matching_attr->min_value;
1915       channel->max_value = matching_attr->max_value;
1916
1917       xcontext->channels_list = g_list_append (xcontext->channels_list,
1918           channel);
1919
1920       /* If the colorbalance settings have not been touched we get Xv values
1921          as defaults and update our internal variables */
1922       if (!xvimagesink->cb_changed) {
1923         gint val;
1924
1925         XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1926             prop_atom, &val);
1927         /* Normalize val to [-1000, 1000] */
1928         val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1929             (double) (channel->max_value - channel->min_value));
1930
1931         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1932           xvimagesink->hue = val;
1933         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1934           xvimagesink->saturation = val;
1935         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1936           xvimagesink->brightness = val;
1937         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1938           xvimagesink->contrast = val;
1939       }
1940     }
1941   }
1942
1943   if (xv_attr)
1944     XFree (xv_attr);
1945
1946   g_mutex_unlock (xvimagesink->x_lock);
1947
1948   return xcontext;
1949 }
1950
1951 /* This function cleans the X context. Closing the Display, releasing the XV
1952    port and unrefing the caps for supported formats. */
1953 static void
1954 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1955 {
1956   GList *formats_list, *channels_list;
1957   GstXContext *xcontext;
1958   gint i = 0;
1959
1960   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1961
1962   GST_OBJECT_LOCK (xvimagesink);
1963   if (xvimagesink->xcontext == NULL) {
1964     GST_OBJECT_UNLOCK (xvimagesink);
1965     return;
1966   }
1967
1968   /* Take the XContext from the sink and clean it up */
1969   xcontext = xvimagesink->xcontext;
1970   xvimagesink->xcontext = NULL;
1971
1972   GST_OBJECT_UNLOCK (xvimagesink);
1973
1974
1975   formats_list = xcontext->formats_list;
1976
1977   while (formats_list) {
1978     GstXvImageFormat *format = formats_list->data;
1979
1980     gst_caps_unref (format->caps);
1981     g_free (format);
1982     formats_list = g_list_next (formats_list);
1983   }
1984
1985   if (xcontext->formats_list)
1986     g_list_free (xcontext->formats_list);
1987
1988   channels_list = xcontext->channels_list;
1989
1990   while (channels_list) {
1991     GstColorBalanceChannel *channel = channels_list->data;
1992
1993     g_object_unref (channel);
1994     channels_list = g_list_next (channels_list);
1995   }
1996
1997   if (xcontext->channels_list)
1998     g_list_free (xcontext->channels_list);
1999
2000   gst_caps_unref (xcontext->caps);
2001   if (xcontext->last_caps)
2002     gst_caps_replace (&xcontext->last_caps, NULL);
2003
2004   for (i = 0; i < xcontext->nb_adaptors; i++) {
2005     g_free (xcontext->adaptors[i]);
2006   }
2007
2008   g_free (xcontext->adaptors);
2009
2010   g_free (xcontext->par);
2011
2012   g_mutex_lock (xvimagesink->x_lock);
2013
2014   GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
2015
2016   XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2017
2018   XCloseDisplay (xcontext->disp);
2019
2020   g_mutex_unlock (xvimagesink->x_lock);
2021
2022   g_free (xcontext);
2023 }
2024
2025 static void
2026 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2027 {
2028   g_mutex_lock (xvimagesink->pool_lock);
2029
2030   while (xvimagesink->image_pool) {
2031     GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2032
2033     xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2034         xvimagesink->image_pool);
2035     gst_xvimage_buffer_free (xvimage);
2036   }
2037
2038   g_mutex_unlock (xvimagesink->pool_lock);
2039 }
2040
2041 /* Element stuff */
2042
2043 /* This function tries to get a format matching with a given caps in the
2044    supported list of formats we generated in gst_xvimagesink_get_xv_support */
2045 static gint
2046 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2047     GstCaps * caps)
2048 {
2049   GList *list = NULL;
2050
2051   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2052
2053   list = xvimagesink->xcontext->formats_list;
2054
2055   while (list) {
2056     GstXvImageFormat *format = list->data;
2057
2058     if (format) {
2059       if (gst_caps_can_intersect (caps, format->caps)) {
2060         return format->format;
2061       }
2062     }
2063     list = g_list_next (list);
2064   }
2065
2066   return -1;
2067 }
2068
2069 static GstCaps *
2070 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2071 {
2072   GstXvImageSink *xvimagesink;
2073
2074   xvimagesink = GST_XVIMAGESINK (bsink);
2075
2076   if (xvimagesink->xcontext)
2077     return gst_caps_ref (xvimagesink->xcontext->caps);
2078
2079   return
2080       gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2081           (xvimagesink)));
2082 }
2083
2084 static gboolean
2085 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2086 {
2087   GstXvImageSink *xvimagesink;
2088   GstStructure *structure;
2089   guint32 im_format = 0;
2090   gboolean ret;
2091   gint video_width, video_height;
2092   gint disp_x, disp_y;
2093   gint disp_width, disp_height;
2094   gint video_par_n, video_par_d;        /* video's PAR */
2095   gint display_par_n, display_par_d;    /* display's PAR */
2096   const GValue *caps_par;
2097   const GValue *caps_disp_reg;
2098   const GValue *fps;
2099   guint num, den;
2100
2101   xvimagesink = GST_XVIMAGESINK (bsink);
2102
2103   GST_DEBUG_OBJECT (xvimagesink,
2104       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2105       GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2106
2107   if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2108     goto incompatible_caps;
2109
2110   structure = gst_caps_get_structure (caps, 0);
2111   ret = gst_structure_get_int (structure, "width", &video_width);
2112   ret &= gst_structure_get_int (structure, "height", &video_height);
2113   fps = gst_structure_get_value (structure, "framerate");
2114   ret &= (fps != NULL);
2115
2116   if (!ret)
2117     goto incomplete_caps;
2118
2119   xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2120   xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2121
2122   xvimagesink->video_width = video_width;
2123   xvimagesink->video_height = video_height;
2124
2125   im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2126   if (im_format == -1)
2127     goto invalid_format;
2128
2129   /* get aspect ratio from caps if it's present, and
2130    * convert video width and height to a display width and height
2131    * using wd / hd = wv / hv * PARv / PARd */
2132
2133   /* get video's PAR */
2134   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2135   if (caps_par) {
2136     video_par_n = gst_value_get_fraction_numerator (caps_par);
2137     video_par_d = gst_value_get_fraction_denominator (caps_par);
2138   } else {
2139     video_par_n = 1;
2140     video_par_d = 1;
2141   }
2142   /* get display's PAR */
2143   if (xvimagesink->par) {
2144     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2145     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2146   } else {
2147     display_par_n = 1;
2148     display_par_d = 1;
2149   }
2150
2151   /* get the display region */
2152   caps_disp_reg = gst_structure_get_value (structure, "display-region");
2153   if (caps_disp_reg) {
2154     disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2155     disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2156     disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2157     disp_height =
2158         g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2159   } else {
2160     disp_x = disp_y = 0;
2161     disp_width = video_width;
2162     disp_height = video_height;
2163   }
2164
2165   if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2166           video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2167     goto no_disp_ratio;
2168
2169   xvimagesink->disp_x = disp_x;
2170   xvimagesink->disp_y = disp_y;
2171   xvimagesink->disp_width = disp_width;
2172   xvimagesink->disp_height = disp_height;
2173
2174   GST_DEBUG_OBJECT (xvimagesink,
2175       "video width/height: %dx%d, calculated display ratio: %d/%d",
2176       video_width, video_height, num, den);
2177
2178   /* now find a width x height that respects this display ratio.
2179    * prefer those that have one of w/h the same as the incoming video
2180    * using wd / hd = num / den */
2181
2182   /* start with same height, because of interlaced video */
2183   /* check hd / den is an integer scale factor, and scale wd with the PAR */
2184   if (video_height % den == 0) {
2185     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2186     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2187         gst_util_uint64_scale_int (video_height, num, den);
2188     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2189   } else if (video_width % num == 0) {
2190     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2191     GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2192     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2193         gst_util_uint64_scale_int (video_width, den, num);
2194   } else {
2195     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2196     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2197         gst_util_uint64_scale_int (video_height, num, den);
2198     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2199   }
2200   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2201       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2202
2203   /* Notify application to set xwindow id now */
2204   g_mutex_lock (xvimagesink->flow_lock);
2205   if (!xvimagesink->xwindow) {
2206     g_mutex_unlock (xvimagesink->flow_lock);
2207     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2208   } else {
2209     g_mutex_unlock (xvimagesink->flow_lock);
2210   }
2211
2212   /* Creating our window and our image with the display size in pixels */
2213   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2214       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2215     goto no_display_size;
2216
2217   g_mutex_lock (xvimagesink->flow_lock);
2218   if (!xvimagesink->xwindow) {
2219     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2220         GST_VIDEO_SINK_WIDTH (xvimagesink),
2221         GST_VIDEO_SINK_HEIGHT (xvimagesink));
2222   }
2223
2224   /* After a resize, we want to redraw the borders in case the new frame size
2225    * doesn't cover the same area */
2226   xvimagesink->redraw_border = TRUE;
2227
2228   /* We renew our xvimage only if size or format changed;
2229    * the xvimage is the same size as the video pixel size */
2230   if ((xvimagesink->xvimage) &&
2231       ((im_format != xvimagesink->xvimage->im_format) ||
2232           (video_width != xvimagesink->xvimage->width) ||
2233           (video_height != xvimagesink->xvimage->height))) {
2234     GST_DEBUG_OBJECT (xvimagesink,
2235         "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2236         GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2237         GST_FOURCC_ARGS (im_format));
2238     GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2239     gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2240     xvimagesink->xvimage = NULL;
2241   }
2242
2243   g_mutex_unlock (xvimagesink->flow_lock);
2244
2245   return TRUE;
2246
2247   /* ERRORS */
2248 incompatible_caps:
2249   {
2250     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2251     return FALSE;
2252   }
2253 incomplete_caps:
2254   {
2255     GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2256         "height or framerate from intersected caps");
2257     return FALSE;
2258   }
2259 invalid_format:
2260   {
2261     GST_DEBUG_OBJECT (xvimagesink,
2262         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2263     return FALSE;
2264   }
2265 no_disp_ratio:
2266   {
2267     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2268         ("Error calculating the output display ratio of the video."));
2269     return FALSE;
2270   }
2271 no_display_size:
2272   {
2273     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2274         ("Error calculating the output display ratio of the video."));
2275     return FALSE;
2276   }
2277 }
2278
2279 static GstStateChangeReturn
2280 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2281 {
2282   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2283   GstXvImageSink *xvimagesink;
2284   GstXContext *xcontext = NULL;
2285
2286   xvimagesink = GST_XVIMAGESINK (element);
2287
2288   switch (transition) {
2289     case GST_STATE_CHANGE_NULL_TO_READY:
2290       /* Initializing the XContext */
2291       if (xvimagesink->xcontext == NULL) {
2292         xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2293         if (xcontext == NULL)
2294           return GST_STATE_CHANGE_FAILURE;
2295         GST_OBJECT_LOCK (xvimagesink);
2296         if (xcontext)
2297           xvimagesink->xcontext = xcontext;
2298         GST_OBJECT_UNLOCK (xvimagesink);
2299       }
2300
2301       /* update object's par with calculated one if not set yet */
2302       if (!xvimagesink->par) {
2303         xvimagesink->par = g_new0 (GValue, 1);
2304         gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2305         GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2306       }
2307       /* call XSynchronize with the current value of synchronous */
2308       GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2309           xvimagesink->synchronous ? "TRUE" : "FALSE");
2310       XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2311       gst_xvimagesink_update_colorbalance (xvimagesink);
2312       gst_xvimagesink_manage_event_thread (xvimagesink);
2313       break;
2314     case GST_STATE_CHANGE_READY_TO_PAUSED:
2315       g_mutex_lock (xvimagesink->pool_lock);
2316       xvimagesink->pool_invalid = FALSE;
2317       g_mutex_unlock (xvimagesink->pool_lock);
2318       break;
2319     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2320       break;
2321     case GST_STATE_CHANGE_PAUSED_TO_READY:
2322       g_mutex_lock (xvimagesink->pool_lock);
2323       xvimagesink->pool_invalid = TRUE;
2324       g_mutex_unlock (xvimagesink->pool_lock);
2325       break;
2326     default:
2327       break;
2328   }
2329
2330   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2331
2332   switch (transition) {
2333     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2334       break;
2335     case GST_STATE_CHANGE_PAUSED_TO_READY:
2336       xvimagesink->fps_n = 0;
2337       xvimagesink->fps_d = 1;
2338       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2339       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2340       break;
2341     case GST_STATE_CHANGE_READY_TO_NULL:
2342       gst_xvimagesink_reset (xvimagesink);
2343       break;
2344     default:
2345       break;
2346   }
2347
2348   return ret;
2349 }
2350
2351 static void
2352 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2353     GstClockTime * start, GstClockTime * end)
2354 {
2355   GstXvImageSink *xvimagesink;
2356
2357   xvimagesink = GST_XVIMAGESINK (bsink);
2358
2359   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2360     *start = GST_BUFFER_TIMESTAMP (buf);
2361     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2362       *end = *start + GST_BUFFER_DURATION (buf);
2363     } else {
2364       if (xvimagesink->fps_n > 0) {
2365         *end = *start +
2366             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2367             xvimagesink->fps_n);
2368       }
2369     }
2370   }
2371 }
2372
2373 static GstFlowReturn
2374 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2375 {
2376   GstXvImageSink *xvimagesink;
2377
2378   xvimagesink = GST_XVIMAGESINK (vsink);
2379
2380   /* If this buffer has been allocated using our buffer management we simply
2381      put the ximage which is in the PRIVATE pointer */
2382   if (GST_IS_XVIMAGE_BUFFER (buf)) {
2383     GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2384     if (!gst_xvimagesink_xvimage_put (xvimagesink,
2385             GST_XVIMAGE_BUFFER_CAST (buf)))
2386       goto no_window;
2387   } else {
2388     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
2389         "slow copy into bufferpool buffer %p", buf);
2390     /* Else we have to copy the data into our private image, */
2391     /* if we have one... */
2392     if (!xvimagesink->xvimage) {
2393       GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2394
2395       xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2396           GST_BUFFER_CAPS (buf));
2397
2398       if (!xvimagesink->xvimage)
2399         /* The create method should have posted an informative error */
2400         goto no_image;
2401
2402       if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2403         GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2404             ("Failed to create output image buffer of %dx%d pixels",
2405                 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2406             ("XServer allocated buffer size did not match input buffer"));
2407
2408         gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2409         xvimagesink->xvimage = NULL;
2410         goto no_image;
2411       }
2412     }
2413
2414     memcpy (xvimagesink->xvimage->xvimage->data,
2415         GST_BUFFER_DATA (buf),
2416         MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2417
2418     if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2419       goto no_window;
2420   }
2421
2422   return GST_FLOW_OK;
2423
2424   /* ERRORS */
2425 no_image:
2426   {
2427     /* No image available. That's very bad ! */
2428     GST_WARNING_OBJECT (xvimagesink, "could not create image");
2429     return GST_FLOW_ERROR;
2430   }
2431 no_window:
2432   {
2433     /* No Window available to put our image into */
2434     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2435     return GST_FLOW_ERROR;
2436   }
2437 }
2438
2439 static gboolean
2440 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
2441 {
2442   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
2443
2444   switch (GST_EVENT_TYPE (event)) {
2445     case GST_EVENT_TAG:{
2446       GstTagList *l;
2447       gchar *title = NULL;
2448
2449       gst_event_parse_tag (event, &l);
2450       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2451
2452       if (title) {
2453         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
2454         gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
2455             title);
2456
2457         g_free (title);
2458       }
2459       break;
2460     }
2461     default:
2462       break;
2463   }
2464   if (GST_BASE_SINK_CLASS (parent_class)->event)
2465     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2466   else
2467     return TRUE;
2468 }
2469
2470 /* Buffer management */
2471
2472 static GstCaps *
2473 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
2474     GstCaps * caps)
2475 {
2476   GstCaps *intersection;
2477   GstCaps *new_caps;
2478   GstStructure *s;
2479   gint width, height;
2480   gint par_n = 1, par_d = 1;
2481   gint dar_n, dar_d;
2482   gint w, h;
2483
2484   new_caps = gst_caps_copy (caps);
2485
2486   s = gst_caps_get_structure (new_caps, 0);
2487
2488   gst_structure_get_int (s, "width", &width);
2489   gst_structure_get_int (s, "height", &height);
2490   gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
2491
2492   gst_structure_remove_field (s, "width");
2493   gst_structure_remove_field (s, "height");
2494   gst_structure_remove_field (s, "pixel-aspect-ratio");
2495
2496   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2497   gst_caps_unref (new_caps);
2498
2499   if (gst_caps_is_empty (intersection))
2500     return intersection;
2501
2502   s = gst_caps_get_structure (intersection, 0);
2503
2504   gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
2505
2506   /* xvimagesink supports all PARs */
2507
2508   gst_structure_fixate_field_nearest_int (s, "width", width);
2509   gst_structure_fixate_field_nearest_int (s, "height", height);
2510   gst_structure_get_int (s, "width", &w);
2511   gst_structure_get_int (s, "height", &h);
2512
2513   gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
2514   gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
2515       NULL);
2516
2517   return intersection;
2518 }
2519
2520 static GstFlowReturn
2521 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2522     GstCaps * caps, GstBuffer ** buf)
2523 {
2524   GstFlowReturn ret = GST_FLOW_OK;
2525   GstXvImageSink *xvimagesink;
2526   GstXvImageBuffer *xvimage = NULL;
2527   GstCaps *intersection = NULL;
2528   GstStructure *structure = NULL;
2529   gint width, height, image_format;
2530
2531   xvimagesink = GST_XVIMAGESINK (bsink);
2532
2533   if (G_UNLIKELY (!caps))
2534     goto no_caps;
2535
2536   g_mutex_lock (xvimagesink->pool_lock);
2537   if (G_UNLIKELY (xvimagesink->pool_invalid))
2538     goto invalid;
2539
2540   if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2541           gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2542     GST_LOG_OBJECT (xvimagesink,
2543         "buffer alloc for same last_caps, reusing caps");
2544     intersection = gst_caps_ref (caps);
2545     image_format = xvimagesink->xcontext->last_format;
2546     width = xvimagesink->xcontext->last_width;
2547     height = xvimagesink->xcontext->last_height;
2548
2549     goto reuse_last_caps;
2550   }
2551
2552   GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2553       GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2554       caps, xvimagesink->xcontext->caps);
2555
2556   /* Check the caps against our xcontext */
2557   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2558
2559   GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2560       GST_PTR_FORMAT, intersection);
2561
2562   if (gst_caps_is_empty (intersection)) {
2563     GstCaps *new_caps;
2564
2565     gst_caps_unref (intersection);
2566
2567     /* So we don't support this kind of buffer, let's define one we'd like */
2568     new_caps = gst_caps_copy (caps);
2569
2570     structure = gst_caps_get_structure (new_caps, 0);
2571     if (!gst_structure_has_field (structure, "width") ||
2572         !gst_structure_has_field (structure, "height")) {
2573       gst_caps_unref (new_caps);
2574       goto invalid;
2575     }
2576
2577     /* Try different dimensions */
2578     intersection =
2579         gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2580
2581     if (gst_caps_is_empty (intersection)) {
2582       /* Try with different YUV formats first */
2583       gst_structure_set_name (structure, "video/x-raw-yuv");
2584
2585       /* Remove format specific fields */
2586       gst_structure_remove_field (structure, "format");
2587       gst_structure_remove_field (structure, "endianness");
2588       gst_structure_remove_field (structure, "depth");
2589       gst_structure_remove_field (structure, "bpp");
2590       gst_structure_remove_field (structure, "red_mask");
2591       gst_structure_remove_field (structure, "green_mask");
2592       gst_structure_remove_field (structure, "blue_mask");
2593       gst_structure_remove_field (structure, "alpha_mask");
2594
2595       /* Reuse intersection with Xcontext */
2596       intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2597     }
2598
2599     if (gst_caps_is_empty (intersection)) {
2600       /* Try with different dimensions and YUV formats */
2601       intersection =
2602           gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2603     }
2604
2605     if (gst_caps_is_empty (intersection)) {
2606       /* Now try with RGB */
2607       gst_structure_set_name (structure, "video/x-raw-rgb");
2608       /* And interset again */
2609       gst_caps_unref (intersection);
2610       intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2611     }
2612
2613     if (gst_caps_is_empty (intersection)) {
2614       /* Try with different dimensions and RGB formats */
2615       intersection =
2616           gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2617     }
2618
2619     /* Clean this copy */
2620     gst_caps_unref (new_caps);
2621
2622     if (gst_caps_is_empty (intersection))
2623       goto incompatible;
2624   }
2625
2626   /* Ensure the returned caps are fixed */
2627   gst_caps_truncate (intersection);
2628
2629   GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2630       GST_PTR_FORMAT, intersection);
2631   if (gst_caps_is_equal (intersection, caps)) {
2632     /* Things work better if we return a buffer with the same caps ptr
2633      * as was asked for when we can */
2634     gst_caps_replace (&intersection, caps);
2635   }
2636
2637   /* Get image format from caps */
2638   image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2639       intersection);
2640
2641   /* Get geometry from caps */
2642   structure = gst_caps_get_structure (intersection, 0);
2643   if (!gst_structure_get_int (structure, "width", &width) ||
2644       !gst_structure_get_int (structure, "height", &height) ||
2645       image_format == -1)
2646     goto invalid_caps;
2647
2648   /* Store our caps and format as the last_caps to avoid expensive
2649    * caps intersection next time */
2650   gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2651   xvimagesink->xcontext->last_format = image_format;
2652   xvimagesink->xcontext->last_width = width;
2653   xvimagesink->xcontext->last_height = height;
2654
2655 reuse_last_caps:
2656
2657   /* Walking through the pool cleaning unusable images and searching for a
2658      suitable one */
2659   while (xvimagesink->image_pool) {
2660     xvimage = xvimagesink->image_pool->data;
2661
2662     if (xvimage) {
2663       /* Removing from the pool */
2664       xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2665           xvimagesink->image_pool);
2666
2667       /* We check for geometry or image format changes */
2668       if ((xvimage->width != width) ||
2669           (xvimage->height != height) || (xvimage->im_format != image_format)) {
2670         /* This image is unusable. Destroying... */
2671         gst_xvimage_buffer_free (xvimage);
2672         xvimage = NULL;
2673       } else {
2674         /* We found a suitable image */
2675         GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2676         break;
2677       }
2678     }
2679   }
2680
2681   if (!xvimage) {
2682     /* We found no suitable image in the pool. Creating... */
2683     GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2684     xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2685   }
2686   g_mutex_unlock (xvimagesink->pool_lock);
2687
2688   if (xvimage) {
2689     /* Make sure the buffer is cleared of any previously used flags */
2690     GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2691     gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
2692   }
2693
2694   *buf = GST_BUFFER_CAST (xvimage);
2695
2696 beach:
2697   if (intersection) {
2698     gst_caps_unref (intersection);
2699   }
2700
2701   return ret;
2702
2703   /* ERRORS */
2704 invalid:
2705   {
2706     GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2707     ret = GST_FLOW_WRONG_STATE;
2708     g_mutex_unlock (xvimagesink->pool_lock);
2709     goto beach;
2710   }
2711 incompatible:
2712   {
2713     GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2714         "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2715         " are completely incompatible with those caps", caps,
2716         xvimagesink->xcontext->caps);
2717     ret = GST_FLOW_NOT_NEGOTIATED;
2718     g_mutex_unlock (xvimagesink->pool_lock);
2719     goto beach;
2720   }
2721 invalid_caps:
2722   {
2723     GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2724         GST_PTR_FORMAT, intersection);
2725     ret = GST_FLOW_NOT_NEGOTIATED;
2726     g_mutex_unlock (xvimagesink->pool_lock);
2727     goto beach;
2728   }
2729 no_caps:
2730   {
2731     GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
2732     *buf = NULL;
2733     ret = GST_FLOW_OK;
2734     goto beach;
2735   }
2736 }
2737
2738 /* Interfaces stuff */
2739
2740 static gboolean
2741 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2742 {
2743   if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2744       type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2745     return TRUE;
2746   else
2747     return FALSE;
2748 }
2749
2750 static void
2751 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2752 {
2753   klass->supported = gst_xvimagesink_interface_supported;
2754 }
2755
2756 static void
2757 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2758     GstStructure * structure)
2759 {
2760   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2761   GstPad *peer;
2762
2763   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2764     GstEvent *event;
2765     GstVideoRectangle src, dst, result;
2766     gdouble x, y, xscale = 1.0, yscale = 1.0;
2767
2768     event = gst_event_new_navigation (structure);
2769
2770     /* We take the flow_lock while we look at the window */
2771     g_mutex_lock (xvimagesink->flow_lock);
2772
2773     if (!xvimagesink->xwindow) {
2774       g_mutex_unlock (xvimagesink->flow_lock);
2775       return;
2776     }
2777
2778     if (xvimagesink->keep_aspect) {
2779       /* We get the frame position using the calculated geometry from _setcaps
2780          that respect pixel aspect ratios */
2781       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2782       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2783       dst.w = xvimagesink->render_rect.w;
2784       dst.h = xvimagesink->render_rect.h;
2785
2786       gst_video_sink_center_rect (src, dst, &result, TRUE);
2787       result.x += xvimagesink->render_rect.x;
2788       result.y += xvimagesink->render_rect.y;
2789     } else {
2790       memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2791     }
2792
2793     g_mutex_unlock (xvimagesink->flow_lock);
2794
2795     /* We calculate scaling using the original video frames geometry to include
2796        pixel aspect ratio scaling. */
2797     xscale = (gdouble) xvimagesink->video_width / result.w;
2798     yscale = (gdouble) xvimagesink->video_height / result.h;
2799
2800     /* Converting pointer coordinates to the non scaled geometry */
2801     if (gst_structure_get_double (structure, "pointer_x", &x)) {
2802       x = MIN (x, result.x + result.w);
2803       x = MAX (x - result.x, 0);
2804       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2805           (gdouble) x * xscale, NULL);
2806     }
2807     if (gst_structure_get_double (structure, "pointer_y", &y)) {
2808       y = MIN (y, result.y + result.h);
2809       y = MAX (y - result.y, 0);
2810       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2811           (gdouble) y * yscale, NULL);
2812     }
2813
2814     gst_pad_send_event (peer, event);
2815     gst_object_unref (peer);
2816   }
2817 }
2818
2819 static void
2820 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2821 {
2822   iface->send_event = gst_xvimagesink_navigation_send_event;
2823 }
2824
2825 static void
2826 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2827 {
2828   XID xwindow_id = id;
2829   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2830   GstXWindow *xwindow = NULL;
2831
2832   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2833
2834   g_mutex_lock (xvimagesink->flow_lock);
2835
2836   /* If we already use that window return */
2837   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2838     g_mutex_unlock (xvimagesink->flow_lock);
2839     return;
2840   }
2841
2842   /* If the element has not initialized the X11 context try to do so */
2843   if (!xvimagesink->xcontext &&
2844       !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2845     g_mutex_unlock (xvimagesink->flow_lock);
2846     /* we have thrown a GST_ELEMENT_ERROR now */
2847     return;
2848   }
2849
2850   gst_xvimagesink_update_colorbalance (xvimagesink);
2851
2852   /* Clear image pool as the images are unusable anyway */
2853   gst_xvimagesink_imagepool_clear (xvimagesink);
2854
2855   /* Clear the xvimage */
2856   if (xvimagesink->xvimage) {
2857     gst_xvimage_buffer_free (xvimagesink->xvimage);
2858     xvimagesink->xvimage = NULL;
2859   }
2860
2861   /* If a window is there already we destroy it */
2862   if (xvimagesink->xwindow) {
2863     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2864     xvimagesink->xwindow = NULL;
2865   }
2866
2867   /* If the xid is 0 we go back to an internal window */
2868   if (xwindow_id == 0) {
2869     /* If no width/height caps nego did not happen window will be created
2870        during caps nego then */
2871     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2872         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2873       xwindow =
2874           gst_xvimagesink_xwindow_new (xvimagesink,
2875           GST_VIDEO_SINK_WIDTH (xvimagesink),
2876           GST_VIDEO_SINK_HEIGHT (xvimagesink));
2877     }
2878   } else {
2879     XWindowAttributes attr;
2880
2881     xwindow = g_new0 (GstXWindow, 1);
2882     xwindow->win = xwindow_id;
2883
2884     /* Set the event we want to receive and create a GC */
2885     g_mutex_lock (xvimagesink->x_lock);
2886
2887     XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2888
2889     xwindow->width = attr.width;
2890     xwindow->height = attr.height;
2891     xwindow->internal = FALSE;
2892     if (!xvimagesink->have_render_rect) {
2893       xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2894       xvimagesink->render_rect.w = attr.width;
2895       xvimagesink->render_rect.h = attr.height;
2896     }
2897     if (xvimagesink->handle_events) {
2898       XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2899           StructureNotifyMask | PointerMotionMask | KeyPressMask |
2900           KeyReleaseMask);
2901     }
2902
2903     xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2904         xwindow->win, 0, NULL);
2905     g_mutex_unlock (xvimagesink->x_lock);
2906   }
2907
2908   if (xwindow)
2909     xvimagesink->xwindow = xwindow;
2910
2911   g_mutex_unlock (xvimagesink->flow_lock);
2912 }
2913
2914 static void
2915 gst_xvimagesink_expose (GstXOverlay * overlay)
2916 {
2917   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2918
2919   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2920   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2921 }
2922
2923 static void
2924 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2925     gboolean handle_events)
2926 {
2927   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2928
2929   xvimagesink->handle_events = handle_events;
2930
2931   g_mutex_lock (xvimagesink->flow_lock);
2932
2933   if (G_UNLIKELY (!xvimagesink->xwindow)) {
2934     g_mutex_unlock (xvimagesink->flow_lock);
2935     return;
2936   }
2937
2938   g_mutex_lock (xvimagesink->x_lock);
2939
2940   if (handle_events) {
2941     if (xvimagesink->xwindow->internal) {
2942       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2943           ExposureMask | StructureNotifyMask | PointerMotionMask |
2944           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2945     } else {
2946       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2947           ExposureMask | StructureNotifyMask | PointerMotionMask |
2948           KeyPressMask | KeyReleaseMask);
2949     }
2950   } else {
2951     XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2952   }
2953
2954   g_mutex_unlock (xvimagesink->x_lock);
2955
2956   g_mutex_unlock (xvimagesink->flow_lock);
2957 }
2958
2959 static void
2960 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2961     gint width, gint height)
2962 {
2963   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2964
2965   /* FIXME: how about some locking? */
2966   if (width >= 0 && height >= 0) {
2967     xvimagesink->render_rect.x = x;
2968     xvimagesink->render_rect.y = y;
2969     xvimagesink->render_rect.w = width;
2970     xvimagesink->render_rect.h = height;
2971     xvimagesink->have_render_rect = TRUE;
2972   } else {
2973     xvimagesink->render_rect.x = 0;
2974     xvimagesink->render_rect.y = 0;
2975     xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2976     xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2977     xvimagesink->have_render_rect = FALSE;
2978   }
2979 }
2980
2981 static void
2982 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2983 {
2984   iface->set_window_handle = gst_xvimagesink_set_window_handle;
2985   iface->expose = gst_xvimagesink_expose;
2986   iface->handle_events = gst_xvimagesink_set_event_handling;
2987   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2988 }
2989
2990 static const GList *
2991 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2992 {
2993   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2994
2995   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2996
2997   if (xvimagesink->xcontext)
2998     return xvimagesink->xcontext->channels_list;
2999   else
3000     return NULL;
3001 }
3002
3003 static void
3004 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
3005     GstColorBalanceChannel * channel, gint value)
3006 {
3007   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3008
3009   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3010   g_return_if_fail (channel->label != NULL);
3011
3012   xvimagesink->cb_changed = TRUE;
3013
3014   /* Normalize val to [-1000, 1000] */
3015   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
3016       (double) (channel->max_value - channel->min_value));
3017
3018   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3019     xvimagesink->hue = value;
3020   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3021     xvimagesink->saturation = value;
3022   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3023     xvimagesink->contrast = value;
3024   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3025     xvimagesink->brightness = value;
3026   } else {
3027     g_warning ("got an unknown channel %s", channel->label);
3028     return;
3029   }
3030
3031   gst_xvimagesink_update_colorbalance (xvimagesink);
3032 }
3033
3034 static gint
3035 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3036     GstColorBalanceChannel * channel)
3037 {
3038   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3039   gint value = 0;
3040
3041   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3042   g_return_val_if_fail (channel->label != NULL, 0);
3043
3044   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3045     value = xvimagesink->hue;
3046   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3047     value = xvimagesink->saturation;
3048   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3049     value = xvimagesink->contrast;
3050   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3051     value = xvimagesink->brightness;
3052   } else {
3053     g_warning ("got an unknown channel %s", channel->label);
3054   }
3055
3056   /* Normalize val to [channel->min_value, channel->max_value] */
3057   value = channel->min_value + (channel->max_value - channel->min_value) *
3058       (value + 1000) / 2000;
3059
3060   return value;
3061 }
3062
3063 static void
3064 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3065 {
3066   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3067   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3068   iface->set_value = gst_xvimagesink_colorbalance_set_value;
3069   iface->get_value = gst_xvimagesink_colorbalance_get_value;
3070 }
3071
3072 static const GList *
3073 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3074 {
3075   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3076   static GList *list = NULL;
3077
3078   if (!list) {
3079     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3080     list =
3081         g_list_append (list, g_object_class_find_property (klass,
3082             "autopaint-colorkey"));
3083     list =
3084         g_list_append (list, g_object_class_find_property (klass,
3085             "double-buffer"));
3086     list =
3087         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3088   }
3089
3090   return list;
3091 }
3092
3093 static void
3094 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3095     guint prop_id, const GParamSpec * pspec)
3096 {
3097   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3098
3099   switch (prop_id) {
3100     case PROP_DEVICE:
3101     case PROP_AUTOPAINT_COLORKEY:
3102     case PROP_DOUBLE_BUFFER:
3103     case PROP_COLORKEY:
3104       GST_DEBUG_OBJECT (xvimagesink,
3105           "probing device list and get capabilities");
3106       if (!xvimagesink->xcontext) {
3107         GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3108         xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3109       }
3110       break;
3111     default:
3112       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3113       break;
3114   }
3115 }
3116
3117 static gboolean
3118 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3119     guint prop_id, const GParamSpec * pspec)
3120 {
3121   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3122   gboolean ret = FALSE;
3123
3124   switch (prop_id) {
3125     case PROP_DEVICE:
3126     case PROP_AUTOPAINT_COLORKEY:
3127     case PROP_DOUBLE_BUFFER:
3128     case PROP_COLORKEY:
3129       if (xvimagesink->xcontext != NULL) {
3130         ret = FALSE;
3131       } else {
3132         ret = TRUE;
3133       }
3134       break;
3135     default:
3136       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3137       break;
3138   }
3139
3140   return ret;
3141 }
3142
3143 static GValueArray *
3144 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3145     guint prop_id, const GParamSpec * pspec)
3146 {
3147   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3148   GValueArray *array = NULL;
3149
3150   if (G_UNLIKELY (!xvimagesink->xcontext)) {
3151     GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3152         "get values");
3153     goto beach;
3154   }
3155
3156   switch (prop_id) {
3157     case PROP_DEVICE:
3158     {
3159       guint i;
3160       GValue value = { 0 };
3161
3162       array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3163       g_value_init (&value, G_TYPE_STRING);
3164
3165       for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3166         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3167
3168         g_value_set_string (&value, adaptor_id_s);
3169         g_value_array_append (array, &value);
3170         g_free (adaptor_id_s);
3171       }
3172       g_value_unset (&value);
3173       break;
3174     }
3175     case PROP_AUTOPAINT_COLORKEY:
3176       if (xvimagesink->have_autopaint_colorkey) {
3177         GValue value = { 0 };
3178
3179         array = g_value_array_new (2);
3180         g_value_init (&value, G_TYPE_BOOLEAN);
3181         g_value_set_boolean (&value, FALSE);
3182         g_value_array_append (array, &value);
3183         g_value_set_boolean (&value, TRUE);
3184         g_value_array_append (array, &value);
3185         g_value_unset (&value);
3186       }
3187       break;
3188     case PROP_DOUBLE_BUFFER:
3189       if (xvimagesink->have_double_buffer) {
3190         GValue value = { 0 };
3191
3192         array = g_value_array_new (2);
3193         g_value_init (&value, G_TYPE_BOOLEAN);
3194         g_value_set_boolean (&value, FALSE);
3195         g_value_array_append (array, &value);
3196         g_value_set_boolean (&value, TRUE);
3197         g_value_array_append (array, &value);
3198         g_value_unset (&value);
3199       }
3200       break;
3201     case PROP_COLORKEY:
3202       if (xvimagesink->have_colorkey) {
3203         GValue value = { 0 };
3204
3205         array = g_value_array_new (1);
3206         g_value_init (&value, GST_TYPE_INT_RANGE);
3207         gst_value_set_int_range (&value, 0, 0xffffff);
3208         g_value_array_append (array, &value);
3209         g_value_unset (&value);
3210       }
3211       break;
3212     default:
3213       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3214       break;
3215   }
3216
3217 beach:
3218   return array;
3219 }
3220
3221 static void
3222 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3223     iface)
3224 {
3225   iface->get_properties = gst_xvimagesink_probe_get_properties;
3226   iface->probe_property = gst_xvimagesink_probe_probe_property;
3227   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3228   iface->get_values = gst_xvimagesink_probe_get_values;
3229 }
3230
3231 /* =========================================== */
3232 /*                                             */
3233 /*              Init & Class init              */
3234 /*                                             */
3235 /* =========================================== */
3236
3237 static void
3238 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3239     const GValue * value, GParamSpec * pspec)
3240 {
3241   GstXvImageSink *xvimagesink;
3242
3243   g_return_if_fail (GST_IS_XVIMAGESINK (object));
3244
3245   xvimagesink = GST_XVIMAGESINK (object);
3246
3247   switch (prop_id) {
3248     case PROP_HUE:
3249       xvimagesink->hue = g_value_get_int (value);
3250       xvimagesink->cb_changed = TRUE;
3251       gst_xvimagesink_update_colorbalance (xvimagesink);
3252       break;
3253     case PROP_CONTRAST:
3254       xvimagesink->contrast = g_value_get_int (value);
3255       xvimagesink->cb_changed = TRUE;
3256       gst_xvimagesink_update_colorbalance (xvimagesink);
3257       break;
3258     case PROP_BRIGHTNESS:
3259       xvimagesink->brightness = g_value_get_int (value);
3260       xvimagesink->cb_changed = TRUE;
3261       gst_xvimagesink_update_colorbalance (xvimagesink);
3262       break;
3263     case PROP_SATURATION:
3264       xvimagesink->saturation = g_value_get_int (value);
3265       xvimagesink->cb_changed = TRUE;
3266       gst_xvimagesink_update_colorbalance (xvimagesink);
3267       break;
3268     case PROP_DISPLAY:
3269       xvimagesink->display_name = g_strdup (g_value_get_string (value));
3270       break;
3271     case PROP_SYNCHRONOUS:
3272       xvimagesink->synchronous = g_value_get_boolean (value);
3273       if (xvimagesink->xcontext) {
3274         XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3275         GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3276             xvimagesink->synchronous ? "TRUE" : "FALSE");
3277       }
3278       break;
3279     case PROP_PIXEL_ASPECT_RATIO:
3280       g_free (xvimagesink->par);
3281       xvimagesink->par = g_new0 (GValue, 1);
3282       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
3283       if (!g_value_transform (value, xvimagesink->par)) {
3284         g_warning ("Could not transform string to aspect ratio");
3285         gst_value_set_fraction (xvimagesink->par, 1, 1);
3286       }
3287       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
3288           gst_value_get_fraction_numerator (xvimagesink->par),
3289           gst_value_get_fraction_denominator (xvimagesink->par));
3290       break;
3291     case PROP_FORCE_ASPECT_RATIO:
3292       xvimagesink->keep_aspect = g_value_get_boolean (value);
3293       break;
3294     case PROP_HANDLE_EVENTS:
3295       gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
3296           g_value_get_boolean (value));
3297       gst_xvimagesink_manage_event_thread (xvimagesink);
3298       break;
3299     case PROP_DEVICE:
3300       xvimagesink->adaptor_no = atoi (g_value_get_string (value));
3301       break;
3302     case PROP_HANDLE_EXPOSE:
3303       xvimagesink->handle_expose = g_value_get_boolean (value);
3304       gst_xvimagesink_manage_event_thread (xvimagesink);
3305       break;
3306     case PROP_DOUBLE_BUFFER:
3307       xvimagesink->double_buffer = g_value_get_boolean (value);
3308       break;
3309     case PROP_AUTOPAINT_COLORKEY:
3310       xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
3311       break;
3312     case PROP_COLORKEY:
3313       xvimagesink->colorkey = g_value_get_int (value);
3314       break;
3315     case PROP_DRAW_BORDERS:
3316       xvimagesink->draw_borders = g_value_get_boolean (value);
3317       break;
3318     default:
3319       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3320       break;
3321   }
3322 }
3323
3324 static void
3325 gst_xvimagesink_get_property (GObject * object, guint prop_id,
3326     GValue * value, GParamSpec * pspec)
3327 {
3328   GstXvImageSink *xvimagesink;
3329
3330   g_return_if_fail (GST_IS_XVIMAGESINK (object));
3331
3332   xvimagesink = GST_XVIMAGESINK (object);
3333
3334   switch (prop_id) {
3335     case PROP_HUE:
3336       g_value_set_int (value, xvimagesink->hue);
3337       break;
3338     case PROP_CONTRAST:
3339       g_value_set_int (value, xvimagesink->contrast);
3340       break;
3341     case PROP_BRIGHTNESS:
3342       g_value_set_int (value, xvimagesink->brightness);
3343       break;
3344     case PROP_SATURATION:
3345       g_value_set_int (value, xvimagesink->saturation);
3346       break;
3347     case PROP_DISPLAY:
3348       g_value_set_string (value, xvimagesink->display_name);
3349       break;
3350     case PROP_SYNCHRONOUS:
3351       g_value_set_boolean (value, xvimagesink->synchronous);
3352       break;
3353     case PROP_PIXEL_ASPECT_RATIO:
3354       if (xvimagesink->par)
3355         g_value_transform (xvimagesink->par, value);
3356       break;
3357     case PROP_FORCE_ASPECT_RATIO:
3358       g_value_set_boolean (value, xvimagesink->keep_aspect);
3359       break;
3360     case PROP_HANDLE_EVENTS:
3361       g_value_set_boolean (value, xvimagesink->handle_events);
3362       break;
3363     case PROP_DEVICE:
3364     {
3365       char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
3366
3367       g_value_set_string (value, adaptor_no_s);
3368       g_free (adaptor_no_s);
3369       break;
3370     }
3371     case PROP_DEVICE_NAME:
3372       if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
3373         g_value_set_string (value,
3374             xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
3375       } else {
3376         g_value_set_string (value, NULL);
3377       }
3378       break;
3379     case PROP_HANDLE_EXPOSE:
3380       g_value_set_boolean (value, xvimagesink->handle_expose);
3381       break;
3382     case PROP_DOUBLE_BUFFER:
3383       g_value_set_boolean (value, xvimagesink->double_buffer);
3384       break;
3385     case PROP_AUTOPAINT_COLORKEY:
3386       g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
3387       break;
3388     case PROP_COLORKEY:
3389       g_value_set_int (value, xvimagesink->colorkey);
3390       break;
3391     case PROP_DRAW_BORDERS:
3392       g_value_set_boolean (value, xvimagesink->draw_borders);
3393       break;
3394     case PROP_WINDOW_WIDTH:
3395       if (xvimagesink->xwindow)
3396         g_value_set_uint64 (value, xvimagesink->xwindow->width);
3397       else
3398         g_value_set_uint64 (value, 0);
3399       break;
3400     case PROP_WINDOW_HEIGHT:
3401       if (xvimagesink->xwindow)
3402         g_value_set_uint64 (value, xvimagesink->xwindow->height);
3403       else
3404         g_value_set_uint64 (value, 0);
3405       break;
3406     default:
3407       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3408       break;
3409   }
3410 }
3411
3412 static void
3413 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
3414 {
3415   GThread *thread;
3416
3417   GST_OBJECT_LOCK (xvimagesink);
3418   xvimagesink->running = FALSE;
3419   /* grab thread and mark it as NULL */
3420   thread = xvimagesink->event_thread;
3421   xvimagesink->event_thread = NULL;
3422   GST_OBJECT_UNLOCK (xvimagesink);
3423
3424   /* invalidate the pool, current allocations continue, new buffer_alloc fails
3425    * with wrong_state */
3426   g_mutex_lock (xvimagesink->pool_lock);
3427   xvimagesink->pool_invalid = TRUE;
3428   g_mutex_unlock (xvimagesink->pool_lock);
3429
3430   /* Wait for our event thread to finish before we clean up our stuff. */
3431   if (thread)
3432     g_thread_join (thread);
3433
3434   if (xvimagesink->cur_image) {
3435     gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
3436     xvimagesink->cur_image = NULL;
3437   }
3438   if (xvimagesink->xvimage) {
3439     gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
3440     xvimagesink->xvimage = NULL;
3441   }
3442
3443   gst_xvimagesink_imagepool_clear (xvimagesink);
3444
3445   if (xvimagesink->xwindow) {
3446     gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3447     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3448     xvimagesink->xwindow = NULL;
3449   }
3450
3451   xvimagesink->render_rect.x = xvimagesink->render_rect.y =
3452       xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
3453   xvimagesink->have_render_rect = FALSE;
3454
3455   gst_xvimagesink_xcontext_clear (xvimagesink);
3456 }
3457
3458 /* Finalize is called only once, dispose can be called multiple times.
3459  * We use mutexes and don't reset stuff to NULL here so let's register
3460  * as a finalize. */
3461 static void
3462 gst_xvimagesink_finalize (GObject * object)
3463 {
3464   GstXvImageSink *xvimagesink;
3465
3466   xvimagesink = GST_XVIMAGESINK (object);
3467
3468   gst_xvimagesink_reset (xvimagesink);
3469
3470   if (xvimagesink->display_name) {
3471     g_free (xvimagesink->display_name);
3472     xvimagesink->display_name = NULL;
3473   }
3474
3475   if (xvimagesink->par) {
3476     g_free (xvimagesink->par);
3477     xvimagesink->par = NULL;
3478   }
3479   if (xvimagesink->x_lock) {
3480     g_mutex_free (xvimagesink->x_lock);
3481     xvimagesink->x_lock = NULL;
3482   }
3483   if (xvimagesink->flow_lock) {
3484     g_mutex_free (xvimagesink->flow_lock);
3485     xvimagesink->flow_lock = NULL;
3486   }
3487   if (xvimagesink->pool_lock) {
3488     g_mutex_free (xvimagesink->pool_lock);
3489     xvimagesink->pool_lock = NULL;
3490   }
3491
3492   g_free (xvimagesink->media_title);
3493
3494   G_OBJECT_CLASS (parent_class)->finalize (object);
3495 }
3496
3497 static void
3498 gst_xvimagesink_init (GstXvImageSink * xvimagesink,
3499     GstXvImageSinkClass * xvimagesinkclass)
3500 {
3501   xvimagesink->display_name = NULL;
3502   xvimagesink->adaptor_no = 0;
3503   xvimagesink->xcontext = NULL;
3504   xvimagesink->xwindow = NULL;
3505   xvimagesink->xvimage = NULL;
3506   xvimagesink->cur_image = NULL;
3507
3508   xvimagesink->hue = xvimagesink->saturation = 0;
3509   xvimagesink->contrast = xvimagesink->brightness = 0;
3510   xvimagesink->cb_changed = FALSE;
3511
3512   xvimagesink->fps_n = 0;
3513   xvimagesink->fps_d = 0;
3514   xvimagesink->video_width = 0;
3515   xvimagesink->video_height = 0;
3516
3517   xvimagesink->x_lock = g_mutex_new ();
3518   xvimagesink->flow_lock = g_mutex_new ();
3519
3520   xvimagesink->image_pool = NULL;
3521   xvimagesink->pool_lock = g_mutex_new ();
3522
3523   xvimagesink->synchronous = FALSE;
3524   xvimagesink->double_buffer = TRUE;
3525   xvimagesink->running = FALSE;
3526   xvimagesink->keep_aspect = FALSE;
3527   xvimagesink->handle_events = TRUE;
3528   xvimagesink->par = NULL;
3529   xvimagesink->handle_expose = TRUE;
3530   xvimagesink->autopaint_colorkey = TRUE;
3531
3532   /* on 16bit displays this becomes r,g,b = 1,2,3
3533    * on 24bit displays this becomes r,g,b = 8,8,16
3534    * as a port atom value
3535    */
3536   xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
3537   xvimagesink->draw_borders = TRUE;
3538 }
3539
3540 static void
3541 gst_xvimagesink_base_init (gpointer g_class)
3542 {
3543   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3544
3545   gst_element_class_set_details_simple (element_class,
3546       "Video sink", "Sink/Video",
3547       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3548
3549   gst_element_class_add_static_pad_template (element_class,
3550       &gst_xvimagesink_sink_template_factory);
3551 }
3552
3553 static void
3554 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3555 {
3556   GObjectClass *gobject_class;
3557   GstElementClass *gstelement_class;
3558   GstBaseSinkClass *gstbasesink_class;
3559   GstVideoSinkClass *videosink_class;
3560
3561   gobject_class = (GObjectClass *) klass;
3562   gstelement_class = (GstElementClass *) klass;
3563   gstbasesink_class = (GstBaseSinkClass *) klass;
3564   videosink_class = (GstVideoSinkClass *) klass;
3565
3566   gobject_class->set_property = gst_xvimagesink_set_property;
3567   gobject_class->get_property = gst_xvimagesink_get_property;
3568
3569   g_object_class_install_property (gobject_class, PROP_CONTRAST,
3570       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3571           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3572   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
3573       g_param_spec_int ("brightness", "Brightness",
3574           "The brightness of the video", -1000, 1000, 0,
3575           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3576   g_object_class_install_property (gobject_class, PROP_HUE,
3577       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3578           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3579   g_object_class_install_property (gobject_class, PROP_SATURATION,
3580       g_param_spec_int ("saturation", "Saturation",
3581           "The saturation of the video", -1000, 1000, 0,
3582           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3583   g_object_class_install_property (gobject_class, PROP_DISPLAY,
3584       g_param_spec_string ("display", "Display", "X Display name", NULL,
3585           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3586   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
3587       g_param_spec_boolean ("synchronous", "Synchronous",
3588           "When enabled, runs the X display in synchronous mode. "
3589           "(unrelated to A/V sync, used only for debugging)", FALSE,
3590           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3591   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
3592       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3593           "The pixel aspect ratio of the device", "1/1",
3594           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3595   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
3596       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3597           "When enabled, scaling will respect original aspect ratio", FALSE,
3598           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3599   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3600       g_param_spec_boolean ("handle-events", "Handle XEvents",
3601           "When enabled, XEvents will be selected and handled", TRUE,
3602           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3603   g_object_class_install_property (gobject_class, PROP_DEVICE,
3604       g_param_spec_string ("device", "Adaptor number",
3605           "The number of the video adaptor", "0",
3606           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3607   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3608       g_param_spec_string ("device-name", "Adaptor name",
3609           "The name of the video adaptor", NULL,
3610           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3611   /**
3612    * GstXvImageSink:handle-expose
3613    *
3614    * When enabled, the current frame will always be drawn in response to X
3615    * Expose.
3616    *
3617    * Since: 0.10.14
3618    */
3619   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3620       g_param_spec_boolean ("handle-expose", "Handle expose",
3621           "When enabled, "
3622           "the current frame will always be drawn in response to X Expose "
3623           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3624
3625   /**
3626    * GstXvImageSink:double-buffer
3627    *
3628    * Whether to double-buffer the output.
3629    *
3630    * Since: 0.10.14
3631    */
3632   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3633       g_param_spec_boolean ("double-buffer", "Double-buffer",
3634           "Whether to double-buffer the output", TRUE,
3635           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3636   /**
3637    * GstXvImageSink:autopaint-colorkey
3638    *
3639    * Whether to autofill overlay with colorkey
3640    *
3641    * Since: 0.10.21
3642    */
3643   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3644       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3645           "Whether to autofill overlay with colorkey", TRUE,
3646           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3647   /**
3648    * GstXvImageSink:colorkey
3649    *
3650    * Color to use for the overlay mask.
3651    *
3652    * Since: 0.10.21
3653    */
3654   g_object_class_install_property (gobject_class, PROP_COLORKEY,
3655       g_param_spec_int ("colorkey", "Colorkey",
3656           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3657           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3658
3659   /**
3660    * GstXvImageSink:draw-borders
3661    *
3662    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3663    * unused parts of the video area.
3664    *
3665    * Since: 0.10.21
3666    */
3667   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3668       g_param_spec_boolean ("draw-borders", "Colorkey",
3669           "Draw black borders to fill unused area in force-aspect-ratio mode",
3670           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3671
3672   /**
3673    * GstXvImageSink:window-width
3674    *
3675    * Actual width of the video window.
3676    *
3677    * Since: 0.10.32
3678    */
3679   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3680       g_param_spec_uint64 ("window-width", "window-width",
3681           "Width of the window", 0, G_MAXUINT64, 0,
3682           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3683
3684   /**
3685    * GstXvImageSink:window-height
3686    *
3687    * Actual height of the video window.
3688    *
3689    * Since: 0.10.32
3690    */
3691   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3692       g_param_spec_uint64 ("window-height", "window-height",
3693           "Height of the window", 0, G_MAXUINT64, 0,
3694           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3695
3696   gobject_class->finalize = gst_xvimagesink_finalize;
3697
3698   gstelement_class->change_state =
3699       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3700
3701   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3702   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3703   gstbasesink_class->buffer_alloc =
3704       GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3705   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3706   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3707
3708   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3709 }
3710
3711 /* ============================================================= */
3712 /*                                                               */
3713 /*                       Public Methods                          */
3714 /*                                                               */
3715 /* ============================================================= */
3716
3717 /* =========================================== */
3718 /*                                             */
3719 /*          Object typing & Creation           */
3720 /*                                             */
3721 /* =========================================== */
3722 static void
3723 gst_xvimagesink_init_interfaces (GType type)
3724 {
3725   static const GInterfaceInfo iface_info = {
3726     (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3727     NULL,
3728     NULL,
3729   };
3730   static const GInterfaceInfo navigation_info = {
3731     (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3732     NULL,
3733     NULL,
3734   };
3735   static const GInterfaceInfo overlay_info = {
3736     (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3737     NULL,
3738     NULL,
3739   };
3740   static const GInterfaceInfo colorbalance_info = {
3741     (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3742     NULL,
3743     NULL,
3744   };
3745   static const GInterfaceInfo propertyprobe_info = {
3746     (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3747     NULL,
3748     NULL,
3749   };
3750
3751   g_type_add_interface_static (type,
3752       GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3753   g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
3754   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
3755   g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
3756       &colorbalance_info);
3757   g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
3758       &propertyprobe_info);
3759
3760   /* register type and create class in a more safe place instead of at
3761    * runtime since the type registration and class creation is not
3762    * threadsafe. */
3763   g_type_class_ref (gst_xvimage_buffer_get_type ());
3764 }
3765
3766 static gboolean
3767 plugin_init (GstPlugin * plugin)
3768 {
3769   if (!gst_element_register (plugin, "xvimagesink",
3770           GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3771     return FALSE;
3772
3773   GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3774       "xvimagesink element");
3775   GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
3776
3777   return TRUE;
3778 }
3779
3780 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3781     GST_VERSION_MINOR,
3782     "xvimagesink",
3783     "XFree86 video output plugin using Xv extension",
3784     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)