120aef4ade33c94672deeb6c79185be697194e48
[framework/multimedia/gst-plugins-base0.10.git] / sys / xvimage / xvimagesink.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *               <2009>,<2010> Stefan Kost <stefan.kost@nokia.com> 
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:element-xvimagesink
23  *
24  * XvImageSink renders video frames to a drawable (XWindow) on a local display
25  * using the XVideo extension. Rendering to a remote display is theorically
26  * possible but i doubt that the XVideo extension is actually available when
27  * connecting to a remote display. This element can receive a Window ID from the
28  * application through the XOverlay interface and will then render video frames
29  * in this drawable. If no Window ID was provided by the application, the
30  * element will create its own internal window and render into it.
31  *
32  * <refsect2>
33  * <title>Scaling</title>
34  * <para>
35  * The XVideo extension, when it's available, handles hardware accelerated
36  * scaling of video frames. This means that the element will just accept
37  * incoming video frames no matter their geometry and will then put them to the
38  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39  * property it is possible to enforce scaling with a constant aspect ratio,
40  * which means drawing black borders around the video frame.
41  * </para>
42  * </refsect2>
43  * <refsect2>
44  * <title>Events</title>
45  * <para>
46  * XvImageSink creates a thread to handle events coming from the drawable. There
47  * are several kind of events that can be grouped in 2 big categories: input
48  * events and window state related events. Input events will be translated to
49  * navigation events and pushed upstream for other elements to react on them.
50  * This includes events such as pointer moves, key press/release, clicks etc...
51  * Other events are used to handle the drawable appearance even when the data
52  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53  * paused, it will receive expose events from the drawable and draw the latest
54  * frame with correct borders/aspect-ratio.
55  * </para>
56  * </refsect2>
57  * <refsect2>
58  * <title>Pixel aspect ratio</title>
59  * <para>
60  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61  * the display specified in the #GstXvImageSink:display property or the
62  * default display if nothing specified. Once this connection is open it will
63  * inspect the display configuration including the physical display geometry and
64  * then calculate the pixel aspect ratio. When receiving video frames with a
65  * different pixel aspect ratio, XvImageSink will use hardware scaling to
66  * display the video frames correctly on display's pixel aspect ratio.
67  * Sometimes the calculated pixel aspect ratio can be wrong, it is
68  * then possible to enforce a specific pixel aspect ratio using the
69  * #GstXvImageSink:pixel-aspect-ratio property.
70  * </para>
71  * </refsect2>
72  * <refsect2>
73  * <title>Examples</title>
74  * |[
75  * gst-launch -v videotestsrc ! xvimagesink
76  * ]| A pipeline to test hardware scaling.
77  * When the test video signal appears you can resize the window and see that
78  * video frames are scaled through hardware (no extra CPU cost).
79  * |[
80  * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81  * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82  * You can observe the borders drawn around the scaled image respecting aspect
83  * ratio.
84  * |[
85  * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86  * ]| A pipeline to test navigation events.
87  * While moving the mouse pointer over the test signal you will see a black box
88  * following the mouse pointer. If you press the mouse button somewhere on the
89  * video and release it somewhere else a green box will appear where you pressed
90  * the button and a red one where you released it. (The navigationtest element
91  * is part of gst-plugins-good.) You can observe here that even if the images
92  * are scaled through hardware the pointer coordinates are converted back to the
93  * original video frame geometry so that the box can be drawn to the correct
94  * position. This also handles borders correctly, limiting coordinates to the
95  * image area
96  * |[
97  * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99  * videotestsrc, in most cases the pixel aspect ratio of the display will be
100  * 1/1. This means that XvImageSink will have to do the scaling to convert
101  * incoming frames to a size that will match the display pixel aspect ratio
102  * (from 320x240 to 320x180 in this case). Note that you might have to escape
103  * some characters for your shell like '\(fraction\)'.
104  * |[
105  * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106  * ]| Demonstrates how to use the colorbalance interface.
107  * </refsect2>
108  */
109
110 /* for developers: there are two useful tools : xvinfo and xvattr */
111
112 #ifdef HAVE_CONFIG_H
113 #include "config.h"
114 #endif
115
116 /* Our interfaces */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
123
124 /* Object header */
125 #include "xvimagesink.h"
126
127 #ifdef GST_EXT_XV_ENHANCEMENT
128 /* Samsung extension headers */
129 /* For xv extension header for buffer transfer (output) */
130 #include "xv_types.h"
131
132 /* max channel count *********************************************************/
133 #define SCMN_IMGB_MAX_PLANE         (4)
134
135 /* image buffer definition ***************************************************
136
137     +------------------------------------------+ ---
138     |                                          |  ^
139     |     a[], p[]                             |  |
140     |     +---------------------------+ ---    |  |
141     |     |                           |  ^     |  |
142     |     |<---------- w[] ---------->|  |     |  |
143     |     |                           |  |     |  |
144     |     |                           |        |
145     |     |                           |  h[]   |  e[]
146     |     |                           |        |
147     |     |                           |  |     |  |
148     |     |                           |  |     |  |
149     |     |                           |  v     |  |
150     |     +---------------------------+ ---    |  |
151     |                                          |  v
152     +------------------------------------------+ ---
153
154     |<----------------- s[] ------------------>|
155 */
156
157 typedef struct
158 {
159         /* width of each image plane */
160         int      w[SCMN_IMGB_MAX_PLANE];
161         /* height of each image plane */
162         int      h[SCMN_IMGB_MAX_PLANE];
163         /* stride of each image plane */
164         int      s[SCMN_IMGB_MAX_PLANE];
165         /* elevation of each image plane */
166         int      e[SCMN_IMGB_MAX_PLANE];
167         /* user space address of each image plane */
168         void   * a[SCMN_IMGB_MAX_PLANE];
169         /* physical address of each image plane, if needs */
170         void   * p[SCMN_IMGB_MAX_PLANE];
171         /* color space type of image */
172         int      cs;
173         /* left postion, if needs */
174         int      x;
175         /* top position, if needs */
176         int      y;
177         /* to align memory */
178         int      __dummy2;
179         /* arbitrary data */
180         int      data[16];
181 } SCMN_IMGB;
182
183 #endif
184
185 /* Debugging category */
186 #include <gst/gstinfo.h>
187 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
188 #define GST_CAT_DEFAULT gst_debug_xvimagesink
189 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
190
191 #ifdef GST_EXT_XV_ENHANCEMENT
192 #define GST_TYPE_XVIMAGESINK_DISPLAY_MODE (gst_xvimagesink_display_mode_get_type())
193
194 static GType
195 gst_xvimagesink_display_mode_get_type(void)
196 {
197         static GType xvimagesink_display_mode_type = 0;
198         static const GEnumValue display_mode_type[] = {
199                 { 1, "Display to both TV and LCD", "BOTH"},
200                 { 2, "Display to TV", "TVONLY"},
201                 { 3, "Display to LCD", "LCDONLY"},
202                 { 4, "Display caption on TV", "TVCAPTION"},
203                 { 5, NULL, NULL},
204         };
205
206         if (!xvimagesink_display_mode_type) {
207                 xvimagesink_display_mode_type = g_enum_register_static("GstXVImageSinkDisplayModeType", display_mode_type);
208         }
209
210         return xvimagesink_display_mode_type;
211 }
212
213 enum {
214     DEGREE_0,
215     DEGREE_90,
216     DEGREE_180,
217     DEGREE_270,
218     DEGREE_NUM,
219 };
220
221 #define GST_TYPE_XVIMAGESINK_ROTATE_ANGLE (gst_xvimagesink_rotate_angle_get_type())
222
223 static GType
224 gst_xvimagesink_rotate_angle_get_type(void)
225 {
226         static GType xvimagesink_rotate_angle_type = 0;
227         static const GEnumValue rotate_angle_type[] = {
228                 { 0, "No rotate", "DEGREE_0"},
229                 { 1, "Rotate 90 degree", "DEGREE_90"},
230                 { 2, "Rotate 180 degree", "DEGREE_180"},
231                 { 3, "Rotate 270 degree", "DEGREE_270"},
232                 { 4, NULL, NULL},
233         };
234
235         if (!xvimagesink_rotate_angle_type) {
236                 xvimagesink_rotate_angle_type = g_enum_register_static("GstXVImageSinkRotateAngleType", rotate_angle_type);
237         }
238
239         return xvimagesink_rotate_angle_type;
240 }
241
242 enum {
243     DISP_GEO_METHOD_LETTER_BOX = 0,
244     DISP_GEO_METHOD_ORIGIN_SIZE,
245     DISP_GEO_METHOD_FULL_SCREEN,
246     DISP_GEO_METHOD_CROPPED_FULL_SCREEN,
247     DISP_GEO_METHOD_CUSTOM_ROI,
248     DISP_GEO_METHOD_NUM,
249 };
250
251 #define DEF_DISPLAY_GEOMETRY_METHOD DISP_GEO_METHOD_LETTER_BOX
252
253 #define GST_TYPE_XVIMAGESINK_DISPLAY_GEOMETRY_METHOD (gst_xvimagesink_display_geometry_method_get_type())
254
255 static GType
256 gst_xvimagesink_display_geometry_method_get_type(void)
257 {
258         static GType xvimagesink_display_geometry_method_type = 0;
259         static const GEnumValue display_geometry_method_type[] = {
260                 { 0, "Letter box", "LETTER_BOX"},
261                 { 1, "Origin size", "ORIGIN_SIZE"},
262                 { 2, "Full-screen", "FULL_SCREEN"},
263                 { 3, "Cropped full-screen", "CROPPED_FULL_SCREEN"},
264                 { 4, "Explicitely described destination ROI", "CUSTOM_ROI"},
265                 { 5, NULL, NULL},
266         };
267
268         if (!xvimagesink_display_geometry_method_type) {
269                 xvimagesink_display_geometry_method_type = g_enum_register_static("GstXVImageSinkDisplayGeometryMethodType", display_geometry_method_type);
270         }
271
272         return xvimagesink_display_geometry_method_type;
273 }
274 #endif /* GST_EXT_XV_ENHANCEMENT */
275
276 typedef struct
277 {
278   unsigned long flags;
279   unsigned long functions;
280   unsigned long decorations;
281   long input_mode;
282   unsigned long status;
283 }
284 MotifWmHints, MwmHints;
285
286 #define MWM_HINTS_DECORATIONS   (1L << 1)
287
288 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
289
290 static GstBufferClass *xvimage_buffer_parent_class = NULL;
291 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
292
293 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
294     xvimagesink);
295 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
296     GstCaps * caps);
297 static void gst_xvimagesink_expose (GstXOverlay * overlay);
298
299 #ifdef GST_EXT_XV_ENHANCEMENT
300 static XImage *make_transparent_image(Display *d, Window win, int w, int h);
301 #endif
302
303 /* Default template - initiated with class struct to allow gst-register to work
304    without X running */
305 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
306     GST_STATIC_PAD_TEMPLATE ("sink",
307     GST_PAD_SINK,
308     GST_PAD_ALWAYS,
309     GST_STATIC_CAPS ("video/x-raw-rgb, "
310         "framerate = (fraction) [ 0, MAX ], "
311         "width = (int) [ 1, MAX ], "
312         "height = (int) [ 1, MAX ]; "
313         "video/x-raw-yuv, "
314         "framerate = (fraction) [ 0, MAX ], "
315         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
316     );
317
318 enum
319 {
320   PROP_0,
321   PROP_CONTRAST,
322   PROP_BRIGHTNESS,
323   PROP_HUE,
324   PROP_SATURATION,
325   PROP_DISPLAY,
326   PROP_SYNCHRONOUS,
327   PROP_PIXEL_ASPECT_RATIO,
328   PROP_FORCE_ASPECT_RATIO,
329   PROP_HANDLE_EVENTS,
330   PROP_DEVICE,
331   PROP_DEVICE_NAME,
332   PROP_HANDLE_EXPOSE,
333   PROP_DOUBLE_BUFFER,
334   PROP_AUTOPAINT_COLORKEY,
335   PROP_COLORKEY,
336   PROP_DRAW_BORDERS,
337   PROP_WINDOW_WIDTH,
338   PROP_WINDOW_HEIGHT,
339 #ifdef GST_EXT_XV_ENHANCEMENT
340   PROP_DISPLAY_MODE,
341   PROP_ROTATE_ANGLE,
342   PROP_DISPLAY_GEOMETRY_METHOD,
343   PROP_VISIBLE,
344   PROP_ZOOM,
345   PROP_DST_ROI_X,
346   PROP_DST_ROI_Y,
347   PROP_DST_ROI_W,
348   PROP_DST_ROI_H,
349   PROP_STOP_VIDEO,
350 #endif
351 };
352
353 static GstVideoSinkClass *parent_class = NULL;
354
355 /* ============================================================= */
356 /*                                                               */
357 /*                       Private Methods                         */
358 /*                                                               */
359 /* ============================================================= */
360
361 /* xvimage buffers */
362
363 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
364
365 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
366 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
367 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
368
369 /* This function destroys a GstXvImage handling XShm availability */
370 static void
371 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
372 {
373   GstXvImageSink *xvimagesink;
374
375   GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
376
377   xvimagesink = xvimage->xvimagesink;
378   if (G_UNLIKELY (xvimagesink == NULL))
379     goto no_sink;
380
381   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
382
383   GST_OBJECT_LOCK (xvimagesink);
384
385   /* If the destroyed image is the current one we destroy our reference too */
386   if (xvimagesink->cur_image == xvimage)
387     xvimagesink->cur_image = NULL;
388
389   /* We might have some buffers destroyed after changing state to NULL */
390   if (xvimagesink->xcontext == NULL) {
391     GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
392 #ifdef HAVE_XSHM
393     /* Need to free the shared memory segment even if the x context
394      * was already cleaned up */
395     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
396       shmdt (xvimage->SHMInfo.shmaddr);
397     }
398 #endif
399     goto beach;
400   }
401
402   g_mutex_lock (xvimagesink->x_lock);
403
404 #ifdef HAVE_XSHM
405   if (xvimagesink->xcontext->use_xshm) {
406     if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
407       GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
408           xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
409       XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
410       XSync (xvimagesink->xcontext->disp, FALSE);
411
412       shmdt (xvimage->SHMInfo.shmaddr);
413     }
414     if (xvimage->xvimage)
415       XFree (xvimage->xvimage);
416   } else
417 #endif /* HAVE_XSHM */
418   {
419     if (xvimage->xvimage) {
420       if (xvimage->xvimage->data) {
421         g_free (xvimage->xvimage->data);
422       }
423       XFree (xvimage->xvimage);
424     }
425   }
426
427   XSync (xvimagesink->xcontext->disp, FALSE);
428
429   g_mutex_unlock (xvimagesink->x_lock);
430
431 beach:
432   GST_OBJECT_UNLOCK (xvimagesink);
433   xvimage->xvimagesink = NULL;
434   gst_object_unref (xvimagesink);
435
436   GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
437       (xvimage));
438
439   return;
440
441 no_sink:
442   {
443     GST_WARNING ("no sink found");
444     return;
445   }
446 }
447
448 static void
449 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
450 {
451   GstXvImageSink *xvimagesink;
452   gboolean running;
453
454   xvimagesink = xvimage->xvimagesink;
455   if (G_UNLIKELY (xvimagesink == NULL))
456     goto no_sink;
457
458   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
459
460   GST_OBJECT_LOCK (xvimagesink);
461   running = xvimagesink->running;
462   GST_OBJECT_UNLOCK (xvimagesink);
463
464   /* If our geometry changed we can't reuse that image. */
465   if (running == FALSE) {
466     GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
467     gst_xvimage_buffer_destroy (xvimage);
468   } else if ((xvimage->width != xvimagesink->video_width) ||
469       (xvimage->height != xvimagesink->video_height)) {
470     GST_LOG_OBJECT (xvimage,
471         "destroy image as its size changed %dx%d vs current %dx%d",
472         xvimage->width, xvimage->height,
473         xvimagesink->video_width, xvimagesink->video_height);
474     gst_xvimage_buffer_destroy (xvimage);
475   } else {
476     /* In that case we can reuse the image and add it to our image pool. */
477     GST_LOG_OBJECT (xvimage, "recycling image in pool");
478     /* need to increment the refcount again to recycle */
479     gst_buffer_ref (GST_BUFFER_CAST (xvimage));
480     g_mutex_lock (xvimagesink->pool_lock);
481     xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
482         xvimage);
483     g_mutex_unlock (xvimagesink->pool_lock);
484   }
485   return;
486
487 no_sink:
488   {
489     GST_WARNING ("no sink found");
490     return;
491   }
492 }
493
494 static void
495 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
496 {
497   /* make sure it is not recycled */
498   xvimage->width = -1;
499   xvimage->height = -1;
500   gst_buffer_unref (GST_BUFFER (xvimage));
501 }
502
503 static void
504 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
505 {
506 #ifdef HAVE_XSHM
507   xvimage->SHMInfo.shmaddr = ((void *) -1);
508   xvimage->SHMInfo.shmid = -1;
509 #endif
510 }
511
512 static void
513 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
514 {
515   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
516
517   xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
518
519   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
520       gst_xvimage_buffer_finalize;
521 }
522
523 static GType
524 gst_xvimage_buffer_get_type (void)
525 {
526   static GType _gst_xvimage_buffer_type;
527
528   if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
529     static const GTypeInfo xvimage_buffer_info = {
530       sizeof (GstBufferClass),
531       NULL,
532       NULL,
533       gst_xvimage_buffer_class_init,
534       NULL,
535       NULL,
536       sizeof (GstXvImageBuffer),
537       0,
538       (GInstanceInitFunc) gst_xvimage_buffer_init,
539       NULL
540     };
541     _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
542         "GstXvImageBuffer", &xvimage_buffer_info, 0);
543   }
544   return _gst_xvimage_buffer_type;
545 }
546
547 /* X11 stuff */
548
549 static gboolean error_caught = FALSE;
550
551 static int
552 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
553 {
554   char error_msg[1024];
555
556   XGetErrorText (display, xevent->error_code, error_msg, 1024);
557   GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
558   error_caught = TRUE;
559   return 0;
560 }
561
562 #ifdef HAVE_XSHM
563 /* This function checks that it is actually really possible to create an image
564    using XShm */
565 static gboolean
566 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
567 {
568   XvImage *xvimage;
569   XShmSegmentInfo SHMInfo;
570   gint size;
571   int (*handler) (Display *, XErrorEvent *);
572   gboolean result = FALSE;
573   gboolean did_attach = FALSE;
574
575   g_return_val_if_fail (xcontext != NULL, FALSE);
576
577   /* Sync to ensure any older errors are already processed */
578   XSync (xcontext->disp, FALSE);
579
580   /* Set defaults so we don't free these later unnecessarily */
581   SHMInfo.shmaddr = ((void *) -1);
582   SHMInfo.shmid = -1;
583
584   /* Setting an error handler to catch failure */
585   error_caught = FALSE;
586   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
587
588   /* Trying to create a 1x1 picture */
589   GST_DEBUG ("XvShmCreateImage of 1x1");
590   xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
591       xcontext->im_format, NULL, 1, 1, &SHMInfo);
592
593   /* Might cause an error, sync to ensure it is noticed */
594   XSync (xcontext->disp, FALSE);
595   if (!xvimage || error_caught) {
596     GST_WARNING ("could not XvShmCreateImage a 1x1 image");
597     goto beach;
598   }
599   size = xvimage->data_size;
600
601   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
602   if (SHMInfo.shmid == -1) {
603     GST_WARNING ("could not get shared memory of %d bytes", size);
604     goto beach;
605   }
606
607   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
608   if (SHMInfo.shmaddr == ((void *) -1)) {
609     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
610     /* Clean up the shared memory segment */
611     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
612     goto beach;
613   }
614
615   xvimage->data = SHMInfo.shmaddr;
616   SHMInfo.readOnly = FALSE;
617
618   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
619     GST_WARNING ("Failed to XShmAttach");
620     /* Clean up the shared memory segment */
621     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
622     goto beach;
623   }
624
625   /* Sync to ensure we see any errors we caused */
626   XSync (xcontext->disp, FALSE);
627
628   /* Delete the shared memory segment as soon as everyone is attached.
629    * This way, it will be deleted as soon as we detach later, and not
630    * leaked if we crash. */
631   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
632
633   if (!error_caught) {
634     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
635         SHMInfo.shmseg);
636
637     did_attach = TRUE;
638     /* store whether we succeeded in result */
639     result = TRUE;
640   } else {
641     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
642         "Not using shared memory.");
643   }
644
645 beach:
646   /* Sync to ensure we swallow any errors we caused and reset error_caught */
647   XSync (xcontext->disp, FALSE);
648
649   error_caught = FALSE;
650   XSetErrorHandler (handler);
651
652   if (did_attach) {
653     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
654         SHMInfo.shmid, SHMInfo.shmseg);
655     XShmDetach (xcontext->disp, &SHMInfo);
656     XSync (xcontext->disp, FALSE);
657   }
658   if (SHMInfo.shmaddr != ((void *) -1))
659     shmdt (SHMInfo.shmaddr);
660   if (xvimage)
661     XFree (xvimage);
662   return result;
663 }
664 #endif /* HAVE_XSHM */
665
666 /* This function handles GstXvImage creation depending on XShm availability */
667 static GstXvImageBuffer *
668 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
669 {
670   GstXvImageBuffer *xvimage = NULL;
671   GstStructure *structure = NULL;
672   gboolean succeeded = FALSE;
673   int (*handler) (Display *, XErrorEvent *);
674
675   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
676
677   if (caps == NULL)
678     return NULL;
679
680   xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
681   GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
682
683   structure = gst_caps_get_structure (caps, 0);
684
685   if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
686       !gst_structure_get_int (structure, "height", &xvimage->height)) {
687     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
688   }
689
690   GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
691       xvimage->height);
692 #ifdef GST_EXT_XV_ENHANCEMENT
693   GST_LOG_OBJECT(xvimagesink, "aligned size %dx%d",
694                                xvimagesink->aligned_width, xvimagesink->aligned_height);
695   if (xvimagesink->aligned_width == 0 || xvimagesink->aligned_height == 0) {
696     GST_INFO_OBJECT(xvimagesink, "aligned size is zero. set size of caps.");
697     xvimagesink->aligned_width = xvimage->width;
698     xvimagesink->aligned_height = xvimage->height;
699   }
700 #endif
701
702   xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
703   if (xvimage->im_format == -1) {
704     GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
705         GST_PTR_FORMAT, caps);
706     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
707         ("Failed to create output image buffer of %dx%d pixels",
708             xvimage->width, xvimage->height), ("Invalid input caps"));
709     goto beach_unlocked;
710   }
711   xvimage->xvimagesink = gst_object_ref (xvimagesink);
712
713   g_mutex_lock (xvimagesink->x_lock);
714
715   /* Setting an error handler to catch failure */
716   error_caught = FALSE;
717   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
718
719 #ifdef HAVE_XSHM
720   if (xvimagesink->xcontext->use_xshm) {
721     int expected_size;
722
723     xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
724         xvimagesink->xcontext->xv_port_id,
725         xvimage->im_format, NULL,
726 #ifdef GST_EXT_XV_ENHANCEMENT
727         xvimagesink->aligned_width, xvimagesink->aligned_height, &xvimage->SHMInfo);
728 #else
729         xvimage->width, xvimage->height, &xvimage->SHMInfo);
730 #endif
731     if (!xvimage->xvimage || error_caught) {
732       g_mutex_unlock (xvimagesink->x_lock);
733       /* Reset error handler */
734       error_caught = FALSE;
735       XSetErrorHandler (handler);
736       /* Push an error */
737       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
738           ("Failed to create output image buffer of %dx%d pixels",
739               xvimage->width, xvimage->height),
740           ("could not XvShmCreateImage a %dx%d image",
741               xvimage->width, xvimage->height));
742       goto beach_unlocked;
743     }
744
745     /* we have to use the returned data_size for our shm size */
746     xvimage->size = xvimage->xvimage->data_size;
747     GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
748         xvimage->size);
749
750     /* calculate the expected size.  This is only for sanity checking the
751      * number we get from X. */
752     switch (xvimage->im_format) {
753       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
754       case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
755       {
756         gint pitches[3];
757         gint offsets[3];
758         guint plane;
759
760         offsets[0] = 0;
761         pitches[0] = GST_ROUND_UP_4 (xvimage->width);
762         offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
763         pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
764         offsets[2] =
765             offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
766         pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
767
768         expected_size =
769             offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
770
771         for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
772           GST_DEBUG_OBJECT (xvimagesink,
773               "Plane %u has a expected pitch of %d bytes, " "offset of %d",
774               plane, pitches[plane], offsets[plane]);
775         }
776         break;
777       }
778       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
779       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
780         expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
781         break;
782
783 #ifdef GST_EXT_XV_ENHANCEMENT
784       case GST_MAKE_FOURCC ('S', 'T', '1', '2'):
785       case GST_MAKE_FOURCC ('S', 'N', '1', '2'):
786       case GST_MAKE_FOURCC ('S', 'U', 'Y', 'V'):
787       case GST_MAKE_FOURCC ('S', 'U', 'Y', '2'):
788       case GST_MAKE_FOURCC ('S', '4', '2', '0'):
789       case GST_MAKE_FOURCC ('S', 'Y', 'V', 'Y'):
790         expected_size = sizeof(SCMN_IMGB);
791         break;
792 #endif
793       default:
794         expected_size = 0;
795         break;
796     }
797     if (expected_size != 0 && xvimage->size != expected_size) {
798       GST_WARNING_OBJECT (xvimagesink,
799           "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
800           xvimage->size, expected_size);
801     }
802
803     /* Be verbose about our XvImage stride */
804     {
805       guint plane;
806
807       for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
808         GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
809             "offset of %d", plane, xvimage->xvimage->pitches[plane],
810             xvimage->xvimage->offsets[plane]);
811       }
812     }
813
814     xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
815         IPC_CREAT | 0777);
816     if (xvimage->SHMInfo.shmid == -1) {
817       g_mutex_unlock (xvimagesink->x_lock);
818       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
819           ("Failed to create output image buffer of %dx%d pixels",
820               xvimage->width, xvimage->height),
821           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
822               xvimage->size));
823       goto beach_unlocked;
824     }
825
826     xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
827     if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
828       g_mutex_unlock (xvimagesink->x_lock);
829       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
830           ("Failed to create output image buffer of %dx%d pixels",
831               xvimage->width, xvimage->height),
832           ("Failed to shmat: %s", g_strerror (errno)));
833       /* Clean up the shared memory segment */
834       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
835       goto beach_unlocked;
836     }
837
838     xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
839     xvimage->SHMInfo.readOnly = FALSE;
840
841     if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
842       /* Clean up the shared memory segment */
843       shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
844
845       g_mutex_unlock (xvimagesink->x_lock);
846       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
847           ("Failed to create output image buffer of %dx%d pixels",
848               xvimage->width, xvimage->height), ("Failed to XShmAttach"));
849       goto beach_unlocked;
850     }
851
852     XSync (xvimagesink->xcontext->disp, FALSE);
853
854     /* Delete the shared memory segment as soon as we everyone is attached.
855      * This way, it will be deleted as soon as we detach later, and not
856      * leaked if we crash. */
857     shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
858
859     GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
860         xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
861   } else
862 #endif /* HAVE_XSHM */
863   {
864     xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
865         xvimagesink->xcontext->xv_port_id,
866 #ifdef GST_EXT_XV_ENHANCEMENT
867         xvimage->im_format, NULL, xvimagesink->aligned_width, xvimagesink->aligned_height);
868 #else
869         xvimage->im_format, NULL, xvimage->width, xvimage->height);
870 #endif
871     if (!xvimage->xvimage || error_caught) {
872       g_mutex_unlock (xvimagesink->x_lock);
873       /* Reset error handler */
874       error_caught = FALSE;
875       XSetErrorHandler (handler);
876       /* Push an error */
877       GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
878           ("Failed to create outputimage buffer of %dx%d pixels",
879               xvimage->width, xvimage->height),
880           ("could not XvCreateImage a %dx%d image",
881               xvimage->width, xvimage->height));
882       goto beach_unlocked;
883     }
884
885     /* we have to use the returned data_size for our image size */
886     xvimage->size = xvimage->xvimage->data_size;
887     xvimage->xvimage->data = g_malloc (xvimage->size);
888
889     XSync (xvimagesink->xcontext->disp, FALSE);
890   }
891
892   /* Reset error handler */
893   error_caught = FALSE;
894   XSetErrorHandler (handler);
895
896   succeeded = TRUE;
897
898   GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
899   GST_BUFFER_SIZE (xvimage) = xvimage->size;
900
901   g_mutex_unlock (xvimagesink->x_lock);
902
903 beach_unlocked:
904   if (!succeeded) {
905     gst_xvimage_buffer_free (xvimage);
906     xvimage = NULL;
907   }
908
909   return xvimage;
910 }
911
912 /* We are called with the x_lock taken */
913 static void
914 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
915     GstXWindow * xwindow, GstVideoRectangle rect)
916 {
917   gint t1, t2;
918
919   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
920   g_return_if_fail (xwindow != NULL);
921
922   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
923       xvimagesink->xcontext->black);
924
925   /* Left border */
926   if (rect.x > xvimagesink->render_rect.x) {
927     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
928         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
929         rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
930   }
931
932   /* Right border */
933   t1 = rect.x + rect.w;
934   t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
935   if (t1 < t2) {
936     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
937         t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
938   }
939
940   /* Top border */
941   if (rect.y > xvimagesink->render_rect.y) {
942     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
943         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
944         xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
945   }
946
947   /* Bottom border */
948   t1 = rect.y + rect.h;
949   t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
950   if (t1 < t2) {
951     XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
952         xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
953   }
954 }
955
956 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
957  * if no window was available  */
958 static gboolean
959 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
960     GstXvImageBuffer * xvimage)
961 {
962   GstVideoRectangle result;
963   gboolean draw_border = FALSE;
964
965 #ifdef GST_EXT_XV_ENHANCEMENT
966   static Atom atom = None;
967
968   GstVideoRectangle src_origin = { 0, 0, 0, 0};
969   GstVideoRectangle src_input  = { 0, 0, 0, 0};
970   GstVideoRectangle src = { 0, 0, 0, 0};
971   GstVideoRectangle dst = { 0, 0, 0, 0};
972
973   int rotate        = 0;
974   int ret           = 0;
975   //int window_rotate = 0;
976 #endif
977
978   /* We take the flow_lock. If expose is in there we don't want to run
979      concurrently from the data flow thread */
980   g_mutex_lock (xvimagesink->flow_lock);
981
982   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
983 #ifdef GST_EXT_XV_ENHANCEMENT
984     GST_INFO_OBJECT( xvimagesink, "xwindow is NULL. Skip xvimage_put." );
985 #endif
986     g_mutex_unlock (xvimagesink->flow_lock);
987     return FALSE;
988   }
989
990 #ifdef GST_EXT_XV_ENHANCEMENT
991   if( xvimagesink->visible == FALSE )
992   {
993     GST_INFO_OBJECT( xvimagesink, "visible is FALSE. Skip xvimage_put." );
994     g_mutex_unlock( xvimagesink->flow_lock );
995     return TRUE;
996   }
997 #endif
998
999   /* Draw borders when displaying the first frame. After this
1000      draw borders only on expose event or after a size change. */
1001   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
1002     draw_border = TRUE;
1003   }
1004
1005   /* Store a reference to the last image we put, lose the previous one */
1006   if (xvimage && xvimagesink->cur_image != xvimage) {
1007     if (xvimagesink->cur_image) {
1008       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
1009       gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
1010     }
1011     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
1012     xvimagesink->cur_image =
1013         GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
1014   }
1015
1016   /* Expose sends a NULL image, we take the latest frame */
1017   if (!xvimage) {
1018     if (xvimagesink->cur_image) {
1019       draw_border = TRUE;
1020       xvimage = xvimagesink->cur_image;
1021     } else {
1022 #ifdef GST_EXT_XV_ENHANCEMENT
1023       GST_INFO_OBJECT( xvimagesink, "cur_image is NULL. Skip xvimage_put." );
1024 #endif
1025       g_mutex_unlock (xvimagesink->flow_lock);
1026       return TRUE;
1027     }
1028   }
1029
1030 #ifdef GST_EXT_XV_ENHANCEMENT
1031   gst_xvimagesink_xwindow_update_geometry( xvimagesink );
1032
1033   src.x = src.y = 0;
1034   src_origin.x = src_origin.y = src_input.x = src_input.y = 0;
1035
1036   src_input.w = src_origin.w = xvimagesink->video_width;
1037   src_input.h = src_origin.h = xvimagesink->video_height;
1038
1039   if (xvimagesink->rotate_angle == DEGREE_0 ||
1040       xvimagesink->rotate_angle == DEGREE_180) {
1041     src.w = src_origin.w;
1042     src.h = src_origin.h;
1043   } else {
1044     src.w = src_origin.h;
1045     src.h = src_origin.w;
1046   }
1047
1048   dst.w = xvimagesink->render_rect.w;
1049   dst.h = xvimagesink->render_rect.h;
1050
1051   switch (xvimagesink->display_geometry_method)
1052   {
1053     case DISP_GEO_METHOD_LETTER_BOX:
1054       gst_video_sink_center_rect (src, dst, &result, TRUE);
1055       result.x += xvimagesink->render_rect.x;
1056       result.y += xvimagesink->render_rect.y;
1057       break;
1058
1059     case DISP_GEO_METHOD_ORIGIN_SIZE:
1060       gst_video_sink_center_rect (src, dst, &result, FALSE);
1061       gst_video_sink_center_rect (dst, src, &src_input, FALSE);
1062
1063       if (xvimagesink->rotate_angle == DEGREE_90 ||
1064           xvimagesink->rotate_angle == DEGREE_270) {
1065         src_input.x = src_input.x ^ src_input.y;
1066         src_input.y = src_input.x ^ src_input.y;
1067         src_input.x = src_input.x ^ src_input.y;
1068
1069         src_input.w = src_input.w ^ src_input.h;
1070         src_input.h = src_input.w ^ src_input.h;
1071         src_input.w = src_input.w ^ src_input.h;
1072       }
1073       break;
1074
1075    case DISP_GEO_METHOD_FULL_SCREEN:
1076       result.x = result.y = 0;
1077       result.w = xvimagesink->xwindow->width;
1078       result.h = xvimagesink->xwindow->height;
1079       break;
1080
1081    case DISP_GEO_METHOD_CROPPED_FULL_SCREEN:
1082       gst_video_sink_center_rect(dst, src, &src_input, TRUE);
1083
1084       result.x = result.y = 0;
1085       result.w = dst.w;
1086       result.h = dst.h;
1087
1088       if (xvimagesink->rotate_angle == DEGREE_90 ||
1089           xvimagesink->rotate_angle == DEGREE_270) {
1090         src_input.x = src_input.x ^ src_input.y;
1091         src_input.y = src_input.x ^ src_input.y;
1092         src_input.x = src_input.x ^ src_input.y;
1093
1094         src_input.w = src_input.w ^ src_input.h;
1095         src_input.h = src_input.w ^ src_input.h;
1096         src_input.w = src_input.w ^ src_input.h;
1097       }
1098       break;
1099
1100     case DISP_GEO_METHOD_CUSTOM_ROI:
1101       result.x = xvimagesink->dst_roi.x;
1102       result.y = xvimagesink->dst_roi.y;
1103       result.w = xvimagesink->dst_roi.w;
1104       result.h = xvimagesink->dst_roi.h;
1105
1106       if (xvimagesink->rotate_angle == DEGREE_90 ||
1107           xvimagesink->rotate_angle == DEGREE_270) {
1108         result.w = xvimagesink->dst_roi.h;
1109         result.h = xvimagesink->dst_roi.w;
1110       }
1111       break;
1112
1113     default:
1114       break;
1115   }
1116
1117   if (xvimagesink->zoom > 1 && xvimagesink->zoom < 10) {
1118     src_input.x += (src_input.w-(src_input.w/xvimagesink->zoom))>>1;
1119     src_input.y += (src_input.h-(src_input.h/xvimagesink->zoom))>>1;
1120     src_input.w /= xvimagesink->zoom;
1121     src_input.h /= xvimagesink->zoom;
1122   }
1123 #else
1124   if (xvimagesink->keep_aspect) {
1125     GstVideoRectangle src, dst;
1126
1127     /* We use the calculated geometry from _setcaps as a source to respect
1128        source and screen pixel aspect ratios. */
1129     src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1130     src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1131     dst.w = xvimagesink->render_rect.w;
1132     dst.h = xvimagesink->render_rect.h;
1133
1134     gst_video_sink_center_rect (src, dst, &result, TRUE);
1135     result.x += xvimagesink->render_rect.x;
1136     result.y += xvimagesink->render_rect.y;
1137   } else {
1138     memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
1139   }
1140 #endif
1141
1142   g_mutex_lock (xvimagesink->x_lock);
1143
1144   if (draw_border && xvimagesink->draw_borders) {
1145     gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
1146         result);
1147     xvimagesink->redraw_border = FALSE;
1148   }
1149
1150   /* We scale to the window's geometry */
1151 #ifdef HAVE_XSHM
1152   if (xvimagesink->xcontext->use_xshm) {
1153     GST_LOG_OBJECT (xvimagesink,
1154         "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
1155         GST_PTR_FORMAT,
1156         xvimage->width, xvimage->height,
1157         xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
1158
1159 #ifdef GST_EXT_XV_ENHANCEMENT
1160     switch( xvimagesink->rotate_angle )
1161     {
1162       /* There's slightly weired code (CCW? CW?) */
1163       case DEGREE_0:
1164         break;
1165       case DEGREE_90:
1166         rotate = 270;
1167         break;
1168       case DEGREE_180:
1169         rotate = 180;
1170         break;
1171       case DEGREE_270:
1172         rotate = 90;
1173         break;
1174       default:
1175         GST_WARNING_OBJECT( xvimagesink, "Unsupported rotation [%d]... set DEGREE 0.",
1176           xvimagesink->rotate_angle );
1177         break;
1178     }
1179
1180     /* Trim as proper size */
1181     if (src_input.w % 2 == 1) {
1182         src_input.w += 1;
1183     }
1184     if (src_input.h % 2 == 1) {
1185         src_input.h += 1;
1186     }
1187
1188     GST_LOG_OBJECT( xvimagesink, "screen[%dx%d],window[%d,%d,%dx%d],method[%d],rotate[%d],zoom[%d],src[%dx%d],dst[%d,%d,%dx%d],input[%d,%d,%dx%d],result[%d,%d,%dx%d]",
1189       xvimagesink->scr_w, xvimagesink->scr_h,
1190       xvimagesink->xwindow->x, xvimagesink->xwindow->y, xvimagesink->xwindow->width, xvimagesink->xwindow->height,
1191       xvimagesink->display_geometry_method, rotate, xvimagesink->zoom,
1192       src_origin.w, src_origin.h,
1193       dst.x, dst.y, dst.w, dst.h,
1194       src_input.x, src_input.y, src_input.w, src_input.h,
1195       result.x, result.y, result.w, result.h );
1196
1197     if (atom == None) {
1198       atom = XInternAtom( xvimagesink->xcontext->disp, 
1199         "_USER_WM_PORT_ATTRIBUTE_ROTATION", False );
1200     }
1201
1202     ret = XvSetPortAttribute(xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom, rotate );
1203     if (ret != Success) {
1204       GST_ERROR_OBJECT( xvimagesink, "XvSetPortAttribute failed[%d]. disp[%x],xv_port_id[%d],atom[%x],rotate[%d]",
1205         ret, xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom, rotate );
1206       return FALSE;
1207     }
1208
1209     /* src input indicates the status when degree is 0 */
1210     /* dst input indicates the area that src will be shown regardless of rotate */
1211
1212     if (xvimagesink->visible && !xvimagesink->is_hided) {
1213       if (xvimagesink->xim_transparenter) {
1214         GST_LOG_OBJECT( xvimagesink, "Transparent related issue." );
1215         XPutImage(xvimagesink->xcontext->disp,
1216           xvimagesink->xwindow->win,
1217           xvimagesink->xwindow->gc,
1218           xvimagesink->xim_transparenter,
1219           0, 0,
1220           result.x, result.y, result.w, result.h);
1221       }
1222
1223       ret = XvShmPutImage (xvimagesink->xcontext->disp,
1224         xvimagesink->xcontext->xv_port_id,
1225         xvimagesink->xwindow->win,
1226         xvimagesink->xwindow->gc, xvimage->xvimage,
1227         src_input.x, src_input.y, src_input.w, src_input.h,
1228         result.x, result.y, result.w, result.h, FALSE);
1229       GST_LOG_OBJECT( xvimagesink, "XvShmPutImage return value [%d]", ret );
1230     } else {
1231       GST_LOG_OBJECT( xvimagesink, "visible is FALSE. skip this image..." );
1232     }
1233 #else /* GST_EXT_XV_ENHANCEMENT */
1234     XvShmPutImage (xvimagesink->xcontext->disp,
1235         xvimagesink->xcontext->xv_port_id,
1236         xvimagesink->xwindow->win,
1237         xvimagesink->xwindow->gc, xvimage->xvimage,
1238         xvimagesink->disp_x, xvimagesink->disp_y,
1239         xvimagesink->disp_width, xvimagesink->disp_height,
1240         result.x, result.y, result.w, result.h, FALSE);
1241 #endif /* GST_EXT_XV_ENHANCEMENT */
1242   } else
1243 #endif /* HAVE_XSHM */
1244   {
1245     XvPutImage (xvimagesink->xcontext->disp,
1246         xvimagesink->xcontext->xv_port_id,
1247         xvimagesink->xwindow->win,
1248         xvimagesink->xwindow->gc, xvimage->xvimage,
1249         xvimagesink->disp_x, xvimagesink->disp_y,
1250         xvimagesink->disp_width, xvimagesink->disp_height,
1251         result.x, result.y, result.w, result.h);
1252   }
1253
1254   XSync (xvimagesink->xcontext->disp, FALSE);
1255
1256   g_mutex_unlock (xvimagesink->x_lock);
1257
1258   g_mutex_unlock (xvimagesink->flow_lock);
1259
1260   return TRUE;
1261 }
1262
1263 static gboolean
1264 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
1265     GstXWindow * window)
1266 {
1267   Atom hints_atom = None;
1268   MotifWmHints *hints;
1269
1270   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
1271   g_return_val_if_fail (window != NULL, FALSE);
1272
1273   g_mutex_lock (xvimagesink->x_lock);
1274
1275   hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
1276       True);
1277   if (hints_atom == None) {
1278     g_mutex_unlock (xvimagesink->x_lock);
1279     return FALSE;
1280   }
1281
1282   hints = g_malloc0 (sizeof (MotifWmHints));
1283
1284   hints->flags |= MWM_HINTS_DECORATIONS;
1285   hints->decorations = 1 << 0;
1286
1287   XChangeProperty (xvimagesink->xcontext->disp, window->win,
1288       hints_atom, hints_atom, 32, PropModeReplace,
1289       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
1290
1291   XSync (xvimagesink->xcontext->disp, FALSE);
1292
1293   g_mutex_unlock (xvimagesink->x_lock);
1294
1295   g_free (hints);
1296
1297   return TRUE;
1298 }
1299
1300 static void
1301 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
1302     GstXWindow * xwindow, const gchar * media_title)
1303 {
1304   if (media_title) {
1305     g_free (xvimagesink->media_title);
1306     xvimagesink->media_title = g_strdup (media_title);
1307   }
1308   if (xwindow) {
1309     /* we have a window */
1310     if (xwindow->internal) {
1311       XTextProperty xproperty;
1312       const gchar *app_name;
1313       const gchar *title = NULL;
1314       gchar *title_mem = NULL;
1315
1316       /* set application name as a title */
1317       app_name = g_get_application_name ();
1318
1319       if (app_name && xvimagesink->media_title) {
1320         title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
1321             app_name, NULL);
1322       } else if (app_name) {
1323         title = app_name;
1324       } else if (xvimagesink->media_title) {
1325         title = xvimagesink->media_title;
1326       }
1327
1328       if (title) {
1329         if ((XStringListToTextProperty (((char **) &title), 1,
1330                     &xproperty)) != 0) {
1331           XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
1332           XFree (xproperty.value);
1333         }
1334
1335         g_free (title_mem);
1336       }
1337     }
1338   }
1339 }
1340
1341 #ifdef GST_EXT_XV_ENHANCEMENT
1342 static XImage *make_transparent_image(Display *d, Window win, int w, int h)
1343 {
1344   XImage             *xim;
1345
1346   /* create a normal ximage */
1347   xim = XCreateImage(d, DefaultVisualOfScreen(DefaultScreenOfDisplay(d)),  24, ZPixmap, 0, NULL, w, h, 32, 0);
1348
1349   /* allocate data for it */
1350   if (xim) {
1351     xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
1352   }
1353
1354   memset(xim->data, 0x00, xim->bytes_per_line * xim->height);
1355   if (!xim || !xim->data) {
1356     /* failed to create XImage or allocate data memory */
1357     if (xim) {
1358       XDestroyImage(xim);
1359     }
1360
1361     return NULL;
1362   }
1363
1364   return xim;
1365 }
1366 #endif
1367
1368 /* This function handles a GstXWindow creation
1369  * The width and height are the actual pixel size on the display */
1370 static GstXWindow *
1371 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
1372     gint width, gint height)
1373 {
1374   GstXWindow *xwindow = NULL;
1375   XGCValues values;
1376 #ifdef GST_EXT_XV_ENHANCEMENT
1377   XSetWindowAttributes win_attr;
1378   XWindowAttributes root_attr;
1379 #endif
1380
1381   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1382
1383   xwindow = g_new0 (GstXWindow, 1);
1384
1385   xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1386 #ifdef GST_EXT_XV_ENHANCEMENT
1387   /* 0 or 180 */
1388   if (xvimagesink->rotate_angle == 0 || xvimagesink->rotate_angle == 2) {
1389     xvimagesink->render_rect.w = xwindow->width = width;
1390     xvimagesink->render_rect.h = xwindow->height = height;
1391   /* 90 or 270 */
1392   } else {
1393     xvimagesink->render_rect.w = xwindow->width = height;
1394     xvimagesink->render_rect.h = xwindow->height = width;
1395   }
1396
1397   XGetWindowAttributes(xvimagesink->xcontext->disp, xvimagesink->xcontext->root, &root_attr);
1398
1399   if (xwindow->width > root_attr.width) {
1400     GST_INFO_OBJECT(xvimagesink, "Width[%d] is bigger than Max width. Set Max[%d].",
1401                                  xwindow->width, root_attr.width);
1402     xvimagesink->render_rect.w = xwindow->width = root_attr.width;
1403   }
1404   if (xwindow->height > root_attr.height) {
1405     GST_INFO_OBJECT(xvimagesink, "Height[%d] is bigger than Max Height. Set Max[%d].",
1406                                  xwindow->height, root_attr.height);
1407     xvimagesink->render_rect.h = xwindow->height = root_attr.height;
1408   }
1409   xwindow->internal = TRUE;
1410
1411   g_mutex_lock (xvimagesink->x_lock);
1412
1413   GST_DEBUG_OBJECT( xvimagesink, "window create [%dx%d]", xwindow->width, xwindow->height );
1414
1415   xwindow->win = XCreateSimpleWindow(xvimagesink->xcontext->disp,
1416                                      xvimagesink->xcontext->root,
1417                                      0, 0, xwindow->width, xwindow->height,
1418                                      0, 0, 0);
1419
1420   xvimagesink->xim_transparenter = make_transparent_image(xvimagesink->xcontext->disp,
1421                                                           xvimagesink->xcontext->root,
1422                                                           xwindow->width, xwindow->height);
1423
1424   /* Make window manager not to change window size as Full screen */
1425   win_attr.override_redirect = True;
1426   XChangeWindowAttributes(xvimagesink->xcontext->disp, xwindow->win, CWOverrideRedirect, &win_attr);
1427 #else
1428   xvimagesink->render_rect.w = width;
1429   xvimagesink->render_rect.h = height;
1430
1431   xwindow->width = width;
1432   xwindow->height = height;
1433   xwindow->internal = TRUE;
1434
1435   g_mutex_lock (xvimagesink->x_lock);
1436
1437   xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
1438       xvimagesink->xcontext->root,
1439       0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
1440 #endif
1441
1442   /* We have to do that to prevent X from redrawing the background on
1443    * ConfigureNotify. This takes away flickering of video when resizing. */
1444   XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
1445
1446   /* set application name as a title */
1447   gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1448
1449   if (xvimagesink->handle_events) {
1450     Atom wm_delete;
1451
1452     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1453         StructureNotifyMask | PointerMotionMask | KeyPressMask |
1454         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1455
1456     /* Tell the window manager we'd like delete client messages instead of
1457      * being killed */
1458     wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1459         "WM_DELETE_WINDOW", True);
1460     if (wm_delete != None) {
1461       (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1462           &wm_delete, 1);
1463     }
1464   }
1465
1466   xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1467       xwindow->win, 0, &values);
1468
1469   XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1470
1471   XSync (xvimagesink->xcontext->disp, FALSE);
1472
1473   g_mutex_unlock (xvimagesink->x_lock);
1474
1475   gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1476
1477   gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1478
1479   return xwindow;
1480 }
1481
1482 /* This function destroys a GstXWindow */
1483 static void
1484 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1485     GstXWindow * xwindow)
1486 {
1487   g_return_if_fail (xwindow != NULL);
1488   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1489
1490   g_mutex_lock (xvimagesink->x_lock);
1491
1492   /* If we did not create that window we just free the GC and let it live */
1493   if (xwindow->internal)
1494     XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1495   else
1496     XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1497
1498   XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1499
1500   XSync (xvimagesink->xcontext->disp, FALSE);
1501
1502   g_mutex_unlock (xvimagesink->x_lock);
1503
1504   g_free (xwindow);
1505 }
1506
1507 static void
1508 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1509 {
1510 #ifdef GST_EXT_XV_ENHANCEMENT
1511   Window root_window, child_window;
1512   XWindowAttributes root_attr;
1513
1514   int cur_win_x = 0;
1515   int cur_win_y = 0;
1516   unsigned int cur_win_width = 0;
1517   unsigned int cur_win_height = 0;
1518   unsigned int cur_win_border_width = 0;
1519   unsigned int cur_win_depth = 0;
1520 #else
1521   XWindowAttributes attr;
1522 #endif
1523
1524   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1525
1526   /* Update the window geometry */
1527   g_mutex_lock (xvimagesink->x_lock);
1528   if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1529     g_mutex_unlock (xvimagesink->x_lock);
1530     return;
1531   }
1532
1533 #ifdef GST_EXT_XV_ENHANCEMENT
1534   /* Get root window and size of current window */
1535   XGetGeometry( xvimagesink->xcontext->disp, xvimagesink->xwindow->win, &root_window,
1536     &cur_win_x, &cur_win_y, /* relative x, y */
1537     &cur_win_width, &cur_win_height,
1538     &cur_win_border_width, &cur_win_depth );
1539
1540   xvimagesink->xwindow->width = cur_win_width;
1541   xvimagesink->xwindow->height = cur_win_height;
1542
1543   /* Get absolute coordinates of current window */
1544   XTranslateCoordinates( xvimagesink->xcontext->disp,
1545     xvimagesink->xwindow->win,
1546     root_window,
1547     0, 0,
1548     &cur_win_x, &cur_win_y, // relative x, y to root window == absolute x, y
1549     &child_window );
1550
1551   xvimagesink->xwindow->x = cur_win_x;
1552   xvimagesink->xwindow->y = cur_win_y;
1553
1554   /* Get size of root window == size of screen */
1555   XGetWindowAttributes(xvimagesink->xcontext->disp, root_window, &root_attr);
1556
1557   xvimagesink->scr_w = root_attr.width;
1558   xvimagesink->scr_h = root_attr.height;
1559
1560   if (!xvimagesink->have_render_rect) {
1561     xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1562     xvimagesink->render_rect.w = cur_win_width;
1563     xvimagesink->render_rect.h = cur_win_height;
1564   }
1565
1566   GST_LOG_OBJECT(xvimagesink, "screen size %dx%d, current window geometry %d,%d,%dx%d, render_rect %d,%d,%dx%d",
1567     xvimagesink->scr_w, xvimagesink->scr_h,
1568     xvimagesink->xwindow->x, xvimagesink->xwindow->y,
1569     xvimagesink->xwindow->width, xvimagesink->xwindow->height,
1570     xvimagesink->render_rect.x, xvimagesink->render_rect.y,
1571     xvimagesink->render_rect.w, xvimagesink->render_rect.h);
1572 #else
1573   XGetWindowAttributes (xvimagesink->xcontext->disp,
1574       xvimagesink->xwindow->win, &attr);
1575
1576   xvimagesink->xwindow->width = attr.width;
1577   xvimagesink->xwindow->height = attr.height;
1578
1579
1580   if (!xvimagesink->have_render_rect) {
1581     xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1582     xvimagesink->render_rect.w = attr.width;
1583     xvimagesink->render_rect.h = attr.height;
1584   }
1585 #endif
1586
1587   g_mutex_unlock (xvimagesink->x_lock);
1588 }
1589
1590 static void
1591 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1592     GstXWindow * xwindow)
1593 {
1594   g_return_if_fail (xwindow != NULL);
1595   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1596
1597   g_mutex_lock (xvimagesink->x_lock);
1598
1599   XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1600       xwindow->win);
1601 #ifndef GST_EXT_XV_ENHANCEMENT
1602   /* Preview area is not updated before other UI is updated in the screen. */
1603   XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
1604       xvimagesink->xcontext->black);
1605
1606   XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1607       xvimagesink->render_rect.x, xvimagesink->render_rect.y,
1608       xvimagesink->render_rect.w, xvimagesink->render_rect.h);
1609 #endif
1610
1611   XSync (xvimagesink->xcontext->disp, FALSE);
1612
1613   g_mutex_unlock (xvimagesink->x_lock);
1614 }
1615
1616 /* This function commits our internal colorbalance settings to our grabbed Xv
1617    port. If the xcontext is not initialized yet it simply returns */
1618 static void
1619 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1620 {
1621   GList *channels = NULL;
1622
1623   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1624
1625   /* If we haven't initialized the X context we can't update anything */
1626   if (xvimagesink->xcontext == NULL)
1627     return;
1628
1629   /* Don't set the attributes if they haven't been changed, to avoid
1630    * rounding errors changing the values */
1631   if (!xvimagesink->cb_changed)
1632     return;
1633
1634   /* For each channel of the colorbalance we calculate the correct value
1635      doing range conversion and then set the Xv port attribute to match our
1636      values. */
1637   channels = xvimagesink->xcontext->channels_list;
1638
1639   while (channels) {
1640     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1641       GstColorBalanceChannel *channel = NULL;
1642       Atom prop_atom;
1643       gint value = 0;
1644       gdouble convert_coef;
1645
1646       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1647       g_object_ref (channel);
1648
1649       /* Our range conversion coef */
1650       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1651
1652       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1653         value = xvimagesink->hue;
1654       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1655         value = xvimagesink->saturation;
1656       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1657         value = xvimagesink->contrast;
1658       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1659         value = xvimagesink->brightness;
1660       } else {
1661         g_warning ("got an unknown channel %s", channel->label);
1662         g_object_unref (channel);
1663         return;
1664       }
1665
1666       /* Committing to Xv port */
1667       g_mutex_lock (xvimagesink->x_lock);
1668       prop_atom =
1669           XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1670       if (prop_atom != None) {
1671         int xv_value;
1672         xv_value =
1673             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1674         XvSetPortAttribute (xvimagesink->xcontext->disp,
1675             xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1676       }
1677       g_mutex_unlock (xvimagesink->x_lock);
1678
1679       g_object_unref (channel);
1680     }
1681     channels = g_list_next (channels);
1682   }
1683 }
1684
1685 /* This function handles XEvents that might be in the queue. It generates
1686    GstEvent that will be sent upstream in the pipeline to handle interactivity
1687    and navigation. It will also listen for configure events on the window to
1688    trigger caps renegotiation so on the fly software scaling can work. */
1689 static void
1690 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1691 {
1692   XEvent e;
1693   guint pointer_x = 0, pointer_y = 0;
1694   gboolean pointer_moved = FALSE;
1695   gboolean exposed = FALSE, configured = FALSE;
1696
1697   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1698
1699   /* Handle Interaction, produces navigation events */
1700
1701   /* We get all pointer motion events, only the last position is
1702      interesting. */
1703   g_mutex_lock (xvimagesink->flow_lock);
1704   g_mutex_lock (xvimagesink->x_lock);
1705   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1706           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1707     g_mutex_unlock (xvimagesink->x_lock);
1708     g_mutex_unlock (xvimagesink->flow_lock);
1709
1710     switch (e.type) {
1711       case MotionNotify:
1712         pointer_x = e.xmotion.x;
1713         pointer_y = e.xmotion.y;
1714         pointer_moved = TRUE;
1715         break;
1716       default:
1717         break;
1718     }
1719     g_mutex_lock (xvimagesink->flow_lock);
1720     g_mutex_lock (xvimagesink->x_lock);
1721   }
1722   if (pointer_moved) {
1723     g_mutex_unlock (xvimagesink->x_lock);
1724     g_mutex_unlock (xvimagesink->flow_lock);
1725
1726     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1727         pointer_x, pointer_y);
1728     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1729         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1730
1731     g_mutex_lock (xvimagesink->flow_lock);
1732     g_mutex_lock (xvimagesink->x_lock);
1733   }
1734
1735   /* We get all events on our window to throw them upstream */
1736   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1737           xvimagesink->xwindow->win,
1738           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1739           &e)) {
1740     KeySym keysym;
1741
1742     /* We lock only for the X function call */
1743     g_mutex_unlock (xvimagesink->x_lock);
1744     g_mutex_unlock (xvimagesink->flow_lock);
1745
1746     switch (e.type) {
1747       case ButtonPress:
1748         /* Mouse button pressed over our window. We send upstream
1749            events for interactivity/navigation */
1750         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1751             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1752         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1753             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1754         break;
1755       case ButtonRelease:
1756         /* Mouse button released over our window. We send upstream
1757            events for interactivity/navigation */
1758         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1759             e.xbutton.button, e.xbutton.x, e.xbutton.y);
1760         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1761             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1762         break;
1763       case KeyPress:
1764       case KeyRelease:
1765         /* Key pressed/released over our window. We send upstream
1766            events for interactivity/navigation */
1767         GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1768             e.xkey.keycode, e.xkey.x, e.xkey.y);
1769         g_mutex_lock (xvimagesink->x_lock);
1770         keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1771             e.xkey.keycode, 0);
1772         g_mutex_unlock (xvimagesink->x_lock);
1773         if (keysym != NoSymbol) {
1774           char *key_str = NULL;
1775
1776           g_mutex_lock (xvimagesink->x_lock);
1777           key_str = XKeysymToString (keysym);
1778           g_mutex_unlock (xvimagesink->x_lock);
1779           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1780               e.type == KeyPress ? "key-press" : "key-release", key_str);
1781         } else {
1782           gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1783               e.type == KeyPress ? "key-press" : "key-release", "unknown");
1784         }
1785         break;
1786       default:
1787         GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1788     }
1789     g_mutex_lock (xvimagesink->flow_lock);
1790     g_mutex_lock (xvimagesink->x_lock);
1791   }
1792
1793   /* Handle Expose */
1794   while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1795           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1796     switch (e.type) {
1797       case Expose:
1798         exposed = TRUE;
1799         break;
1800       case ConfigureNotify:
1801         g_mutex_unlock (xvimagesink->x_lock);
1802         GST_INFO_OBJECT (xvimagesink, "Call gst_xvimagesink_xwindow_update_geometry!");
1803         gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1804         g_mutex_lock (xvimagesink->x_lock);
1805         configured = TRUE;
1806         break;
1807       default:
1808         break;
1809     }
1810   }
1811
1812   if (xvimagesink->handle_expose && (exposed || configured)) {
1813     g_mutex_unlock (xvimagesink->x_lock);
1814     g_mutex_unlock (xvimagesink->flow_lock);
1815
1816     gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1817
1818     g_mutex_lock (xvimagesink->flow_lock);
1819     g_mutex_lock (xvimagesink->x_lock);
1820   }
1821
1822   /* Handle Display events */
1823   while (XPending (xvimagesink->xcontext->disp)) {
1824     XNextEvent (xvimagesink->xcontext->disp, &e);
1825     switch (e.type) {
1826       case ClientMessage:{
1827 #ifdef GST_EXT_XV_ENHANCEMENT
1828         int active_pid, deactive_pid;
1829         Atom active_win_atom;
1830         Atom deactive_win_atom;
1831         Atom atom_stream;
1832 #endif
1833         Atom wm_delete;
1834
1835         wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1836             "WM_DELETE_WINDOW", True);
1837         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1838           /* Handle window deletion by posting an error on the bus */
1839           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1840               ("Output window was closed"), (NULL));
1841
1842           g_mutex_unlock (xvimagesink->x_lock);
1843           gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1844           xvimagesink->xwindow = NULL;
1845           g_mutex_lock (xvimagesink->x_lock);
1846         }
1847 #ifdef GST_EXT_XV_ENHANCEMENT
1848         GST_INFO_OBJECT( xvimagesink, "message type : %d", e.xclient.message_type );
1849
1850         active_win_atom   = XInternAtom (xvimagesink->xcontext->disp, "_X_ILLUME_ACTIVATE_WINDOW", False);
1851         deactive_win_atom = XInternAtom (xvimagesink->xcontext->disp, "_X_ILLUME_DEACTIVATE_WINDOW", False);
1852
1853         if( !active_win_atom || !deactive_win_atom )
1854         {
1855           GST_WARNING_OBJECT( xvimagesink, "Can NOT get active[%d] or deactive[%d] win atom",
1856                                            active_win_atom, deactive_win_atom );
1857         }
1858         else
1859         {
1860           /* Window is DEACTIVATED */
1861           if( e.xclient.message_type == deactive_win_atom )
1862           {
1863             /* Get pid of activated/deactivated window */
1864             active_pid = e.xclient.data.l[1];
1865             deactive_pid = e.xclient.data.l[3];
1866
1867             if( active_pid != deactive_pid )
1868             {
1869               GST_INFO_OBJECT( xvimagesink, "XClientMessage : Window DEACTIVATED" );
1870
1871               xvimagesink->is_hided = TRUE;
1872               atom_stream = XInternAtom( xvimagesink->xcontext->disp,
1873                                               "_USER_WM_PORT_ATTRIBUTE_STREAM_OFF", False );
1874               if( atom_stream != None )
1875               {
1876                 if( XvSetPortAttribute( xvimagesink->xcontext->disp,
1877                                         xvimagesink->xcontext->xv_port_id,
1878                                         atom_stream, 0 ) != Success )
1879                 {
1880                   GST_WARNING_OBJECT( xvimagesink, "STREAM OFF failed" );
1881                 }
1882                 XSync( xvimagesink->xcontext->disp, FALSE );
1883               }
1884             }
1885             else
1886             {
1887               GST_INFO_OBJECT( xvimagesink, "Window is DEACTIVATED, but same process. so Do not clear screen." );
1888             }
1889           }
1890           /* Window is ACTIVATED */
1891           else if( e.xclient.message_type == active_win_atom )
1892           {
1893             /* Get pid of activated/deactivated window */
1894             active_pid = e.xclient.data.l[1];
1895             deactive_pid = e.xclient.data.l[3];
1896
1897             if( active_pid != deactive_pid )
1898             {
1899               GST_INFO_OBJECT( xvimagesink, "XClientMessage : Window ACTIVATED" );
1900
1901               g_mutex_unlock (xvimagesink->x_lock);
1902               g_mutex_unlock (xvimagesink->flow_lock);
1903
1904               xvimagesink->is_hided = FALSE;
1905               gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1906
1907               g_mutex_lock (xvimagesink->flow_lock);
1908               g_mutex_lock (xvimagesink->x_lock);
1909             }
1910             else
1911             {
1912               GST_INFO_OBJECT( xvimagesink, "Window is ACTIVATED, but same process. so Do not redraw screen." );
1913             }
1914           }
1915         }
1916 #endif
1917       }
1918         break;
1919       default:
1920         break;
1921     }
1922   }
1923
1924   g_mutex_unlock (xvimagesink->x_lock);
1925   g_mutex_unlock (xvimagesink->flow_lock);
1926 }
1927
1928 static void
1929 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1930     XvAdaptorInfo * adaptors, int adaptor_no)
1931 {
1932   gint j;
1933   gint res;
1934
1935   /* Do we support XvImageMask ? */
1936   if (!(adaptors[adaptor_no].type & XvImageMask)) {
1937     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1938         adaptors[adaptor_no].name);
1939     return;
1940   }
1941
1942   /* We found such an adaptor, looking for an available port */
1943   for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1944     /* We try to grab the port */
1945     res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1946     if (Success == res) {
1947       xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1948       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1949           adaptors[adaptor_no].num_ports);
1950     } else {
1951       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1952           adaptors[adaptor_no].name, res);
1953     }
1954   }
1955 }
1956
1957 /* This function generates a caps with all supported format by the first
1958    Xv grabable port we find. We store each one of the supported formats in a
1959    format list and append the format to a newly created caps that we return
1960    If this function does not return NULL because of an error, it also grabs
1961    the port via XvGrabPort */
1962 static GstCaps *
1963 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1964     GstXContext * xcontext)
1965 {
1966   gint i;
1967   XvAdaptorInfo *adaptors;
1968   gint nb_formats;
1969   XvImageFormatValues *formats = NULL;
1970   guint nb_encodings;
1971   XvEncodingInfo *encodings = NULL;
1972   gulong max_w = G_MAXINT, max_h = G_MAXINT;
1973   GstCaps *caps = NULL;
1974   GstCaps *rgb_caps = NULL;
1975
1976   g_return_val_if_fail (xcontext != NULL, NULL);
1977
1978   /* First let's check that XVideo extension is available */
1979   if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1980     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1981         ("Could not initialise Xv output"),
1982         ("XVideo extension is not available"));
1983     return NULL;
1984   }
1985
1986   /* Then we get adaptors list */
1987   if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1988           &xcontext->nb_adaptors, &adaptors)) {
1989     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1990         ("Could not initialise Xv output"),
1991         ("Failed getting XV adaptors list"));
1992     return NULL;
1993   }
1994
1995   xcontext->xv_port_id = 0;
1996
1997   GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1998
1999   xcontext->adaptors =
2000       (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
2001
2002   /* Now fill up our adaptor name array */
2003   for (i = 0; i < xcontext->nb_adaptors; i++) {
2004     xcontext->adaptors[i] = g_strdup (adaptors[i].name);
2005   }
2006
2007   if (xvimagesink->adaptor_no >= 0 &&
2008       xvimagesink->adaptor_no < xcontext->nb_adaptors) {
2009     /* Find xv port from user defined adaptor */
2010     gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
2011         xvimagesink->adaptor_no);
2012   }
2013
2014   if (!xcontext->xv_port_id) {
2015     /* Now search for an adaptor that supports XvImageMask */
2016     for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
2017       gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
2018       xvimagesink->adaptor_no = i;
2019     }
2020   }
2021
2022   XvFreeAdaptorInfo (adaptors);
2023
2024   if (!xcontext->xv_port_id) {
2025     xvimagesink->adaptor_no = -1;
2026     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
2027         ("Could not initialise Xv output"), ("No port available"));
2028     return NULL;
2029   }
2030
2031   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
2032   {
2033     int count, todo = 3;
2034     XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
2035         xcontext->xv_port_id, &count);
2036     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
2037     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
2038     static const char colorkey[] = "XV_COLORKEY";
2039
2040     GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
2041
2042     xvimagesink->have_autopaint_colorkey = FALSE;
2043     xvimagesink->have_double_buffer = FALSE;
2044     xvimagesink->have_colorkey = FALSE;
2045
2046     for (i = 0; ((i < count) && todo); i++)
2047       if (!strcmp (attr[i].name, autopaint)) {
2048         const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
2049
2050         /* turn on autopaint colorkey */
2051         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2052             (xvimagesink->autopaint_colorkey ? 1 : 0));
2053         todo--;
2054         xvimagesink->have_autopaint_colorkey = TRUE;
2055       } else if (!strcmp (attr[i].name, dbl_buffer)) {
2056         const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
2057
2058         XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2059             (xvimagesink->double_buffer ? 1 : 0));
2060         todo--;
2061         xvimagesink->have_double_buffer = TRUE;
2062       } else if (!strcmp (attr[i].name, colorkey)) {
2063         /* Set the colorkey, default is something that is dark but hopefully
2064          * won't randomly appear on the screen elsewhere (ie not black or greys)
2065          * can be overridden by setting "colorkey" property
2066          */
2067         const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
2068         guint32 ckey = 0;
2069         gboolean set_attr = TRUE;
2070         guint cr, cg, cb;
2071
2072         /* set a colorkey in the right format RGB565/RGB888
2073          * We only handle these 2 cases, because they're the only types of
2074          * devices we've encountered. If we don't recognise it, leave it alone
2075          */
2076         cr = (xvimagesink->colorkey >> 16);
2077         cg = (xvimagesink->colorkey >> 8) & 0xFF;
2078         cb = (xvimagesink->colorkey) & 0xFF;
2079         switch (xcontext->depth) {
2080           case 16:             /* RGB 565 */
2081             cr >>= 3;
2082             cg >>= 2;
2083             cb >>= 3;
2084             ckey = (cr << 11) | (cg << 5) | cb;
2085             break;
2086           case 24:
2087           case 32:             /* RGB 888 / ARGB 8888 */
2088             ckey = (cr << 16) | (cg << 8) | cb;
2089             break;
2090           default:
2091             GST_DEBUG_OBJECT (xvimagesink,
2092                 "Unknown bit depth %d for Xv Colorkey - not adjusting",
2093                 xcontext->depth);
2094             set_attr = FALSE;
2095             break;
2096         }
2097
2098         if (set_attr) {
2099           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
2100               (guint32) attr[i].max_value);
2101           GST_LOG_OBJECT (xvimagesink,
2102               "Setting color key for display depth %d to 0x%x",
2103               xcontext->depth, ckey);
2104
2105           XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2106               (gint) ckey);
2107         }
2108         todo--;
2109         xvimagesink->have_colorkey = TRUE;
2110       }
2111
2112     XFree (attr);
2113   }
2114
2115   /* Get the list of encodings supported by the adapter and look for the
2116    * XV_IMAGE encoding so we can determine the maximum width and height
2117    * supported */
2118   XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
2119       &encodings);
2120
2121   for (i = 0; i < nb_encodings; i++) {
2122     GST_LOG_OBJECT (xvimagesink,
2123         "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
2124         i, encodings[i].name, encodings[i].width, encodings[i].height,
2125         encodings[i].rate.numerator, encodings[i].rate.denominator);
2126     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
2127       max_w = encodings[i].width;
2128       max_h = encodings[i].height;
2129 #ifdef GST_EXT_XV_ENHANCEMENT
2130       xvimagesink->scr_w = max_w;
2131       xvimagesink->scr_h = max_h;
2132 #endif
2133     }
2134   }
2135
2136   XvFreeEncodingInfo (encodings);
2137
2138   /* We get all image formats supported by our port */
2139   formats = XvListImageFormats (xcontext->disp,
2140       xcontext->xv_port_id, &nb_formats);
2141   caps = gst_caps_new_empty ();
2142   for (i = 0; i < nb_formats; i++) {
2143     GstCaps *format_caps = NULL;
2144     gboolean is_rgb_format = FALSE;
2145
2146     /* We set the image format of the xcontext to an existing one. This
2147        is just some valid image format for making our xshm calls check before
2148        caps negotiation really happens. */
2149     xcontext->im_format = formats[i].id;
2150
2151     switch (formats[i].type) {
2152       case XvRGB:
2153       {
2154         XvImageFormatValues *fmt = &(formats[i]);
2155         gint endianness = G_BIG_ENDIAN;
2156
2157         if (fmt->byte_order == LSBFirst) {
2158           /* our caps system handles 24/32bpp RGB as big-endian. */
2159           if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
2160             fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
2161             fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
2162             fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
2163
2164             if (fmt->bits_per_pixel == 24) {
2165               fmt->red_mask >>= 8;
2166               fmt->green_mask >>= 8;
2167               fmt->blue_mask >>= 8;
2168             }
2169           } else
2170             endianness = G_LITTLE_ENDIAN;
2171         }
2172
2173         format_caps = gst_caps_new_simple ("video/x-raw-rgb",
2174             "endianness", G_TYPE_INT, endianness,
2175             "depth", G_TYPE_INT, fmt->depth,
2176             "bpp", G_TYPE_INT, fmt->bits_per_pixel,
2177             "red_mask", G_TYPE_INT, fmt->red_mask,
2178             "green_mask", G_TYPE_INT, fmt->green_mask,
2179             "blue_mask", G_TYPE_INT, fmt->blue_mask,
2180             "width", GST_TYPE_INT_RANGE, 1, max_w,
2181             "height", GST_TYPE_INT_RANGE, 1, max_h,
2182             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
2183
2184         is_rgb_format = TRUE;
2185         break;
2186       }
2187       case XvYUV:
2188         format_caps = gst_caps_new_simple ("video/x-raw-yuv",
2189             "format", GST_TYPE_FOURCC, formats[i].id,
2190             "width", GST_TYPE_INT_RANGE, 1, max_w,
2191             "height", GST_TYPE_INT_RANGE, 1, max_h,
2192             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
2193         break;
2194       default:
2195         g_assert_not_reached ();
2196         break;
2197     }
2198
2199     if (format_caps) {
2200       GstXvImageFormat *format = NULL;
2201
2202       format = g_new0 (GstXvImageFormat, 1);
2203       if (format) {
2204         format->format = formats[i].id;
2205         format->caps = gst_caps_copy (format_caps);
2206         xcontext->formats_list = g_list_append (xcontext->formats_list, format);
2207       }
2208
2209       if (is_rgb_format) {
2210         if (rgb_caps == NULL)
2211           rgb_caps = format_caps;
2212         else
2213           gst_caps_append (rgb_caps, format_caps);
2214       } else
2215         gst_caps_append (caps, format_caps);
2216     }
2217   }
2218
2219   /* Collected all caps into either the caps or rgb_caps structures.
2220    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
2221   if (rgb_caps)
2222     gst_caps_append (caps, rgb_caps);
2223
2224   if (formats)
2225     XFree (formats);
2226
2227   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
2228
2229   if (gst_caps_is_empty (caps)) {
2230     gst_caps_unref (caps);
2231     XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2232     GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
2233         ("No supported format found"));
2234     return NULL;
2235   }
2236
2237   return caps;
2238 }
2239
2240 static gpointer
2241 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
2242 {
2243   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2244
2245   GST_OBJECT_LOCK (xvimagesink);
2246   while (xvimagesink->running) {
2247     GST_OBJECT_UNLOCK (xvimagesink);
2248
2249     if (xvimagesink->xwindow) {
2250       gst_xvimagesink_handle_xevents (xvimagesink);
2251     }
2252     /* FIXME: do we want to align this with the framerate or anything else? */
2253     g_usleep (G_USEC_PER_SEC / 20);
2254
2255     GST_OBJECT_LOCK (xvimagesink);
2256   }
2257   GST_OBJECT_UNLOCK (xvimagesink);
2258
2259   return NULL;
2260 }
2261
2262 static void
2263 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
2264 {
2265   GThread *thread = NULL;
2266
2267   /* don't start the thread too early */
2268   if (xvimagesink->xcontext == NULL) {
2269     return;
2270   }
2271
2272   GST_OBJECT_LOCK (xvimagesink);
2273   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
2274     if (!xvimagesink->event_thread) {
2275       /* Setup our event listening thread */
2276       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
2277           xvimagesink->handle_expose, xvimagesink->handle_events);
2278       xvimagesink->running = TRUE;
2279       xvimagesink->event_thread = g_thread_create (
2280           (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
2281     }
2282   } else {
2283     if (xvimagesink->event_thread) {
2284       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
2285           xvimagesink->handle_expose, xvimagesink->handle_events);
2286       xvimagesink->running = FALSE;
2287       /* grab thread and mark it as NULL */
2288       thread = xvimagesink->event_thread;
2289       xvimagesink->event_thread = NULL;
2290     }
2291   }
2292   GST_OBJECT_UNLOCK (xvimagesink);
2293
2294   /* Wait for our event thread to finish */
2295   if (thread)
2296     g_thread_join (thread);
2297
2298 }
2299
2300
2301 /* This function calculates the pixel aspect ratio based on the properties
2302  * in the xcontext structure and stores it there. */
2303 static void
2304 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
2305 {
2306   static const gint par[][2] = {
2307     {1, 1},                     /* regular screen */
2308     {16, 15},                   /* PAL TV */
2309     {11, 10},                   /* 525 line Rec.601 video */
2310     {54, 59},                   /* 625 line Rec.601 video */
2311     {64, 45},                   /* 1280x1024 on 16:9 display */
2312     {5, 3},                     /* 1280x1024 on 4:3 display */
2313     {4, 3}                      /*  800x600 on 16:9 display */
2314   };
2315   gint i;
2316   gint index;
2317   gdouble ratio;
2318   gdouble delta;
2319
2320 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
2321
2322   /* first calculate the "real" ratio based on the X values;
2323    * which is the "physical" w/h divided by the w/h in pixels of the display */
2324   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
2325       / (xcontext->heightmm * xcontext->width);
2326
2327   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
2328    * override here */
2329   if (xcontext->width == 720 && xcontext->height == 576) {
2330     ratio = 4.0 * 576 / (3.0 * 720);
2331   }
2332   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
2333   /* now find the one from par[][2] with the lowest delta to the real one */
2334   delta = DELTA (0);
2335   index = 0;
2336
2337   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
2338     gdouble this_delta = DELTA (i);
2339
2340     if (this_delta < delta) {
2341       index = i;
2342       delta = this_delta;
2343     }
2344   }
2345
2346   GST_DEBUG ("Decided on index %d (%d/%d)", index,
2347       par[index][0], par[index][1]);
2348
2349   g_free (xcontext->par);
2350   xcontext->par = g_new0 (GValue, 1);
2351   g_value_init (xcontext->par, GST_TYPE_FRACTION);
2352   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
2353   GST_DEBUG ("set xcontext PAR to %d/%d",
2354       gst_value_get_fraction_numerator (xcontext->par),
2355       gst_value_get_fraction_denominator (xcontext->par));
2356 }
2357
2358 /* This function gets the X Display and global info about it. Everything is
2359    stored in our object and will be cleaned when the object is disposed. Note
2360    here that caps for supported format are generated without any window or
2361    image creation */
2362 static GstXContext *
2363 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
2364 {
2365   GstXContext *xcontext = NULL;
2366   XPixmapFormatValues *px_formats = NULL;
2367   gint nb_formats = 0, i, j, N_attr;
2368   XvAttribute *xv_attr;
2369   Atom prop_atom;
2370   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
2371     "XV_BRIGHTNESS", "XV_CONTRAST"
2372   };
2373
2374   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2375
2376   xcontext = g_new0 (GstXContext, 1);
2377   xcontext->im_format = 0;
2378
2379   g_mutex_lock (xvimagesink->x_lock);
2380
2381   xcontext->disp = XOpenDisplay (xvimagesink->display_name);
2382
2383   if (!xcontext->disp) {
2384     g_mutex_unlock (xvimagesink->x_lock);
2385     g_free (xcontext);
2386     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2387         ("Could not initialise Xv output"), ("Could not open display"));
2388     return NULL;
2389   }
2390
2391   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
2392   xcontext->screen_num = DefaultScreen (xcontext->disp);
2393   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
2394   xcontext->root = DefaultRootWindow (xcontext->disp);
2395   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
2396   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
2397   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
2398
2399   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
2400   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
2401   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
2402   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
2403
2404   GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
2405       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
2406
2407   gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
2408   /* We get supported pixmap formats at supported depth */
2409   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
2410
2411   if (!px_formats) {
2412     XCloseDisplay (xcontext->disp);
2413     g_mutex_unlock (xvimagesink->x_lock);
2414     g_free (xcontext->par);
2415     g_free (xcontext);
2416     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
2417         ("Could not initialise Xv output"), ("Could not get pixel formats"));
2418     return NULL;
2419   }
2420
2421   /* We get bpp value corresponding to our running depth */
2422   for (i = 0; i < nb_formats; i++) {
2423     if (px_formats[i].depth == xcontext->depth)
2424       xcontext->bpp = px_formats[i].bits_per_pixel;
2425   }
2426
2427   XFree (px_formats);
2428
2429   xcontext->endianness =
2430       (ImageByteOrder (xcontext->disp) ==
2431       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
2432
2433   /* our caps system handles 24/32bpp RGB as big-endian. */
2434   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
2435       xcontext->endianness == G_LITTLE_ENDIAN) {
2436     xcontext->endianness = G_BIG_ENDIAN;
2437     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
2438     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
2439     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
2440     if (xcontext->bpp == 24) {
2441       xcontext->visual->red_mask >>= 8;
2442       xcontext->visual->green_mask >>= 8;
2443       xcontext->visual->blue_mask >>= 8;
2444     }
2445   }
2446
2447   xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
2448
2449   if (!xcontext->caps) {
2450     XCloseDisplay (xcontext->disp);
2451     g_mutex_unlock (xvimagesink->x_lock);
2452     g_free (xcontext->par);
2453     g_free (xcontext);
2454     /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
2455     return NULL;
2456   }
2457 #ifdef HAVE_XSHM
2458   /* Search for XShm extension support */
2459   if (XShmQueryExtension (xcontext->disp) &&
2460       gst_xvimagesink_check_xshm_calls (xcontext)) {
2461     xcontext->use_xshm = TRUE;
2462     GST_DEBUG ("xvimagesink is using XShm extension");
2463   } else
2464 #endif /* HAVE_XSHM */
2465   {
2466     xcontext->use_xshm = FALSE;
2467     GST_DEBUG ("xvimagesink is not using XShm extension");
2468   }
2469
2470   xv_attr = XvQueryPortAttributes (xcontext->disp,
2471       xcontext->xv_port_id, &N_attr);
2472
2473
2474   /* Generate the channels list */
2475   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
2476     XvAttribute *matching_attr = NULL;
2477
2478     /* Retrieve the property atom if it exists. If it doesn't exist, 
2479      * the attribute itself must not either, so we can skip */
2480     prop_atom = XInternAtom (xcontext->disp, channels[i], True);
2481     if (prop_atom == None)
2482       continue;
2483
2484     if (xv_attr != NULL) {
2485       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
2486         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
2487           matching_attr = xv_attr + j;
2488     }
2489
2490     if (matching_attr) {
2491       GstColorBalanceChannel *channel;
2492
2493       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
2494       channel->label = g_strdup (channels[i]);
2495       channel->min_value = matching_attr ? matching_attr->min_value : -1000;
2496       channel->max_value = matching_attr ? matching_attr->max_value : 1000;
2497
2498       xcontext->channels_list = g_list_append (xcontext->channels_list,
2499           channel);
2500
2501       /* If the colorbalance settings have not been touched we get Xv values
2502          as defaults and update our internal variables */
2503       if (!xvimagesink->cb_changed) {
2504         gint val;
2505
2506         XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
2507             prop_atom, &val);
2508         /* Normalize val to [-1000, 1000] */
2509         val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
2510             (double) (channel->max_value - channel->min_value));
2511
2512         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
2513           xvimagesink->hue = val;
2514         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
2515           xvimagesink->saturation = val;
2516         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
2517           xvimagesink->brightness = val;
2518         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
2519           xvimagesink->contrast = val;
2520       }
2521     }
2522   }
2523
2524   if (xv_attr)
2525     XFree (xv_attr);
2526
2527   g_mutex_unlock (xvimagesink->x_lock);
2528
2529   return xcontext;
2530 }
2531
2532 /* This function cleans the X context. Closing the Display, releasing the XV
2533    port and unrefing the caps for supported formats. */
2534 static void
2535 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
2536 {
2537   GList *formats_list, *channels_list;
2538   GstXContext *xcontext;
2539   gint i = 0;
2540
2541   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2542
2543   GST_OBJECT_LOCK (xvimagesink);
2544   if (xvimagesink->xcontext == NULL) {
2545     GST_OBJECT_UNLOCK (xvimagesink);
2546     return;
2547   }
2548
2549   /* Take the XContext from the sink and clean it up */
2550   xcontext = xvimagesink->xcontext;
2551   xvimagesink->xcontext = NULL;
2552
2553   GST_OBJECT_UNLOCK (xvimagesink);
2554
2555
2556   formats_list = xcontext->formats_list;
2557
2558   while (formats_list) {
2559     GstXvImageFormat *format = formats_list->data;
2560
2561     gst_caps_unref (format->caps);
2562     g_free (format);
2563     formats_list = g_list_next (formats_list);
2564   }
2565
2566   if (xcontext->formats_list)
2567     g_list_free (xcontext->formats_list);
2568
2569   channels_list = xcontext->channels_list;
2570
2571   while (channels_list) {
2572     GstColorBalanceChannel *channel = channels_list->data;
2573
2574     g_object_unref (channel);
2575     channels_list = g_list_next (channels_list);
2576   }
2577
2578   if (xcontext->channels_list)
2579     g_list_free (xcontext->channels_list);
2580
2581   gst_caps_unref (xcontext->caps);
2582   if (xcontext->last_caps)
2583     gst_caps_replace (&xcontext->last_caps, NULL);
2584
2585   for (i = 0; i < xcontext->nb_adaptors; i++) {
2586     g_free (xcontext->adaptors[i]);
2587   }
2588
2589   g_free (xcontext->adaptors);
2590
2591   g_free (xcontext->par);
2592
2593   g_mutex_lock (xvimagesink->x_lock);
2594
2595   GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
2596
2597   XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2598
2599   XCloseDisplay (xcontext->disp);
2600
2601   g_mutex_unlock (xvimagesink->x_lock);
2602
2603   g_free (xcontext);
2604 }
2605
2606 static void
2607 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2608 {
2609   g_mutex_lock (xvimagesink->pool_lock);
2610
2611   while (xvimagesink->image_pool) {
2612     GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2613
2614     xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2615         xvimagesink->image_pool);
2616     gst_xvimage_buffer_free (xvimage);
2617   }
2618
2619   g_mutex_unlock (xvimagesink->pool_lock);
2620 }
2621
2622 /* Element stuff */
2623
2624 /* This function tries to get a format matching with a given caps in the
2625    supported list of formats we generated in gst_xvimagesink_get_xv_support */
2626 static gint
2627 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2628     GstCaps * caps)
2629 {
2630   GList *list = NULL;
2631
2632   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2633
2634   list = xvimagesink->xcontext->formats_list;
2635
2636   while (list) {
2637     GstXvImageFormat *format = list->data;
2638
2639     if (format) {
2640       if (gst_caps_can_intersect (caps, format->caps)) {
2641         return format->format;
2642       }
2643     }
2644     list = g_list_next (list);
2645   }
2646
2647   return -1;
2648 }
2649
2650 static GstCaps *
2651 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2652 {
2653   GstXvImageSink *xvimagesink;
2654
2655   xvimagesink = GST_XVIMAGESINK (bsink);
2656
2657   if (xvimagesink->xcontext)
2658     return gst_caps_ref (xvimagesink->xcontext->caps);
2659
2660   return
2661       gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2662           (xvimagesink)));
2663 }
2664
2665 static gboolean
2666 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2667 {
2668   GstXvImageSink *xvimagesink;
2669   GstStructure *structure;
2670   guint32 im_format = 0;
2671   gboolean ret;
2672   gint video_width, video_height;
2673   gint disp_x, disp_y;
2674   gint disp_width, disp_height;
2675   gint video_par_n, video_par_d;        /* video's PAR */
2676   gint display_par_n, display_par_d;    /* display's PAR */
2677   const GValue *caps_par;
2678   const GValue *caps_disp_reg;
2679   const GValue *fps;
2680   guint num, den;
2681
2682   xvimagesink = GST_XVIMAGESINK (bsink);
2683
2684   GST_DEBUG_OBJECT (xvimagesink,
2685       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2686       GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2687
2688   if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2689     goto incompatible_caps;
2690
2691   structure = gst_caps_get_structure (caps, 0);
2692   ret = gst_structure_get_int (structure, "width", &video_width);
2693   ret &= gst_structure_get_int (structure, "height", &video_height);
2694   fps = gst_structure_get_value (structure, "framerate");
2695   ret &= (fps != NULL);
2696
2697   if (!ret)
2698     goto incomplete_caps;
2699
2700 #ifdef GST_EXT_XV_ENHANCEMENT
2701   xvimagesink->aligned_width = video_width;
2702   xvimagesink->aligned_height = video_height;
2703 #endif
2704
2705   xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2706   xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2707
2708   xvimagesink->video_width = video_width;
2709   xvimagesink->video_height = video_height;
2710
2711   im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2712   if (im_format == -1)
2713     goto invalid_format;
2714
2715   /* get aspect ratio from caps if it's present, and
2716    * convert video width and height to a display width and height
2717    * using wd / hd = wv / hv * PARv / PARd */
2718
2719   /* get video's PAR */
2720   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2721   if (caps_par) {
2722     video_par_n = gst_value_get_fraction_numerator (caps_par);
2723     video_par_d = gst_value_get_fraction_denominator (caps_par);
2724   } else {
2725     video_par_n = 1;
2726     video_par_d = 1;
2727   }
2728   /* get display's PAR */
2729   if (xvimagesink->par) {
2730     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2731     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2732   } else {
2733     display_par_n = 1;
2734     display_par_d = 1;
2735   }
2736
2737   /* get the display region */
2738   caps_disp_reg = gst_structure_get_value (structure, "display-region");
2739   if (caps_disp_reg) {
2740     disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2741     disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2742     disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2743     disp_height =
2744         g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2745   } else {
2746     disp_x = disp_y = 0;
2747     disp_width = video_width;
2748     disp_height = video_height;
2749   }
2750
2751   if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2752           video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2753     goto no_disp_ratio;
2754
2755   xvimagesink->disp_x = disp_x;
2756   xvimagesink->disp_y = disp_y;
2757   xvimagesink->disp_width = disp_width;
2758   xvimagesink->disp_height = disp_height;
2759
2760   GST_DEBUG_OBJECT (xvimagesink,
2761       "video width/height: %dx%d, calculated display ratio: %d/%d",
2762       video_width, video_height, num, den);
2763
2764   /* now find a width x height that respects this display ratio.
2765    * prefer those that have one of w/h the same as the incoming video
2766    * using wd / hd = num / den */
2767
2768   /* start with same height, because of interlaced video */
2769   /* check hd / den is an integer scale factor, and scale wd with the PAR */
2770   if (video_height % den == 0) {
2771     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2772     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2773         gst_util_uint64_scale_int (video_height, num, den);
2774     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2775   } else if (video_width % num == 0) {
2776     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2777     GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2778     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2779         gst_util_uint64_scale_int (video_width, den, num);
2780   } else {
2781     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2782     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2783         gst_util_uint64_scale_int (video_height, num, den);
2784     GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2785   }
2786   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2787       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2788
2789   /* Notify application to set xwindow id now */
2790   g_mutex_lock (xvimagesink->flow_lock);
2791   if (!xvimagesink->xwindow) {
2792     g_mutex_unlock (xvimagesink->flow_lock);
2793     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2794   } else {
2795     g_mutex_unlock (xvimagesink->flow_lock);
2796   }
2797
2798   /* Creating our window and our image with the display size in pixels */
2799   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2800       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2801     goto no_display_size;
2802
2803   g_mutex_lock (xvimagesink->flow_lock);
2804   if (!xvimagesink->xwindow) {
2805     xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2806         GST_VIDEO_SINK_WIDTH (xvimagesink),
2807         GST_VIDEO_SINK_HEIGHT (xvimagesink));
2808   }
2809
2810   /* After a resize, we want to redraw the borders in case the new frame size 
2811    * doesn't cover the same area */
2812   xvimagesink->redraw_border = TRUE;
2813
2814   /* We renew our xvimage only if size or format changed;
2815    * the xvimage is the same size as the video pixel size */
2816   if ((xvimagesink->xvimage) &&
2817       ((im_format != xvimagesink->xvimage->im_format) ||
2818           (video_width != xvimagesink->xvimage->width) ||
2819           (video_height != xvimagesink->xvimage->height))) {
2820     GST_DEBUG_OBJECT (xvimagesink,
2821         "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2822         GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2823         GST_FOURCC_ARGS (im_format));
2824     GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2825     gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2826     xvimagesink->xvimage = NULL;
2827   }
2828
2829   g_mutex_unlock (xvimagesink->flow_lock);
2830
2831   return TRUE;
2832
2833   /* ERRORS */
2834 incompatible_caps:
2835   {
2836     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2837     return FALSE;
2838   }
2839 incomplete_caps:
2840   {
2841     GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2842         "height or framerate from intersected caps");
2843     return FALSE;
2844   }
2845 invalid_format:
2846   {
2847     GST_DEBUG_OBJECT (xvimagesink,
2848         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2849     return FALSE;
2850   }
2851 no_disp_ratio:
2852   {
2853     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2854         ("Error calculating the output display ratio of the video."));
2855     return FALSE;
2856   }
2857 no_display_size:
2858   {
2859     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2860         ("Error calculating the output display ratio of the video."));
2861     return FALSE;
2862   }
2863 }
2864
2865 static GstStateChangeReturn
2866 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2867 {
2868   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2869   GstXvImageSink *xvimagesink;
2870   GstXContext *xcontext = NULL;
2871 #ifdef GST_EXT_XV_ENHANCEMENT
2872   Atom atom_preemption = None;
2873 #endif
2874
2875   xvimagesink = GST_XVIMAGESINK (element);
2876
2877   switch (transition) {
2878     case GST_STATE_CHANGE_NULL_TO_READY:
2879       /* Initializing the XContext */
2880       if (xvimagesink->xcontext == NULL) {
2881         xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2882         if (xcontext == NULL)
2883           return GST_STATE_CHANGE_FAILURE;
2884         GST_OBJECT_LOCK (xvimagesink);
2885         if (xcontext)
2886           xvimagesink->xcontext = xcontext;
2887         GST_OBJECT_UNLOCK (xvimagesink);
2888       }
2889
2890       /* update object's par with calculated one if not set yet */
2891       if (!xvimagesink->par) {
2892         xvimagesink->par = g_new0 (GValue, 1);
2893         gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2894         GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2895       }
2896       /* call XSynchronize with the current value of synchronous */
2897       GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2898           xvimagesink->synchronous ? "TRUE" : "FALSE");
2899       XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2900       gst_xvimagesink_update_colorbalance (xvimagesink);
2901       gst_xvimagesink_manage_event_thread (xvimagesink);
2902       break;
2903     case GST_STATE_CHANGE_READY_TO_PAUSED:
2904       g_mutex_lock (xvimagesink->pool_lock);
2905       xvimagesink->pool_invalid = FALSE;
2906       g_mutex_unlock (xvimagesink->pool_lock);
2907       break;
2908     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2909 #ifdef GST_EXT_XV_ENHANCEMENT
2910       g_mutex_lock (xvimagesink->x_lock);
2911       atom_preemption = XInternAtom( xvimagesink->xcontext->disp,
2912                                      "_USER_WM_PORT_ATTRIBUTE_PREEMPTION", False );
2913       if(atom_preemption != None)
2914       {
2915          if( XvSetPortAttribute( xvimagesink->xcontext->disp,
2916                                  xvimagesink->xcontext->xv_port_id,
2917                                  atom_preemption, 1 ) != Success )
2918          {
2919             GST_ERROR_OBJECT(xvimagesink, "%d: XvSetPortAttribute: preemption failed.\n", atom_preemption);
2920          }
2921          XSync (xvimagesink->xcontext->disp, FALSE);
2922       }
2923       g_mutex_unlock (xvimagesink->x_lock);
2924 #endif
2925       break;
2926     case GST_STATE_CHANGE_PAUSED_TO_READY:
2927       g_mutex_lock (xvimagesink->pool_lock);
2928       xvimagesink->pool_invalid = TRUE;
2929       g_mutex_unlock (xvimagesink->pool_lock);
2930       break;
2931     default:
2932       break;
2933   }
2934
2935   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2936
2937   switch (transition) {
2938     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2939 #ifdef GST_EXT_XV_ENHANCEMENT
2940       xvimagesink->rotate_changed = TRUE;
2941       g_mutex_lock (xvimagesink->x_lock);
2942       atom_preemption = XInternAtom( xvimagesink->xcontext->disp,
2943                                      "_USER_WM_PORT_ATTRIBUTE_PREEMPTION", False );
2944       if(atom_preemption != None)
2945       {
2946          if( XvSetPortAttribute( xvimagesink->xcontext->disp,
2947                                  xvimagesink->xcontext->xv_port_id,
2948                                  atom_preemption, 0 ) != Success )
2949          {
2950             GST_ERROR_OBJECT(xvimagesink, "%d: XvSetPortAttribute: preemption failed.\n", atom_preemption);
2951          }
2952          XSync (xvimagesink->xcontext->disp, FALSE);
2953       }
2954       g_mutex_unlock (xvimagesink->x_lock);
2955 #endif
2956       break;
2957     case GST_STATE_CHANGE_PAUSED_TO_READY:
2958       xvimagesink->fps_n = 0;
2959       xvimagesink->fps_d = 1;
2960       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2961       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2962       break;
2963     case GST_STATE_CHANGE_READY_TO_NULL:
2964       gst_xvimagesink_reset (xvimagesink);
2965       break;
2966     default:
2967       break;
2968   }
2969
2970   return ret;
2971 }
2972
2973 static void
2974 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2975     GstClockTime * start, GstClockTime * end)
2976 {
2977   GstXvImageSink *xvimagesink;
2978
2979   xvimagesink = GST_XVIMAGESINK (bsink);
2980
2981   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2982     *start = GST_BUFFER_TIMESTAMP (buf);
2983     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2984       *end = *start + GST_BUFFER_DURATION (buf);
2985     } else {
2986       if (xvimagesink->fps_n > 0) {
2987         *end = *start +
2988             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2989             xvimagesink->fps_n);
2990       }
2991     }
2992   }
2993 }
2994
2995 static GstFlowReturn
2996 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2997 {
2998   GstXvImageSink *xvimagesink;
2999
3000 #ifdef GST_EXT_XV_ENHANCEMENT
3001   XV_PUTIMAGE_DATA_PTR img_data = NULL;
3002   SCMN_IMGB *scmn_imgb = NULL;
3003   GstMultiPlaneImageBuffer* mp_buf = NULL;
3004   int ret = 0;
3005   gint format = 0;
3006 #endif
3007
3008   xvimagesink = GST_XVIMAGESINK (vsink);
3009
3010 #ifdef GST_EXT_XV_ENHANCEMENT
3011   if( xvimagesink->stop_video )
3012   {
3013     GST_INFO( "Stop video is TRUE. so skip show frame..." );
3014     return GST_FLOW_OK;
3015   }
3016 #endif
3017
3018   /* If this buffer has been allocated using our buffer management we simply
3019      put the ximage which is in the PRIVATE pointer */
3020   if (GST_IS_XVIMAGE_BUFFER (buf)) {
3021     GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
3022     if (!gst_xvimagesink_xvimage_put (xvimagesink,
3023             GST_XVIMAGE_BUFFER_CAST (buf)))
3024       goto no_window;
3025   } else {
3026     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
3027         "slow copy into bufferpool buffer %p", buf);
3028     /* Else we have to copy the data into our private image, */
3029     /* if we have one... */
3030     if (!xvimagesink->xvimage) {
3031       GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
3032
3033 #ifdef GST_EXT_XV_ENHANCEMENT
3034       format = gst_xvimagesink_get_format_from_caps(xvimagesink, GST_BUFFER_CAPS(buf));
3035       switch (format) {
3036         case GST_MAKE_FOURCC('S', 'T', '1', '2'):
3037         case GST_MAKE_FOURCC('S', 'N', '1', '2'):
3038         case GST_MAKE_FOURCC('S', '4', '2', '0'):
3039         case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
3040         case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
3041         case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
3042           scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
3043           if(scmn_imgb == NULL) {
3044             GST_DEBUG_OBJECT( xvimagesink, "scmn_imgb is NULL. Skip xvimage put..." );
3045             return GST_FLOW_OK;
3046           }
3047           xvimagesink->aligned_width = scmn_imgb->s[0];
3048           xvimagesink->aligned_height = scmn_imgb->e[0];
3049           GST_INFO_OBJECT(xvimagesink, "Use aligned width,height[%dx%d]",
3050                                        xvimagesink->aligned_width, xvimagesink->aligned_height);
3051           break;
3052         default:
3053           GST_INFO_OBJECT(xvimagesink, "Use original width,height of caps");
3054           break;
3055       }
3056 #endif
3057
3058       xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
3059           GST_BUFFER_CAPS (buf));
3060
3061       if (!xvimagesink->xvimage)
3062         /* The create method should have posted an informative error */
3063         goto no_image;
3064
3065       if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
3066         GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
3067             ("Failed to create output image buffer of %dx%d pixels",
3068                 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
3069             ("XServer allocated buffer size did not match input buffer"));
3070
3071         gst_xvimage_buffer_destroy (xvimagesink->xvimage);
3072         xvimagesink->xvimage = NULL;
3073         goto no_image;
3074       }
3075     }
3076
3077 #ifdef GST_EXT_XV_ENHANCEMENT
3078
3079     switch (xvimagesink->xvimage->im_format) {
3080       /* Cases for specified formats of Samsung extension */
3081       case GST_MAKE_FOURCC('S', 'T', '1', '2'):
3082       case GST_MAKE_FOURCC('S', 'N', '1', '2'):
3083       case GST_MAKE_FOURCC('S', '4', '2', '0'):
3084       case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
3085       case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
3086       case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
3087       {
3088         GST_DEBUG("Samsung extension display format activated. fourcc:%d,display mode:%d,Rotate angle:%d",
3089                   xvimagesink->xvimage->im_format, xvimagesink->display_mode, xvimagesink->rotate_angle);
3090
3091         if (xvimagesink->xvimage->xvimage->data) {
3092           img_data = (XV_PUTIMAGE_DATA_PTR) xvimagesink->xvimage->xvimage->data;
3093           XV_PUTIMAGE_INIT_DATA(img_data);
3094
3095           scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
3096           if (scmn_imgb == NULL) {
3097             GST_DEBUG_OBJECT( xvimagesink, "scmn_imgb is NULL. Skip xvimage put..." );
3098             return GST_FLOW_OK;
3099           }
3100
3101           img_data->YPhyAddr  = (unsigned int)scmn_imgb->p[0];
3102           img_data->CbPhyAddr = (unsigned int)scmn_imgb->p[1];
3103           img_data->CrPhyAddr = (unsigned int)scmn_imgb->p[2];
3104
3105           GST_LOG_OBJECT(xvimagesink, "Ypaddr[%p],CbPaddr[%p],CrPaddr[%p]",
3106                                       img_data->YPhyAddr, img_data->CbPhyAddr, img_data->CrPhyAddr );
3107
3108           img_data->RotAngle = xvimagesink->rotate_angle;
3109           img_data->VideoMode = xvimagesink->display_mode;
3110         } else {
3111           GST_WARNING_OBJECT( xvimagesink, "xvimage->data is NULL. skip xvimage put..." );
3112           return GST_FLOW_OK;
3113         }
3114         break;
3115       }
3116       default:
3117       {
3118         GST_DEBUG("Normal format activated. fourcc = %d", xvimagesink->xvimage->im_format);
3119         memcpy (xvimagesink->xvimage->xvimage->data,
3120         GST_BUFFER_DATA (buf),
3121         MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
3122         break;
3123       }
3124     }
3125 #else
3126     memcpy (xvimagesink->xvimage->xvimage->data,
3127         GST_BUFFER_DATA (buf),
3128         MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
3129 #endif
3130     if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
3131       goto no_window;
3132   }
3133
3134   return GST_FLOW_OK;
3135
3136   /* ERRORS */
3137 no_image:
3138   {
3139     /* No image available. That's very bad ! */
3140     GST_WARNING_OBJECT (xvimagesink, "could not create image");
3141     return GST_FLOW_ERROR;
3142   }
3143 no_window:
3144   {
3145     /* No Window available to put our image into */
3146     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
3147     return GST_FLOW_ERROR;
3148   }
3149 }
3150
3151 static gboolean
3152 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
3153 {
3154   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
3155
3156   switch (GST_EVENT_TYPE (event)) {
3157     case GST_EVENT_TAG:{
3158       GstTagList *l;
3159       gchar *title = NULL;
3160
3161       gst_event_parse_tag (event, &l);
3162       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
3163
3164       if (title) {
3165         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
3166         gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
3167             title);
3168
3169         g_free (title);
3170       }
3171       break;
3172     }
3173     default:
3174       break;
3175   }
3176   if (GST_BASE_SINK_CLASS (parent_class)->event)
3177     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
3178   else
3179     return TRUE;
3180 }
3181
3182 /* Buffer management */
3183
3184 static GstCaps *
3185 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
3186     GstCaps * caps)
3187 {
3188   GstCaps *intersection;
3189   GstCaps *new_caps;
3190   GstStructure *s;
3191   gint width, height;
3192   gint par_n = 1, par_d = 1;
3193   gint dar_n, dar_d;
3194   gint w, h;
3195
3196   new_caps = gst_caps_copy (caps);
3197
3198   s = gst_caps_get_structure (new_caps, 0);
3199
3200   gst_structure_get_int (s, "width", &width);
3201   gst_structure_get_int (s, "height", &height);
3202   gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
3203
3204   gst_structure_remove_field (s, "width");
3205   gst_structure_remove_field (s, "height");
3206   gst_structure_remove_field (s, "pixel-aspect-ratio");
3207
3208   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
3209   gst_caps_unref (new_caps);
3210
3211   if (gst_caps_is_empty (intersection))
3212     return intersection;
3213
3214   s = gst_caps_get_structure (intersection, 0);
3215
3216   gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
3217
3218   /* xvimagesink supports all PARs */
3219
3220   gst_structure_fixate_field_nearest_int (s, "width", width);
3221   gst_structure_fixate_field_nearest_int (s, "height", height);
3222   gst_structure_get_int (s, "width", &w);
3223   gst_structure_get_int (s, "height", &h);
3224
3225   gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
3226   gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
3227       NULL);
3228
3229   return intersection;
3230 }
3231
3232 static GstFlowReturn
3233 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
3234     GstCaps * caps, GstBuffer ** buf)
3235 {
3236   GstFlowReturn ret = GST_FLOW_OK;
3237   GstXvImageSink *xvimagesink;
3238   GstXvImageBuffer *xvimage = NULL;
3239   GstCaps *intersection = NULL;
3240   GstStructure *structure = NULL;
3241   gint width, height, image_format;
3242
3243   xvimagesink = GST_XVIMAGESINK (bsink);
3244
3245   if (G_UNLIKELY (!caps))
3246     goto no_caps;
3247
3248   g_mutex_lock (xvimagesink->pool_lock);
3249   if (G_UNLIKELY (xvimagesink->pool_invalid))
3250     goto invalid;
3251
3252   if (G_LIKELY (xvimagesink->xcontext->last_caps &&
3253           gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
3254     GST_LOG_OBJECT (xvimagesink,
3255         "buffer alloc for same last_caps, reusing caps");
3256     intersection = gst_caps_ref (caps);
3257     image_format = xvimagesink->xcontext->last_format;
3258     width = xvimagesink->xcontext->last_width;
3259     height = xvimagesink->xcontext->last_height;
3260
3261     goto reuse_last_caps;
3262   }
3263
3264   GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
3265       GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
3266       caps, xvimagesink->xcontext->caps);
3267
3268   /* Check the caps against our xcontext */
3269   intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
3270
3271   GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
3272       GST_PTR_FORMAT, intersection);
3273
3274   if (gst_caps_is_empty (intersection)) {
3275     GstCaps *new_caps;
3276
3277     gst_caps_unref (intersection);
3278
3279     /* So we don't support this kind of buffer, let's define one we'd like */
3280     new_caps = gst_caps_copy (caps);
3281
3282     structure = gst_caps_get_structure (new_caps, 0);
3283     if (!gst_structure_has_field (structure, "width") ||
3284         !gst_structure_has_field (structure, "height")) {
3285       gst_caps_unref (new_caps);
3286       goto invalid;
3287     }
3288
3289     /* Try different dimensions */
3290     intersection =
3291         gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
3292
3293     if (gst_caps_is_empty (intersection)) {
3294       /* Try with different YUV formats first */
3295       gst_structure_set_name (structure, "video/x-raw-yuv");
3296
3297       /* Remove format specific fields */
3298       gst_structure_remove_field (structure, "format");
3299       gst_structure_remove_field (structure, "endianness");
3300       gst_structure_remove_field (structure, "depth");
3301       gst_structure_remove_field (structure, "bpp");
3302       gst_structure_remove_field (structure, "red_mask");
3303       gst_structure_remove_field (structure, "green_mask");
3304       gst_structure_remove_field (structure, "blue_mask");
3305       gst_structure_remove_field (structure, "alpha_mask");
3306
3307       /* Reuse intersection with Xcontext */
3308       intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
3309     }
3310
3311     if (gst_caps_is_empty (intersection)) {
3312       /* Try with different dimensions and YUV formats */
3313       intersection =
3314           gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
3315     }
3316
3317     if (gst_caps_is_empty (intersection)) {
3318       /* Now try with RGB */
3319       gst_structure_set_name (structure, "video/x-raw-rgb");
3320       /* And interset again */
3321       gst_caps_unref (intersection);
3322       intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
3323     }
3324
3325     if (gst_caps_is_empty (intersection)) {
3326       /* Try with different dimensions and RGB formats */
3327       intersection =
3328           gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
3329     }
3330
3331     /* Clean this copy */
3332     gst_caps_unref (new_caps);
3333
3334     if (gst_caps_is_empty (intersection))
3335       goto incompatible;
3336   }
3337
3338   /* Ensure the returned caps are fixed */
3339   gst_caps_truncate (intersection);
3340
3341   GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
3342       GST_PTR_FORMAT, intersection);
3343   if (gst_caps_is_equal (intersection, caps)) {
3344     /* Things work better if we return a buffer with the same caps ptr
3345      * as was asked for when we can */
3346     gst_caps_replace (&intersection, caps);
3347   }
3348
3349   /* Get image format from caps */
3350   image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
3351       intersection);
3352
3353   /* Get geometry from caps */
3354   structure = gst_caps_get_structure (intersection, 0);
3355   if (!gst_structure_get_int (structure, "width", &width) ||
3356       !gst_structure_get_int (structure, "height", &height) ||
3357       image_format == -1)
3358     goto invalid_caps;
3359
3360   /* Store our caps and format as the last_caps to avoid expensive
3361    * caps intersection next time */
3362   gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
3363   xvimagesink->xcontext->last_format = image_format;
3364   xvimagesink->xcontext->last_width = width;
3365   xvimagesink->xcontext->last_height = height;
3366
3367 reuse_last_caps:
3368
3369   /* Walking through the pool cleaning unusable images and searching for a
3370      suitable one */
3371   while (xvimagesink->image_pool) {
3372     xvimage = xvimagesink->image_pool->data;
3373     if (xvimage) {
3374       /* Removing from the pool */
3375       xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
3376           xvimagesink->image_pool);
3377
3378       /* We check for geometry or image format changes */
3379       if ((xvimage->width != width) ||
3380           (xvimage->height != height) || (xvimage->im_format != image_format)) {
3381         /* This image is unusable. Destroying... */
3382         gst_xvimage_buffer_free (xvimage);
3383         xvimage = NULL;
3384       } else {
3385         /* We found a suitable image */
3386         GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
3387         break;
3388       }
3389     }
3390   }
3391
3392   if (!xvimage) {
3393     /* We found no suitable image in the pool. Creating... */
3394     GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
3395     xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
3396   }
3397   g_mutex_unlock (xvimagesink->pool_lock);
3398
3399   if (xvimage) {
3400     /* Make sure the buffer is cleared of any previously used flags */
3401     GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
3402     gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
3403   }
3404
3405   *buf = GST_BUFFER_CAST (xvimage);
3406
3407 beach:
3408   if (intersection) {
3409     gst_caps_unref (intersection);
3410   }
3411
3412   return ret;
3413
3414   /* ERRORS */
3415 invalid:
3416   {
3417     GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
3418     ret = GST_FLOW_WRONG_STATE;
3419     g_mutex_unlock (xvimagesink->pool_lock);
3420     goto beach;
3421   }
3422 incompatible:
3423   {
3424     GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
3425         "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
3426         " are completely incompatible with those caps", caps,
3427         xvimagesink->xcontext->caps);
3428     ret = GST_FLOW_NOT_NEGOTIATED;
3429     g_mutex_unlock (xvimagesink->pool_lock);
3430     goto beach;
3431   }
3432 invalid_caps:
3433   {
3434     GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
3435         GST_PTR_FORMAT, intersection);
3436     ret = GST_FLOW_NOT_NEGOTIATED;
3437     g_mutex_unlock (xvimagesink->pool_lock);
3438     goto beach;
3439   }
3440 no_caps:
3441   {
3442     GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
3443     *buf = NULL;
3444     ret = GST_FLOW_OK;
3445     goto beach;
3446   }
3447 }
3448
3449 /* Interfaces stuff */
3450
3451 static gboolean
3452 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
3453 {
3454   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
3455       type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
3456   return TRUE;
3457 }
3458
3459 static void
3460 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
3461 {
3462   klass->supported = gst_xvimagesink_interface_supported;
3463 }
3464
3465 static void
3466 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
3467     GstStructure * structure)
3468 {
3469   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
3470   GstPad *peer;
3471
3472   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
3473     GstEvent *event;
3474     GstVideoRectangle src, dst, result;
3475     gdouble x, y, xscale = 1.0, yscale = 1.0;
3476
3477     event = gst_event_new_navigation (structure);
3478
3479     /* We take the flow_lock while we look at the window */
3480     g_mutex_lock (xvimagesink->flow_lock);
3481
3482     if (!xvimagesink->xwindow) {
3483       g_mutex_unlock (xvimagesink->flow_lock);
3484       return;
3485     }
3486
3487     if (xvimagesink->keep_aspect) {
3488       /* We get the frame position using the calculated geometry from _setcaps
3489          that respect pixel aspect ratios */
3490       src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
3491       src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
3492       dst.w = xvimagesink->render_rect.w;
3493       dst.h = xvimagesink->render_rect.h;
3494
3495       gst_video_sink_center_rect (src, dst, &result, TRUE);
3496       result.x += xvimagesink->render_rect.x;
3497       result.y += xvimagesink->render_rect.y;
3498     } else {
3499       memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
3500     }
3501
3502     g_mutex_unlock (xvimagesink->flow_lock);
3503
3504     /* We calculate scaling using the original video frames geometry to include
3505        pixel aspect ratio scaling. */
3506     xscale = (gdouble) xvimagesink->video_width / result.w;
3507     yscale = (gdouble) xvimagesink->video_height / result.h;
3508
3509     /* Converting pointer coordinates to the non scaled geometry */
3510     if (gst_structure_get_double (structure, "pointer_x", &x)) {
3511       x = MIN (x, result.x + result.w);
3512       x = MAX (x - result.x, 0);
3513       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
3514           (gdouble) x * xscale, NULL);
3515     }
3516     if (gst_structure_get_double (structure, "pointer_y", &y)) {
3517       y = MIN (y, result.y + result.h);
3518       y = MAX (y - result.y, 0);
3519       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
3520           (gdouble) y * yscale, NULL);
3521     }
3522
3523     gst_pad_send_event (peer, event);
3524     gst_object_unref (peer);
3525   }
3526 }
3527
3528 static void
3529 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
3530 {
3531   iface->send_event = gst_xvimagesink_navigation_send_event;
3532 }
3533
3534 static void
3535 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
3536 {
3537   XID xwindow_id = id;
3538   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
3539   GstXWindow *xwindow = NULL;
3540
3541   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3542
3543   g_mutex_lock (xvimagesink->flow_lock);
3544
3545 #ifdef GST_EXT_XV_ENHANCEMENT
3546   GST_INFO_OBJECT( xvimagesink, "ENTER, id : %d", xwindow_id );
3547 #endif
3548
3549   /* If we already use that window return */
3550   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
3551     g_mutex_unlock (xvimagesink->flow_lock);
3552     return;
3553   }
3554
3555   /* If the element has not initialized the X11 context try to do so */
3556   if (!xvimagesink->xcontext &&
3557       !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
3558     g_mutex_unlock (xvimagesink->flow_lock);
3559     /* we have thrown a GST_ELEMENT_ERROR now */
3560     return;
3561   }
3562
3563   gst_xvimagesink_update_colorbalance (xvimagesink);
3564
3565   /* Clear image pool as the images are unusable anyway */
3566   gst_xvimagesink_imagepool_clear (xvimagesink);
3567
3568   /* Clear the xvimage */
3569   if (xvimagesink->xvimage) {
3570     gst_xvimage_buffer_free (xvimagesink->xvimage);
3571     xvimagesink->xvimage = NULL;
3572   }
3573
3574   /* If a window is there already we destroy it */
3575   if (xvimagesink->xwindow) {
3576     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3577     xvimagesink->xwindow = NULL;
3578   }
3579
3580   /* If the xid is 0 we go back to an internal window */
3581   if (xwindow_id == 0) {
3582     /* If no width/height caps nego did not happen window will be created
3583        during caps nego then */
3584 #ifdef GST_EXT_XV_ENHANCEMENT
3585   GST_INFO_OBJECT( xvimagesink, "xid is 0. create window[%dx%d]",
3586     GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink) );
3587 #endif
3588     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
3589         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
3590       xwindow =
3591           gst_xvimagesink_xwindow_new (xvimagesink,
3592           GST_VIDEO_SINK_WIDTH (xvimagesink),
3593           GST_VIDEO_SINK_HEIGHT (xvimagesink));
3594     }
3595   } else {
3596     XWindowAttributes attr;
3597
3598     xwindow = g_new0 (GstXWindow, 1);
3599     xwindow->win = xwindow_id;
3600
3601     /* Set the event we want to receive and create a GC */
3602     g_mutex_lock (xvimagesink->x_lock);
3603
3604     XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
3605
3606     xwindow->width = attr.width;
3607     xwindow->height = attr.height;
3608     xwindow->internal = FALSE;
3609     if (!xvimagesink->have_render_rect) {
3610       xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
3611       xvimagesink->render_rect.w = attr.width;
3612       xvimagesink->render_rect.h = attr.height;
3613     }
3614     if (xvimagesink->handle_events) {
3615       XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
3616           StructureNotifyMask | PointerMotionMask | KeyPressMask |
3617           KeyReleaseMask);
3618     }
3619
3620     xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
3621         xwindow->win, 0, NULL);
3622     g_mutex_unlock (xvimagesink->x_lock);
3623   }
3624
3625   if (xwindow)
3626     xvimagesink->xwindow = xwindow;
3627
3628   g_mutex_unlock (xvimagesink->flow_lock);
3629 }
3630
3631 static void
3632 gst_xvimagesink_expose (GstXOverlay * overlay)
3633 {
3634   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
3635
3636   gst_xvimagesink_xwindow_update_geometry (xvimagesink);
3637 #ifdef GST_EXT_XV_ENHANCEMENT
3638   GST_INFO_OBJECT( xvimagesink, "Overlay window exposed. update it");
3639   gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
3640 #else
3641   gst_xvimagesink_xvimage_put (xvimagesink, NULL);
3642 #endif
3643 }
3644
3645 static void
3646 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
3647     gboolean handle_events)
3648 {
3649   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
3650
3651   xvimagesink->handle_events = handle_events;
3652
3653   g_mutex_lock (xvimagesink->flow_lock);
3654
3655   if (G_UNLIKELY (!xvimagesink->xwindow)) {
3656     g_mutex_unlock (xvimagesink->flow_lock);
3657     return;
3658   }
3659
3660   g_mutex_lock (xvimagesink->x_lock);
3661
3662   if (handle_events) {
3663     if (xvimagesink->xwindow->internal) {
3664       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
3665           ExposureMask | StructureNotifyMask | PointerMotionMask |
3666           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
3667     } else {
3668       XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
3669           ExposureMask | StructureNotifyMask | PointerMotionMask |
3670           KeyPressMask | KeyReleaseMask);
3671     }
3672   } else {
3673     XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
3674   }
3675
3676   g_mutex_unlock (xvimagesink->x_lock);
3677
3678   g_mutex_unlock (xvimagesink->flow_lock);
3679 }
3680
3681 static void
3682 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
3683     gint width, gint height)
3684 {
3685   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
3686
3687   /* FIXME: how about some locking? */
3688   if (width >= 0 && height >= 0) {
3689     xvimagesink->render_rect.x = x;
3690     xvimagesink->render_rect.y = y;
3691     xvimagesink->render_rect.w = width;
3692     xvimagesink->render_rect.h = height;
3693     xvimagesink->have_render_rect = TRUE;
3694   } else {
3695     xvimagesink->render_rect.x = 0;
3696     xvimagesink->render_rect.y = 0;
3697     xvimagesink->render_rect.w = xvimagesink->xwindow->width;
3698     xvimagesink->render_rect.h = xvimagesink->xwindow->height;
3699     xvimagesink->have_render_rect = FALSE;
3700   }
3701 }
3702
3703 static void
3704 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
3705 {
3706   iface->set_window_handle = gst_xvimagesink_set_window_handle;
3707   iface->expose = gst_xvimagesink_expose;
3708   iface->handle_events = gst_xvimagesink_set_event_handling;
3709   iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
3710 }
3711
3712 static const GList *
3713 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
3714 {
3715   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3716
3717   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
3718
3719   if (xvimagesink->xcontext)
3720     return xvimagesink->xcontext->channels_list;
3721   else
3722     return NULL;
3723 }
3724
3725 static void
3726 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
3727     GstColorBalanceChannel * channel, gint value)
3728 {
3729   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3730
3731   g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3732   g_return_if_fail (channel->label != NULL);
3733
3734   xvimagesink->cb_changed = TRUE;
3735
3736   /* Normalize val to [-1000, 1000] */
3737   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
3738       (double) (channel->max_value - channel->min_value));
3739
3740   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3741     xvimagesink->hue = value;
3742   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3743     xvimagesink->saturation = value;
3744   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3745     xvimagesink->contrast = value;
3746   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3747     xvimagesink->brightness = value;
3748   } else {
3749     g_warning ("got an unknown channel %s", channel->label);
3750     return;
3751   }
3752
3753   gst_xvimagesink_update_colorbalance (xvimagesink);
3754 }
3755
3756 static gint
3757 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3758     GstColorBalanceChannel * channel)
3759 {
3760   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3761   gint value = 0;
3762
3763   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3764   g_return_val_if_fail (channel->label != NULL, 0);
3765
3766   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3767     value = xvimagesink->hue;
3768   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3769     value = xvimagesink->saturation;
3770   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3771     value = xvimagesink->contrast;
3772   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3773     value = xvimagesink->brightness;
3774   } else {
3775     g_warning ("got an unknown channel %s", channel->label);
3776   }
3777
3778   /* Normalize val to [channel->min_value, channel->max_value] */
3779   value = channel->min_value + (channel->max_value - channel->min_value) *
3780       (value + 1000) / 2000;
3781
3782   return value;
3783 }
3784
3785 static void
3786 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3787 {
3788   GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3789   iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3790   iface->set_value = gst_xvimagesink_colorbalance_set_value;
3791   iface->get_value = gst_xvimagesink_colorbalance_get_value;
3792 }
3793
3794 static const GList *
3795 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3796 {
3797   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3798   static GList *list = NULL;
3799
3800   if (!list) {
3801     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3802     list =
3803         g_list_append (list, g_object_class_find_property (klass,
3804             "autopaint-colorkey"));
3805     list =
3806         g_list_append (list, g_object_class_find_property (klass,
3807             "double-buffer"));
3808     list =
3809         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3810   }
3811
3812   return list;
3813 }
3814
3815 static void
3816 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3817     guint prop_id, const GParamSpec * pspec)
3818 {
3819   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3820
3821   switch (prop_id) {
3822     case PROP_DEVICE:
3823     case PROP_AUTOPAINT_COLORKEY:
3824     case PROP_DOUBLE_BUFFER:
3825     case PROP_COLORKEY:
3826       GST_DEBUG_OBJECT (xvimagesink,
3827           "probing device list and get capabilities");
3828       if (!xvimagesink->xcontext) {
3829         GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3830         xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3831       }
3832       break;
3833     default:
3834       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3835       break;
3836   }
3837 }
3838
3839 static gboolean
3840 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3841     guint prop_id, const GParamSpec * pspec)
3842 {
3843   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3844   gboolean ret = FALSE;
3845
3846   switch (prop_id) {
3847     case PROP_DEVICE:
3848     case PROP_AUTOPAINT_COLORKEY:
3849     case PROP_DOUBLE_BUFFER:
3850     case PROP_COLORKEY:
3851       if (xvimagesink->xcontext != NULL) {
3852         ret = FALSE;
3853       } else {
3854         ret = TRUE;
3855       }
3856       break;
3857     default:
3858       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3859       break;
3860   }
3861
3862   return ret;
3863 }
3864
3865 static GValueArray *
3866 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3867     guint prop_id, const GParamSpec * pspec)
3868 {
3869   GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3870   GValueArray *array = NULL;
3871
3872   if (G_UNLIKELY (!xvimagesink->xcontext)) {
3873     GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3874         "get values");
3875     goto beach;
3876   }
3877
3878   switch (prop_id) {
3879     case PROP_DEVICE:
3880     {
3881       guint i;
3882       GValue value = { 0 };
3883
3884       array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3885       g_value_init (&value, G_TYPE_STRING);
3886
3887       for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3888         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3889
3890         g_value_set_string (&value, adaptor_id_s);
3891         g_value_array_append (array, &value);
3892         g_free (adaptor_id_s);
3893       }
3894       g_value_unset (&value);
3895       break;
3896     }
3897     case PROP_AUTOPAINT_COLORKEY:
3898       if (xvimagesink->have_autopaint_colorkey) {
3899         GValue value = { 0 };
3900
3901         array = g_value_array_new (2);
3902         g_value_init (&value, G_TYPE_BOOLEAN);
3903         g_value_set_boolean (&value, FALSE);
3904         g_value_array_append (array, &value);
3905         g_value_set_boolean (&value, TRUE);
3906         g_value_array_append (array, &value);
3907         g_value_unset (&value);
3908       }
3909       break;
3910     case PROP_DOUBLE_BUFFER:
3911       if (xvimagesink->have_double_buffer) {
3912         GValue value = { 0 };
3913
3914         array = g_value_array_new (2);
3915         g_value_init (&value, G_TYPE_BOOLEAN);
3916         g_value_set_boolean (&value, FALSE);
3917         g_value_array_append (array, &value);
3918         g_value_set_boolean (&value, TRUE);
3919         g_value_array_append (array, &value);
3920         g_value_unset (&value);
3921       }
3922       break;
3923     case PROP_COLORKEY:
3924       if (xvimagesink->have_colorkey) {
3925         GValue value = { 0 };
3926
3927         array = g_value_array_new (1);
3928         g_value_init (&value, GST_TYPE_INT_RANGE);
3929         gst_value_set_int_range (&value, 0, 0xffffff);
3930         g_value_array_append (array, &value);
3931         g_value_unset (&value);
3932       }
3933       break;
3934     default:
3935       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3936       break;
3937   }
3938
3939 beach:
3940   return array;
3941 }
3942
3943 static void
3944 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3945     iface)
3946 {
3947   iface->get_properties = gst_xvimagesink_probe_get_properties;
3948   iface->probe_property = gst_xvimagesink_probe_probe_property;
3949   iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3950   iface->get_values = gst_xvimagesink_probe_get_values;
3951 }
3952
3953 /* =========================================== */
3954 /*                                             */
3955 /*              Init & Class init              */
3956 /*                                             */
3957 /* =========================================== */
3958
3959 static void
3960 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3961     const GValue * value, GParamSpec * pspec)
3962 {
3963   GstXvImageSink *xvimagesink;
3964
3965   g_return_if_fail (GST_IS_XVIMAGESINK (object));
3966
3967   xvimagesink = GST_XVIMAGESINK (object);
3968
3969   switch (prop_id) {
3970     case PROP_HUE:
3971       xvimagesink->hue = g_value_get_int (value);
3972       xvimagesink->cb_changed = TRUE;
3973       gst_xvimagesink_update_colorbalance (xvimagesink);
3974       break;
3975     case PROP_CONTRAST:
3976       xvimagesink->contrast = g_value_get_int (value);
3977       xvimagesink->cb_changed = TRUE;
3978       gst_xvimagesink_update_colorbalance (xvimagesink);
3979       break;
3980     case PROP_BRIGHTNESS:
3981       xvimagesink->brightness = g_value_get_int (value);
3982       xvimagesink->cb_changed = TRUE;
3983       gst_xvimagesink_update_colorbalance (xvimagesink);
3984       break;
3985     case PROP_SATURATION:
3986       xvimagesink->saturation = g_value_get_int (value);
3987       xvimagesink->cb_changed = TRUE;
3988       gst_xvimagesink_update_colorbalance (xvimagesink);
3989       break;
3990     case PROP_DISPLAY:
3991       xvimagesink->display_name = g_strdup (g_value_get_string (value));
3992       break;
3993     case PROP_SYNCHRONOUS:
3994       xvimagesink->synchronous = g_value_get_boolean (value);
3995       if (xvimagesink->xcontext) {
3996         XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3997         GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3998             xvimagesink->synchronous ? "TRUE" : "FALSE");
3999       }
4000       break;
4001     case PROP_PIXEL_ASPECT_RATIO:
4002       g_free (xvimagesink->par);
4003       xvimagesink->par = g_new0 (GValue, 1);
4004       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
4005       if (!g_value_transform (value, xvimagesink->par)) {
4006         g_warning ("Could not transform string to aspect ratio");
4007         gst_value_set_fraction (xvimagesink->par, 1, 1);
4008       }
4009       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
4010           gst_value_get_fraction_numerator (xvimagesink->par),
4011           gst_value_get_fraction_denominator (xvimagesink->par));
4012       break;
4013     case PROP_FORCE_ASPECT_RATIO:
4014       xvimagesink->keep_aspect = g_value_get_boolean (value);
4015       break;
4016     case PROP_HANDLE_EVENTS:
4017       gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
4018           g_value_get_boolean (value));
4019       gst_xvimagesink_manage_event_thread (xvimagesink);
4020       break;
4021     case PROP_DEVICE:
4022       xvimagesink->adaptor_no = atoi (g_value_get_string (value));
4023       break;
4024     case PROP_HANDLE_EXPOSE:
4025       xvimagesink->handle_expose = g_value_get_boolean (value);
4026       gst_xvimagesink_manage_event_thread (xvimagesink);
4027       break;
4028     case PROP_DOUBLE_BUFFER:
4029       xvimagesink->double_buffer = g_value_get_boolean (value);
4030       break;
4031     case PROP_AUTOPAINT_COLORKEY:
4032       xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
4033       break;
4034     case PROP_COLORKEY:
4035       xvimagesink->colorkey = g_value_get_int (value);
4036       break;
4037     case PROP_DRAW_BORDERS:
4038       xvimagesink->draw_borders = g_value_get_boolean (value);
4039       break;
4040 #ifdef GST_EXT_XV_ENHANCEMENT
4041     case PROP_DISPLAY_MODE:
4042       xvimagesink->display_mode = g_value_get_enum (value);
4043       break;
4044     case PROP_DISPLAY_GEOMETRY_METHOD:
4045       xvimagesink->display_geometry_method = g_value_get_enum (value);
4046       GST_LOG("Overlay geometry changed. update it");
4047       gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
4048       break;
4049     case PROP_ROTATE_ANGLE:
4050       xvimagesink->rotate_angle = g_value_get_enum (value);
4051       xvimagesink->rotate_changed = TRUE;
4052       break;
4053     case PROP_VISIBLE:
4054       g_mutex_lock( xvimagesink->flow_lock );
4055       g_mutex_lock( xvimagesink->x_lock );
4056
4057       if( xvimagesink->visible && ( g_value_get_boolean( value ) == FALSE ) )
4058       {
4059         Atom atom_stream = XInternAtom( xvimagesink->xcontext->disp,
4060                                         "_USER_WM_PORT_ATTRIBUTE_STREAM_OFF", False );
4061         if( atom_stream != None )
4062         {
4063           if( XvSetPortAttribute( xvimagesink->xcontext->disp,
4064                                   xvimagesink->xcontext->xv_port_id,
4065                                   atom_stream, 0 ) != Success )
4066           {
4067             GST_WARNING_OBJECT( xvimagesink, "Set visible FALSE failed" );
4068           }
4069
4070           XSync( xvimagesink->xcontext->disp, FALSE );
4071         }
4072       }
4073       else if( !xvimagesink->visible && ( g_value_get_boolean( value ) == TRUE ) )
4074       {
4075         g_mutex_unlock( xvimagesink->x_lock );
4076         g_mutex_unlock( xvimagesink->flow_lock );
4077         GST_INFO_OBJECT( xvimagesink, "Set visible as TRUE. Update it." );
4078         gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
4079         g_mutex_lock( xvimagesink->flow_lock );
4080         g_mutex_lock( xvimagesink->x_lock );
4081       }
4082
4083       xvimagesink->visible = g_value_get_boolean (value);
4084
4085       g_mutex_unlock( xvimagesink->x_lock );
4086       g_mutex_unlock( xvimagesink->flow_lock );
4087       break;
4088     case PROP_ZOOM:
4089       xvimagesink->zoom = g_value_get_int (value);
4090       break;
4091     case PROP_DST_ROI_X:
4092       xvimagesink->dst_roi.x = g_value_get_int (value);
4093       break;
4094     case PROP_DST_ROI_Y:
4095       xvimagesink->dst_roi.y = g_value_get_int (value);
4096       break;
4097     case PROP_DST_ROI_W:
4098       xvimagesink->dst_roi.w = g_value_get_int (value);
4099       break;
4100     case PROP_DST_ROI_H:
4101       xvimagesink->dst_roi.h = g_value_get_int (value);
4102       break;
4103     case PROP_STOP_VIDEO:
4104       xvimagesink->stop_video = g_value_get_int (value);
4105       g_mutex_lock( xvimagesink->flow_lock );
4106
4107       if( xvimagesink->stop_video )
4108       {
4109         GST_INFO_OBJECT( xvimagesink, "Xwindow CLEAR when set video-stop property" );
4110         gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
4111       }
4112
4113       g_mutex_unlock( xvimagesink->flow_lock );
4114       break;
4115 #endif
4116     default:
4117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4118       break;
4119   }
4120 }
4121
4122 static void
4123 gst_xvimagesink_get_property (GObject * object, guint prop_id,
4124     GValue * value, GParamSpec * pspec)
4125 {
4126   GstXvImageSink *xvimagesink;
4127
4128   g_return_if_fail (GST_IS_XVIMAGESINK (object));
4129
4130   xvimagesink = GST_XVIMAGESINK (object);
4131
4132   switch (prop_id) {
4133     case PROP_HUE:
4134       g_value_set_int (value, xvimagesink->hue);
4135       break;
4136     case PROP_CONTRAST:
4137       g_value_set_int (value, xvimagesink->contrast);
4138       break;
4139     case PROP_BRIGHTNESS:
4140       g_value_set_int (value, xvimagesink->brightness);
4141       break;
4142     case PROP_SATURATION:
4143       g_value_set_int (value, xvimagesink->saturation);
4144       break;
4145     case PROP_DISPLAY:
4146       g_value_set_string (value, xvimagesink->display_name);
4147       break;
4148     case PROP_SYNCHRONOUS:
4149       g_value_set_boolean (value, xvimagesink->synchronous);
4150       break;
4151     case PROP_PIXEL_ASPECT_RATIO:
4152       if (xvimagesink->par)
4153         g_value_transform (xvimagesink->par, value);
4154       break;
4155     case PROP_FORCE_ASPECT_RATIO:
4156       g_value_set_boolean (value, xvimagesink->keep_aspect);
4157       break;
4158     case PROP_HANDLE_EVENTS:
4159       g_value_set_boolean (value, xvimagesink->handle_events);
4160       break;
4161     case PROP_DEVICE:
4162     {
4163       char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
4164
4165       g_value_set_string (value, adaptor_no_s);
4166       g_free (adaptor_no_s);
4167       break;
4168     }
4169     case PROP_DEVICE_NAME:
4170       if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
4171         g_value_set_string (value,
4172             xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
4173       } else {
4174         g_value_set_string (value, NULL);
4175       }
4176       break;
4177     case PROP_HANDLE_EXPOSE:
4178       g_value_set_boolean (value, xvimagesink->handle_expose);
4179       break;
4180     case PROP_DOUBLE_BUFFER:
4181       g_value_set_boolean (value, xvimagesink->double_buffer);
4182       break;
4183     case PROP_AUTOPAINT_COLORKEY:
4184       g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
4185       break;
4186     case PROP_COLORKEY:
4187       g_value_set_int (value, xvimagesink->colorkey);
4188       break;
4189     case PROP_DRAW_BORDERS:
4190       g_value_set_boolean (value, xvimagesink->draw_borders);
4191       break;
4192     case PROP_WINDOW_WIDTH:
4193       if (xvimagesink->xwindow)
4194         g_value_set_uint64 (value, xvimagesink->xwindow->width);
4195       else
4196         g_value_set_uint64 (value, 0);
4197       break;
4198     case PROP_WINDOW_HEIGHT:
4199       if (xvimagesink->xwindow)
4200         g_value_set_uint64 (value, xvimagesink->xwindow->height);
4201       else
4202         g_value_set_uint64 (value, 0);
4203       break;
4204 #ifdef GST_EXT_XV_ENHANCEMENT
4205     case PROP_DISPLAY_MODE:
4206       g_value_set_enum (value, xvimagesink->display_mode);
4207       break;
4208     case PROP_DISPLAY_GEOMETRY_METHOD:
4209       g_value_set_enum (value, xvimagesink->display_geometry_method);
4210       break;
4211     case PROP_ROTATE_ANGLE:
4212       g_value_set_enum (value, xvimagesink->rotate_angle);
4213       break;
4214     case PROP_VISIBLE:
4215       g_value_set_boolean (value, xvimagesink->visible);
4216       break;
4217     case PROP_ZOOM:
4218       g_value_set_int (value, xvimagesink->zoom);
4219       break;
4220     case PROP_DST_ROI_X:
4221       g_value_set_int (value, xvimagesink->dst_roi.x);
4222       break;
4223     case PROP_DST_ROI_Y:
4224       g_value_set_int (value, xvimagesink->dst_roi.y);
4225       break;
4226     case PROP_DST_ROI_W:
4227       g_value_set_int (value, xvimagesink->dst_roi.w);
4228       break;
4229     case PROP_DST_ROI_H:
4230       g_value_set_int (value, xvimagesink->dst_roi.h);
4231       break;
4232     case PROP_STOP_VIDEO:
4233       g_value_set_int (value, xvimagesink->stop_video);
4234       break;
4235 #endif
4236     default:
4237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4238       break;
4239   }
4240 }
4241
4242 static void
4243 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
4244 {
4245   GThread *thread;
4246
4247   GST_OBJECT_LOCK (xvimagesink);
4248   xvimagesink->running = FALSE;
4249   /* grab thread and mark it as NULL */
4250   thread = xvimagesink->event_thread;
4251   xvimagesink->event_thread = NULL;
4252   GST_OBJECT_UNLOCK (xvimagesink);
4253
4254   /* invalidate the pool, current allocations continue, new buffer_alloc fails
4255    * with wrong_state */
4256   g_mutex_lock (xvimagesink->pool_lock);
4257   xvimagesink->pool_invalid = TRUE;
4258   g_mutex_unlock (xvimagesink->pool_lock);
4259
4260   /* Wait for our event thread to finish before we clean up our stuff. */
4261   if (thread)
4262     g_thread_join (thread);
4263
4264   if (xvimagesink->cur_image) {
4265     gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
4266     xvimagesink->cur_image = NULL;
4267   }
4268   if (xvimagesink->xvimage) {
4269     gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
4270     xvimagesink->xvimage = NULL;
4271   }
4272
4273   gst_xvimagesink_imagepool_clear (xvimagesink);
4274
4275   if (xvimagesink->xwindow) {
4276     gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
4277     gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
4278     xvimagesink->xwindow = NULL;
4279   }
4280
4281   xvimagesink->render_rect.x = xvimagesink->render_rect.y =
4282       xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
4283   xvimagesink->have_render_rect = FALSE;
4284
4285   gst_xvimagesink_xcontext_clear (xvimagesink);
4286 }
4287
4288 /* Finalize is called only once, dispose can be called multiple times.
4289  * We use mutexes and don't reset stuff to NULL here so let's register
4290  * as a finalize. */
4291 static void
4292 gst_xvimagesink_finalize (GObject * object)
4293 {
4294   GstXvImageSink *xvimagesink;
4295
4296   xvimagesink = GST_XVIMAGESINK (object);
4297
4298   gst_xvimagesink_reset (xvimagesink);
4299
4300   if (xvimagesink->display_name) {
4301     g_free (xvimagesink->display_name);
4302     xvimagesink->display_name = NULL;
4303   }
4304
4305   if (xvimagesink->par) {
4306     g_free (xvimagesink->par);
4307     xvimagesink->par = NULL;
4308   }
4309   if (xvimagesink->x_lock) {
4310     g_mutex_free (xvimagesink->x_lock);
4311     xvimagesink->x_lock = NULL;
4312   }
4313   if (xvimagesink->flow_lock) {
4314     g_mutex_free (xvimagesink->flow_lock);
4315     xvimagesink->flow_lock = NULL;
4316   }
4317   if (xvimagesink->pool_lock) {
4318     g_mutex_free (xvimagesink->pool_lock);
4319     xvimagesink->pool_lock = NULL;
4320   }
4321
4322   g_free (xvimagesink->media_title);
4323
4324   G_OBJECT_CLASS (parent_class)->finalize (object);
4325 }
4326
4327
4328 static void
4329 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
4330 {
4331   xvimagesink->display_name = NULL;
4332   xvimagesink->adaptor_no = 0;
4333   xvimagesink->xcontext = NULL;
4334   xvimagesink->xwindow = NULL;
4335   xvimagesink->xvimage = NULL;
4336   xvimagesink->cur_image = NULL;
4337
4338   xvimagesink->hue = xvimagesink->saturation = 0;
4339   xvimagesink->contrast = xvimagesink->brightness = 0;
4340   xvimagesink->cb_changed = FALSE;
4341
4342   xvimagesink->fps_n = 0;
4343   xvimagesink->fps_d = 0;
4344   xvimagesink->video_width = 0;
4345   xvimagesink->video_height = 0;
4346
4347   xvimagesink->x_lock = g_mutex_new ();
4348   xvimagesink->flow_lock = g_mutex_new ();
4349
4350   xvimagesink->image_pool = NULL;
4351   xvimagesink->pool_lock = g_mutex_new ();
4352
4353   xvimagesink->synchronous = FALSE;
4354   xvimagesink->double_buffer = TRUE;
4355   xvimagesink->running = FALSE;
4356   xvimagesink->keep_aspect = FALSE;
4357   xvimagesink->handle_events = TRUE;
4358   xvimagesink->par = NULL;
4359   xvimagesink->handle_expose = TRUE;
4360   xvimagesink->autopaint_colorkey = TRUE;
4361
4362   /* on 16bit displays this becomes r,g,b = 1,2,3
4363    * on 24bit displays this becomes r,g,b = 8,8,16
4364    * as a port atom value
4365    */
4366   xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
4367   xvimagesink->draw_borders = TRUE;
4368
4369 #ifdef GST_EXT_XV_ENHANCEMENT
4370   xvimagesink->display_mode = VIDEO_MODE_LCDONLY;
4371   xvimagesink->rotate_changed = TRUE;
4372   xvimagesink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
4373   xvimagesink->rotate_angle = DEGREE_270;
4374   xvimagesink->visible = TRUE;
4375   xvimagesink->zoom = 1;
4376   xvimagesink->rotation = -1;
4377   xvimagesink->dst_roi.x = 0;
4378   xvimagesink->dst_roi.y = 0;
4379   xvimagesink->dst_roi.w = 0;
4380   xvimagesink->dst_roi.h = 0;
4381   xvimagesink->xim_transparenter = NULL;
4382   xvimagesink->scr_w = 0;
4383   xvimagesink->scr_h = 0;
4384   xvimagesink->aligned_width = 0;
4385   xvimagesink->aligned_height = 0;
4386   xvimagesink->stop_video = FALSE;
4387   xvimagesink->is_hided = FALSE;
4388 #endif
4389 }
4390
4391 static void
4392 gst_xvimagesink_base_init (gpointer g_class)
4393 {
4394   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
4395
4396   gst_element_class_set_details_simple (element_class,
4397       "Video sink", "Sink/Video",
4398       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
4399
4400   gst_element_class_add_pad_template (element_class,
4401       gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
4402 }
4403
4404 static void
4405 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
4406 {
4407   GObjectClass *gobject_class;
4408   GstElementClass *gstelement_class;
4409   GstBaseSinkClass *gstbasesink_class;
4410   GstVideoSinkClass *videosink_class;
4411
4412   gobject_class = (GObjectClass *) klass;
4413   gstelement_class = (GstElementClass *) klass;
4414   gstbasesink_class = (GstBaseSinkClass *) klass;
4415   videosink_class = (GstVideoSinkClass *) klass;
4416
4417   parent_class = g_type_class_peek_parent (klass);
4418
4419   gobject_class->set_property = gst_xvimagesink_set_property;
4420   gobject_class->get_property = gst_xvimagesink_get_property;
4421
4422   g_object_class_install_property (gobject_class, PROP_CONTRAST,
4423       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
4424           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4425   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
4426       g_param_spec_int ("brightness", "Brightness",
4427           "The brightness of the video", -1000, 1000, 0,
4428           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4429   g_object_class_install_property (gobject_class, PROP_HUE,
4430       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
4431           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4432   g_object_class_install_property (gobject_class, PROP_SATURATION,
4433       g_param_spec_int ("saturation", "Saturation",
4434           "The saturation of the video", -1000, 1000, 0,
4435           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4436   g_object_class_install_property (gobject_class, PROP_DISPLAY,
4437       g_param_spec_string ("display", "Display", "X Display name", NULL,
4438           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4439   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
4440       g_param_spec_boolean ("synchronous", "Synchronous",
4441           "When enabled, runs "
4442           "the X display in synchronous mode. (used only for debugging)", FALSE,
4443           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4444   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
4445       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
4446           "The pixel aspect ratio of the device", "1/1",
4447           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4448   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
4449       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
4450           "When enabled, scaling will respect original aspect ratio", FALSE,
4451           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4452   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
4453       g_param_spec_boolean ("handle-events", "Handle XEvents",
4454           "When enabled, XEvents will be selected and handled", TRUE,
4455           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4456   g_object_class_install_property (gobject_class, PROP_DEVICE,
4457       g_param_spec_string ("device", "Adaptor number",
4458           "The number of the video adaptor", "0",
4459           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4460   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
4461       g_param_spec_string ("device-name", "Adaptor name",
4462           "The name of the video adaptor", NULL,
4463           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
4464   /**
4465    * GstXvImageSink:handle-expose
4466    *
4467    * When enabled, the current frame will always be drawn in response to X
4468    * Expose.
4469    *
4470    * Since: 0.10.14
4471    */
4472   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
4473       g_param_spec_boolean ("handle-expose", "Handle expose",
4474           "When enabled, "
4475           "the current frame will always be drawn in response to X Expose "
4476           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4477
4478   /**
4479    * GstXvImageSink:double-buffer
4480    *
4481    * Whether to double-buffer the output.
4482    *
4483    * Since: 0.10.14
4484    */
4485   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
4486       g_param_spec_boolean ("double-buffer", "Double-buffer",
4487           "Whether to double-buffer the output", TRUE,
4488           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4489   /**
4490    * GstXvImageSink:autopaint-colorkey
4491    *
4492    * Whether to autofill overlay with colorkey
4493    *
4494    * Since: 0.10.21
4495    */
4496   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
4497       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
4498           "Whether to autofill overlay with colorkey", TRUE,
4499           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4500   /**
4501    * GstXvImageSink:colorkey
4502    *
4503    * Color to use for the overlay mask.
4504    *
4505    * Since: 0.10.21
4506    */
4507   g_object_class_install_property (gobject_class, PROP_COLORKEY,
4508       g_param_spec_int ("colorkey", "Colorkey",
4509           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
4510           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4511
4512   /**
4513    * GstXvImageSink:draw-borders
4514    *
4515    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
4516    * unused parts of the video area.
4517    *
4518    * Since: 0.10.21
4519    */
4520   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
4521       g_param_spec_boolean ("draw-borders", "Colorkey",
4522           "Draw black borders to fill unused area in force-aspect-ratio mode",
4523           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4524
4525   /**
4526    * GstXvImageSink:window-width
4527    *
4528    * Actual width of the video window.
4529    *
4530    * Since: 0.10.32
4531    */
4532   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
4533       g_param_spec_uint64 ("window-width", "window-width",
4534           "Width of the window", 0, G_MAXUINT64, 0,
4535           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
4536
4537   /**
4538    * GstXvImageSink:window-height
4539    *
4540    * Actual height of the video window.
4541    *
4542    * Since: 0.10.32
4543    */
4544   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
4545       g_param_spec_uint64 ("window-height", "window-height",
4546           "Height of the window", 0, G_MAXUINT64, 0,
4547           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
4548
4549 #ifdef GST_EXT_XV_ENHANCEMENT
4550   /**
4551    * GstXvImageSink:display-mode
4552    *
4553    * select display mode
4554    */
4555   g_object_class_install_property(gobject_class, PROP_DISPLAY_MODE,
4556     g_param_spec_enum("display-mode", "Display Mode",
4557       "Display device setting",
4558       GST_TYPE_XVIMAGESINK_DISPLAY_MODE, VIDEO_MODE_LCDONLY,
4559       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4560
4561   /**
4562    * GstXvImageSink:display-geometry-method
4563    *
4564    * Display geometrical method setting
4565    */
4566   g_object_class_install_property(gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
4567     g_param_spec_enum("display-geometry-method", "Display geometry method",
4568       "Geometrical method for display",
4569       GST_TYPE_XVIMAGESINK_DISPLAY_GEOMETRY_METHOD, DEF_DISPLAY_GEOMETRY_METHOD,
4570       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4571
4572   /**
4573    * GstXvImageSink:rotate
4574    *
4575    * Draw rotation angle setting
4576    */
4577   g_object_class_install_property(gobject_class, PROP_ROTATE_ANGLE,
4578     g_param_spec_enum("rotate", "Rotate angle",
4579       "Rotate angle of display output",
4580       GST_TYPE_XVIMAGESINK_ROTATE_ANGLE, DEGREE_270,
4581       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4582
4583   /**
4584    * GstXvImageSink:visible
4585    *
4586    * Whether reserve original src size or not
4587    */
4588   g_object_class_install_property (gobject_class, PROP_VISIBLE,
4589       g_param_spec_boolean ("visible", "Visible",
4590           "Draws screen or blacks out, true means visible, false blacks out",
4591           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4592
4593   /**
4594    * GstXvImageSink:zoom
4595    *
4596    * Scale small area of screen to 2X, 3X, ... , 9X
4597    */
4598   g_object_class_install_property (gobject_class, PROP_ZOOM,
4599       g_param_spec_int ("zoom", "Zoom",
4600           "Zooms screen as nX", 1, 9, 1,
4601           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4602
4603   /**
4604    * GstXvImageSink:dst-roi-x
4605    *
4606    * X value of Destination ROI
4607    */
4608   g_object_class_install_property (gobject_class, PROP_DST_ROI_X,
4609       g_param_spec_int ("dst-roi-x", "Dst-ROI-X",
4610           "X value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
4611           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4612
4613   /**
4614    * GstXvImageSink:dst-roi-y
4615    *
4616    * Y value of Destination ROI
4617    */
4618   g_object_class_install_property (gobject_class, PROP_DST_ROI_Y,
4619       g_param_spec_int ("dst-roi-y", "Dst-ROI-Y",
4620           "Y value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
4621           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4622
4623   /**
4624    * GstXvImageSink:dst-roi-w
4625    *
4626    * W value of Destination ROI
4627    */
4628   g_object_class_install_property (gobject_class, PROP_DST_ROI_W,
4629       g_param_spec_int ("dst-roi-w", "Dst-ROI-W",
4630           "W value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
4631           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4632
4633   /**
4634    * GstXvImageSink:dst-roi-h
4635    *
4636    * H value of Destination ROI
4637    */
4638   g_object_class_install_property (gobject_class, PROP_DST_ROI_H,
4639       g_param_spec_int ("dst-roi-h", "Dst-ROI-H",
4640           "H value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
4641           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4642
4643   /**
4644    * GstXvImageSink:stop-video
4645    *
4646    * Stop video for releasing video source buffer
4647    */
4648   g_object_class_install_property (gobject_class, PROP_STOP_VIDEO,
4649       g_param_spec_int ("stop-video", "Stop-Video",
4650           "Stop video for releasing video source buffer", 0, 1, 0,
4651           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4652 #endif
4653
4654   gobject_class->finalize = gst_xvimagesink_finalize;
4655
4656   gstelement_class->change_state =
4657       GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
4658
4659   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
4660   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
4661   gstbasesink_class->buffer_alloc =
4662       GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
4663   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
4664   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
4665
4666   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
4667 }
4668
4669 /* ============================================================= */
4670 /*                                                               */
4671 /*                       Public Methods                          */
4672 /*                                                               */
4673 /* ============================================================= */
4674
4675 /* =========================================== */
4676 /*                                             */
4677 /*          Object typing & Creation           */
4678 /*                                             */
4679 /* =========================================== */
4680
4681 GType
4682 gst_xvimagesink_get_type (void)
4683 {
4684   static GType xvimagesink_type = 0;
4685
4686   if (!xvimagesink_type) {
4687     static const GTypeInfo xvimagesink_info = {
4688       sizeof (GstXvImageSinkClass),
4689       gst_xvimagesink_base_init,
4690       NULL,
4691       (GClassInitFunc) gst_xvimagesink_class_init,
4692       NULL,
4693       NULL,
4694       sizeof (GstXvImageSink),
4695       0,
4696       (GInstanceInitFunc) gst_xvimagesink_init,
4697     };
4698     static const GInterfaceInfo iface_info = {
4699       (GInterfaceInitFunc) gst_xvimagesink_interface_init,
4700       NULL,
4701       NULL,
4702     };
4703     static const GInterfaceInfo navigation_info = {
4704       (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
4705       NULL,
4706       NULL,
4707     };
4708     static const GInterfaceInfo overlay_info = {
4709       (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
4710       NULL,
4711       NULL,
4712     };
4713     static const GInterfaceInfo colorbalance_info = {
4714       (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
4715       NULL,
4716       NULL,
4717     };
4718     static const GInterfaceInfo propertyprobe_info = {
4719       (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
4720       NULL,
4721       NULL,
4722     };
4723     xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
4724         "GstXvImageSink", &xvimagesink_info, 0);
4725
4726     g_type_add_interface_static (xvimagesink_type,
4727         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
4728     g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
4729         &navigation_info);
4730     g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
4731         &overlay_info);
4732     g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
4733         &colorbalance_info);
4734     g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE,
4735         &propertyprobe_info);
4736
4737
4738     /* register type and create class in a more safe place instead of at
4739      * runtime since the type registration and class creation is not
4740      * threadsafe. */
4741     g_type_class_ref (gst_xvimage_buffer_get_type ());
4742   }
4743
4744   return xvimagesink_type;
4745 }
4746
4747 static gboolean
4748 plugin_init (GstPlugin * plugin)
4749 {
4750   if (!gst_element_register (plugin, "xvimagesink",
4751           GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
4752     return FALSE;
4753
4754   GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
4755       "xvimagesink element");
4756   GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
4757
4758   return TRUE;
4759 }
4760
4761 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
4762     GST_VERSION_MINOR,
4763     "xvimagesink",
4764     "XFree86 video output plugin using Xv extension",
4765     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)