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