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