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