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