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