gst_plugins_base/sys/ximage/ximagesink.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-ximagesink
       
    22  *
       
    23  * <refsect2>
       
    24  * <para>
       
    25  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
       
    26  * display. This element can receive a Window ID from the application through
       
    27  * the XOverlay interface and will then render video frames in this drawable.
       
    28  * If no Window ID was provided by the application, the element will create its
       
    29  * own internal window and render into it.
       
    30  * </para>
       
    31  * <title>Scaling</title>
       
    32  * <para>
       
    33  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
       
    34  * reverse caps negotiation to try to get scaled video frames for the drawable.
       
    35  * This is accomplished by asking the peer pad if it accepts some different caps
       
    36  * which in most cases implies that there is a scaling element in the pipeline,
       
    37  * or that an element generating the video frames can generate them with a 
       
    38  * different geometry. This mechanism is handled during buffer allocations, for
       
    39  * each allocation request the video sink will check the drawable geometry, look
       
    40  * at the
       
    41  * <link linkend="GstXImageSink--force-aspect-ratio">force-aspect-ratio</link>
       
    42  * property, calculate the geometry of desired video frames and then check that
       
    43  * the peer pad accept those new caps. If it does it will then allocate a buffer
       
    44  * in video memory with this new geometry and return it with the new caps.
       
    45  * </para>
       
    46  * <title>Events</title>
       
    47  * <para>
       
    48  * XImageSink creates a thread to handle events coming from the drawable. There
       
    49  * are several kind of events that can be grouped in 2 big categories: input 
       
    50  * events and window state related events. Input events will be translated to
       
    51  * navigation events and pushed upstream for other elements to react on them.
       
    52  * This includes events such as pointer moves, key press/release, clicks etc...
       
    53  * Other events are used to handle the drawable appearance even when the data
       
    54  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
       
    55  * paused, it will receive expose events from the drawable and draw the latest
       
    56  * frame with correct borders/aspect-ratio.
       
    57  * </para>
       
    58  * <title>Pixel aspect ratio</title>
       
    59  * <para>
       
    60  * When changing state to GST_STATE_READY, XImageSink will open a connection to
       
    61  * the display specified in the
       
    62  * <link linkend="GstXImageSink--display">display</link> property or the default
       
    63  * display if nothing specified. Once this connection is open it will inspect 
       
    64  * the display configuration including the physical display geometry and 
       
    65  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
       
    66  * video sink will set the calculated pixel aspect ratio on the caps to make 
       
    67  * sure that incoming video frames will have the correct pixel aspect ratio for
       
    68  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
       
    69  * then possible to enforce a specific pixel aspect ratio using the
       
    70  * <link linkend="GstXImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link>
       
    71  * property.
       
    72  * </para>
       
    73  * <title>Examples</title>
       
    74  * <para>
       
    75  * Here is a simple pipeline to test reverse negotiation :
       
    76  * <programlisting>
       
    77  * gst-launch -v videotestsrc ! queue ! ximagesink
       
    78  * </programlisting>
       
    79  * When the test video signal appears you can resize the window and see that
       
    80  * scaled buffers of the desired size are going to arrive with a short delay.
       
    81  * This illustrates how buffers of desired size are allocated along the way.
       
    82  * If you take away the queue, scaling will happen almost immediately.
       
    83  * </para>
       
    84  * <para>
       
    85  * Here is a simple pipeline to test navigation events :
       
    86  * <programlisting>
       
    87  * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
       
    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.)
       
    94  * </para>
       
    95  * <para>
       
    96  * Here is a simple pipeline to test pixel aspect ratio :
       
    97  * <programlisting>
       
    98  * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
       
    99  * </programlisting>
       
   100  * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
       
   101  * videotestsrc, in most cases the pixel aspect ratio of the display will be
       
   102  * 1/1. This means that videoscale will have to do the scaling to convert 
       
   103  * incoming frames to a size that will match the display pixel aspect ratio
       
   104  * (from 320x240 to 320x180 in this case). Note that you might have to escape 
       
   105  * some characters for your shell like '\(fraction\)'.
       
   106  * </para>
       
   107  * </refsect2>
       
   108  */
       
   109 
       
   110 #ifdef HAVE_CONFIG_H
       
   111 #include "config.h"
       
   112 #endif
       
   113 
       
   114 /* Our interfaces */
       
   115 #include <gst/interfaces/navigation.h>
       
   116 #include <gst/interfaces/xoverlay.h>
       
   117 
       
   118 /* Object header */
       
   119 #include "ximagesink.h"
       
   120 
       
   121 /* Debugging category */
       
   122 #include <gst/gstinfo.h>
       
   123 
       
   124 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
       
   125 #define GST_CAT_DEFAULT gst_debug_ximagesink
       
   126 
       
   127 typedef struct
       
   128 {
       
   129   unsigned long flags;
       
   130   unsigned long functions;
       
   131   unsigned long decorations;
       
   132   long input_mode;
       
   133   unsigned long status;
       
   134 }
       
   135 MotifWmHints, MwmHints;
       
   136 
       
   137 #define MWM_HINTS_DECORATIONS   (1L << 1)
       
   138 
       
   139 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
       
   140 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
       
   141     GstXImageBuffer * ximage);
       
   142 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
       
   143     GstXWindow * xwindow);
       
   144 static void gst_ximagesink_expose (GstXOverlay * overlay);
       
   145 
       
   146 /* ElementFactory information */
       
   147 static const GstElementDetails gst_ximagesink_details =
       
   148 GST_ELEMENT_DETAILS ("Video sink",
       
   149     "Sink/Video",
       
   150     "A standard X based videosink",
       
   151     "Julien Moutte <julien@moutte.net>");
       
   152 
       
   153 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
       
   154 GST_STATIC_PAD_TEMPLATE ("sink",
       
   155     GST_PAD_SINK,
       
   156     GST_PAD_ALWAYS,
       
   157     GST_STATIC_CAPS ("video/x-raw-rgb, "
       
   158         "framerate = (fraction) [ 0, MAX ], "
       
   159         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
       
   160     );
       
   161 
       
   162 enum
       
   163 {
       
   164   PROP_0,
       
   165   PROP_DISPLAY,
       
   166   PROP_SYNCHRONOUS,
       
   167   PROP_PIXEL_ASPECT_RATIO,
       
   168   PROP_FORCE_ASPECT_RATIO,
       
   169   PROP_HANDLE_EVENTS,
       
   170   PROP_HANDLE_EXPOSE
       
   171 };
       
   172 
       
   173 static GstVideoSinkClass *parent_class = NULL;
       
   174 
       
   175 /* ============================================================= */
       
   176 /*                                                               */
       
   177 /*                       Private Methods                         */
       
   178 /*                                                               */
       
   179 /* ============================================================= */
       
   180 
       
   181 /* ximage buffers */
       
   182 
       
   183 static GstBufferClass *ximage_buffer_parent_class = NULL;
       
   184 
       
   185 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
       
   186 
       
   187 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
       
   188 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
       
   189 #define GST_XIMAGE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
       
   190 
       
   191 /* So some words about GstMiniObject, this is pretty messy...
       
   192    GstMiniObject does not use the standard finalizing of GObjects, you are 
       
   193    supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
       
   194    which will handle its own refcount system and call gst_mini_object_free.
       
   195    gst_mini_object_free will call the class finalize method which is not the 
       
   196    one from GObject, after calling this finalize method it will free the object
       
   197    instance for you if the refcount is still 0 so you should not chain up */
       
   198 static void
       
   199 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
       
   200 {
       
   201   GstXImageSink *ximagesink = NULL;
       
   202   gboolean recycled = FALSE;
       
   203   gboolean running;
       
   204 
       
   205   g_return_if_fail (ximage != NULL);
       
   206 
       
   207   ximagesink = ximage->ximagesink;
       
   208   if (G_UNLIKELY (ximagesink == NULL)) {
       
   209     GST_WARNING_OBJECT (ximagesink, "no sink found");
       
   210     goto beach;
       
   211   }
       
   212 
       
   213   GST_OBJECT_LOCK (ximagesink);
       
   214   running = ximagesink->running;
       
   215   GST_OBJECT_UNLOCK (ximagesink);
       
   216 
       
   217   if (running == FALSE) {
       
   218     /* If the sink is shutting down, need to clear the image */
       
   219     GST_DEBUG_OBJECT (ximagesink,
       
   220         "destroy image %p because the sink is shutting down", ximage);
       
   221     gst_ximagesink_ximage_destroy (ximagesink, ximage);
       
   222   } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
       
   223       (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
       
   224     /* If our geometry changed we can't reuse that image. */
       
   225     GST_DEBUG_OBJECT (ximagesink,
       
   226         "destroy image %p as its size changed %dx%d vs current %dx%d",
       
   227         ximage, ximage->width, ximage->height,
       
   228         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
       
   229     gst_ximagesink_ximage_destroy (ximagesink, ximage);
       
   230   } else {
       
   231     /* In that case we can reuse the image and add it to our image pool. */
       
   232     GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
       
   233     /* need to increment the refcount again to recycle */
       
   234     gst_buffer_ref (GST_BUFFER_CAST (ximage));
       
   235     g_mutex_lock (ximagesink->pool_lock);
       
   236     ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
       
   237     g_mutex_unlock (ximagesink->pool_lock);
       
   238     recycled = TRUE;
       
   239   }
       
   240 
       
   241   if (!recycled)
       
   242     GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->
       
   243         finalize (GST_MINI_OBJECT (ximage));
       
   244 
       
   245 beach:
       
   246   return;
       
   247 }
       
   248 
       
   249 static void
       
   250 gst_ximage_buffer_free (GstXImageBuffer * ximage)
       
   251 {
       
   252   /* make sure it is not recycled */
       
   253   ximage->width = -1;
       
   254   ximage->height = -1;
       
   255   gst_buffer_unref (GST_BUFFER_CAST (ximage));
       
   256 }
       
   257 
       
   258 static void
       
   259 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
       
   260 {
       
   261 #ifdef HAVE_XSHM
       
   262   ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
       
   263   ximage_buffer->SHMInfo.shmid = -1;
       
   264 #endif
       
   265 }
       
   266 
       
   267 static void
       
   268 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
       
   269 {
       
   270   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
       
   271 
       
   272   ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
       
   273 
       
   274   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
       
   275       gst_ximage_buffer_finalize;
       
   276 }
       
   277 
       
   278 static GType
       
   279 gst_ximage_buffer_get_type (void)
       
   280 {
       
   281   static GType _gst_ximage_buffer_type;
       
   282 
       
   283   if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
       
   284     static const GTypeInfo ximage_buffer_info = {
       
   285       sizeof (GstBufferClass),
       
   286       NULL,
       
   287       NULL,
       
   288       gst_ximage_buffer_class_init,
       
   289       NULL,
       
   290       NULL,
       
   291       sizeof (GstXImageBuffer),
       
   292       0,
       
   293       (GInstanceInitFunc) gst_ximage_buffer_init,
       
   294       NULL
       
   295     };
       
   296     _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
       
   297         "GstXImageBuffer", &ximage_buffer_info, 0);
       
   298   }
       
   299   return _gst_ximage_buffer_type;
       
   300 }
       
   301 
       
   302 /* X11 stuff */
       
   303 
       
   304 static gboolean error_caught = FALSE;
       
   305 
       
   306 static int
       
   307 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
       
   308 {
       
   309   char error_msg[1024];
       
   310 
       
   311   XGetErrorText (display, xevent->error_code, error_msg, 1024);
       
   312   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
       
   313   error_caught = TRUE;
       
   314   return 0;
       
   315 }
       
   316 
       
   317 #ifdef HAVE_XSHM                /* Check that XShm calls actually work */
       
   318 
       
   319 static gboolean
       
   320 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
       
   321     GstXContext * xcontext)
       
   322 {
       
   323   XImage *ximage;
       
   324   XShmSegmentInfo SHMInfo;
       
   325   size_t size;
       
   326   int (*handler) (Display *, XErrorEvent *);
       
   327   gboolean result = FALSE;
       
   328   gboolean did_attach = FALSE;
       
   329 
       
   330   g_return_val_if_fail (xcontext != NULL, FALSE);
       
   331 
       
   332   /* Sync to ensure any older errors are already processed */
       
   333   XSync (xcontext->disp, FALSE);
       
   334 
       
   335   /* Set defaults so we don't free these later unnecessarily */
       
   336   SHMInfo.shmaddr = ((void *) -1);
       
   337   SHMInfo.shmid = -1;
       
   338 
       
   339   /* Setting an error handler to catch failure */
       
   340   error_caught = FALSE;
       
   341   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
       
   342 
       
   343   /* Trying to create a 1x1 ximage */
       
   344   GST_DEBUG ("XShmCreateImage of 1x1");
       
   345 
       
   346   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
       
   347       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
       
   348 
       
   349   /* Might cause an error, sync to ensure it is noticed */
       
   350   XSync (xcontext->disp, FALSE);
       
   351   if (!ximage || error_caught) {
       
   352     GST_WARNING ("could not XShmCreateImage a 1x1 image");
       
   353     goto beach;
       
   354   }
       
   355   size = ximage->height * ximage->bytes_per_line;
       
   356 
       
   357   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
       
   358   if (SHMInfo.shmid == -1) {
       
   359     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
       
   360         size);
       
   361     goto beach;
       
   362   }
       
   363 
       
   364   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
       
   365   if (SHMInfo.shmaddr == ((void *) -1)) {
       
   366     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
       
   367     /* Clean up shm seg */
       
   368     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
       
   369     goto beach;
       
   370   }
       
   371 
       
   372   /* Delete the shared memory segment as soon as we manage to attach. 
       
   373    * This way, it will be deleted as soon as we detach later, and not
       
   374    * leaked if we crash. */
       
   375   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
       
   376 
       
   377   ximage->data = SHMInfo.shmaddr;
       
   378   SHMInfo.readOnly = FALSE;
       
   379 
       
   380   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
       
   381     GST_WARNING ("Failed to XShmAttach");
       
   382     goto beach;
       
   383   }
       
   384 
       
   385   /* Sync to ensure we see any errors we caused */
       
   386   XSync (xcontext->disp, FALSE);
       
   387 
       
   388   if (!error_caught) {
       
   389     did_attach = TRUE;
       
   390     /* store whether we succeeded in result */
       
   391     result = TRUE;
       
   392   }
       
   393 
       
   394 beach:
       
   395   /* Sync to ensure we swallow any errors we caused and reset error_caught */
       
   396   XSync (xcontext->disp, FALSE);
       
   397   error_caught = FALSE;
       
   398   XSetErrorHandler (handler);
       
   399 
       
   400   if (did_attach) {
       
   401     XShmDetach (xcontext->disp, &SHMInfo);
       
   402     XSync (xcontext->disp, FALSE);
       
   403   }
       
   404   if (SHMInfo.shmaddr != ((void *) -1))
       
   405     shmdt (SHMInfo.shmaddr);
       
   406   if (ximage)
       
   407     XDestroyImage (ximage);
       
   408   return result;
       
   409 }
       
   410 #endif /* HAVE_XSHM */
       
   411 
       
   412 /* This function handles GstXImageBuffer creation depending on XShm availability */
       
   413 static GstXImageBuffer *
       
   414 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
       
   415 {
       
   416   GstXImageBuffer *ximage = NULL;
       
   417   GstStructure *structure = NULL;
       
   418   gboolean succeeded = FALSE;
       
   419   int (*handler) (Display *, XErrorEvent *);
       
   420 
       
   421   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
       
   422 
       
   423   ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
       
   424 
       
   425   structure = gst_caps_get_structure (caps, 0);
       
   426 
       
   427   if (!gst_structure_get_int (structure, "width", &ximage->width) ||
       
   428       !gst_structure_get_int (structure, "height", &ximage->height)) {
       
   429     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
       
   430   }
       
   431 
       
   432   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
       
   433       ximage->width, ximage->height);
       
   434 
       
   435   g_mutex_lock (ximagesink->x_lock);
       
   436 
       
   437   /* Setting an error handler to catch failure */
       
   438   error_caught = FALSE;
       
   439   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
       
   440 
       
   441 #ifdef HAVE_XSHM
       
   442   if (ximagesink->xcontext->use_xshm) {
       
   443     ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
       
   444         ximagesink->xcontext->visual,
       
   445         ximagesink->xcontext->depth,
       
   446         ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
       
   447     if (!ximage->ximage || error_caught) {
       
   448       g_mutex_unlock (ximagesink->x_lock);
       
   449       /* Reset error handler */
       
   450       error_caught = FALSE;
       
   451       XSetErrorHandler (handler);
       
   452       /* Push an error */
       
   453       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
   454           ("Failed to create output image buffer of %dx%d pixels",
       
   455               ximage->width, ximage->height),
       
   456           ("could not XShmCreateImage a %dx%d image",
       
   457               ximage->width, ximage->height));
       
   458       goto beach;
       
   459     }
       
   460 
       
   461     /* we have to use the returned bytes_per_line for our shm size */
       
   462     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
       
   463     GST_LOG_OBJECT (ximagesink,
       
   464         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
       
   465         ximage->size, ximage->width, ximage->ximage->bytes_per_line);
       
   466 
       
   467     ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
       
   468         IPC_CREAT | 0777);
       
   469     if (ximage->SHMInfo.shmid == -1) {
       
   470       g_mutex_unlock (ximagesink->x_lock);
       
   471       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
   472           ("Failed to create output image buffer of %dx%d pixels",
       
   473               ximage->width, ximage->height),
       
   474           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
       
   475               ximage->size));
       
   476       goto beach;
       
   477     }
       
   478 
       
   479     ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
       
   480     if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
       
   481       g_mutex_unlock (ximagesink->x_lock);
       
   482       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
   483           ("Failed to create output image buffer of %dx%d pixels",
       
   484               ximage->width, ximage->height),
       
   485           ("Failed to shmat: %s", g_strerror (errno)));
       
   486       /* Clean up the shared memory segment */
       
   487       shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
       
   488       goto beach;
       
   489     }
       
   490 
       
   491     /* Now that we've attached, we can delete the shared memory segment.
       
   492      * This way, it will be deleted as soon as we detach later, and not
       
   493      * leaked if we crash. */
       
   494     shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
       
   495 
       
   496     ximage->ximage->data = ximage->SHMInfo.shmaddr;
       
   497     ximage->SHMInfo.readOnly = FALSE;
       
   498 
       
   499     if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
       
   500       g_mutex_unlock (ximagesink->x_lock);
       
   501       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
   502           ("Failed to create output image buffer of %dx%d pixels",
       
   503               ximage->width, ximage->height), ("Failed to XShmAttach"));
       
   504       goto beach;
       
   505     }
       
   506 
       
   507     XSync (ximagesink->xcontext->disp, FALSE);
       
   508   } else
       
   509 #endif /* HAVE_XSHM */
       
   510   {
       
   511     guint allocsize;
       
   512 
       
   513     ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
       
   514         ximagesink->xcontext->visual,
       
   515         ximagesink->xcontext->depth,
       
   516         ZPixmap, 0, NULL,
       
   517         ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
       
   518     if (!ximage->ximage || error_caught) {
       
   519       g_mutex_unlock (ximagesink->x_lock);
       
   520       /* Reset error handler */
       
   521       error_caught = FALSE;
       
   522       XSetErrorHandler (handler);
       
   523       /* Push an error */
       
   524       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
   525           ("Failed to create output image buffer of %dx%d pixels",
       
   526               ximage->width, ximage->height),
       
   527           ("could not XCreateImage a %dx%d image",
       
   528               ximage->width, ximage->height));
       
   529       goto beach;
       
   530     }
       
   531 
       
   532     /* upstream will assume that rowstrides are multiples of 4, but this
       
   533      * doesn't always seem to be the case with XCreateImage() */
       
   534     if ((ximage->ximage->bytes_per_line % 4) != 0) {
       
   535       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
       
   536           "usually assumed");
       
   537     }
       
   538 
       
   539     /* we have to use the returned bytes_per_line for our image size */
       
   540     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
       
   541 
       
   542     /* alloc a bit more for unexpected strides to avoid crashes upstream.
       
   543      * FIXME: if we get an unrounded stride, the image will be displayed
       
   544      * distorted, since all upstream elements assume a rounded stride */
       
   545     allocsize =
       
   546         GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
       
   547         ximage->ximage->height;
       
   548     ximage->ximage->data = g_malloc (allocsize);
       
   549     GST_LOG_OBJECT (ximagesink,
       
   550         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
       
   551         "stride %d", ximage->size, allocsize, ximage->width,
       
   552         ximage->ximage->bytes_per_line);
       
   553 
       
   554     XSync (ximagesink->xcontext->disp, FALSE);
       
   555   }
       
   556 
       
   557   /* Reset error handler */
       
   558   error_caught = FALSE;
       
   559   XSetErrorHandler (handler);
       
   560 
       
   561   succeeded = TRUE;
       
   562 
       
   563   GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
       
   564   GST_BUFFER_SIZE (ximage) = ximage->size;
       
   565 
       
   566   /* Keep a ref to our sink */
       
   567   ximage->ximagesink = gst_object_ref (ximagesink);
       
   568 
       
   569   g_mutex_unlock (ximagesink->x_lock);
       
   570 beach:
       
   571   if (!succeeded) {
       
   572     gst_ximage_buffer_free (ximage);
       
   573     ximage = NULL;
       
   574   }
       
   575 
       
   576   return ximage;
       
   577 }
       
   578 
       
   579 /* This function destroys a GstXImageBuffer handling XShm availability */
       
   580 static void
       
   581 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
       
   582     GstXImageBuffer * ximage)
       
   583 {
       
   584   g_return_if_fail (ximage != NULL);
       
   585   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   586 
       
   587   /* Hold the object lock to ensure the XContext doesn't disappear */
       
   588   GST_OBJECT_LOCK (ximagesink);
       
   589 
       
   590   /* If the destroyed image is the current one we destroy our reference too */
       
   591   if (ximagesink->cur_image == ximage) {
       
   592     ximagesink->cur_image = NULL;
       
   593   }
       
   594 
       
   595   /* We might have some buffers destroyed after changing state to NULL */
       
   596   if (!ximagesink->xcontext) {
       
   597     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
       
   598 #ifdef HAVE_XSHM
       
   599     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
       
   600       shmdt (ximage->SHMInfo.shmaddr);
       
   601     }
       
   602 #endif
       
   603     goto beach;
       
   604   }
       
   605 
       
   606   g_mutex_lock (ximagesink->x_lock);
       
   607 
       
   608 #ifdef HAVE_XSHM
       
   609   if (ximagesink->xcontext->use_xshm) {
       
   610     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
       
   611       XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
       
   612       XSync (ximagesink->xcontext->disp, 0);
       
   613       shmdt (ximage->SHMInfo.shmaddr);
       
   614     }
       
   615     if (ximage->ximage)
       
   616       XDestroyImage (ximage->ximage);
       
   617 
       
   618   } else
       
   619 #endif /* HAVE_XSHM */
       
   620   {
       
   621     if (ximage->ximage) {
       
   622       XDestroyImage (ximage->ximage);
       
   623     }
       
   624   }
       
   625 
       
   626   XSync (ximagesink->xcontext->disp, FALSE);
       
   627 
       
   628   g_mutex_unlock (ximagesink->x_lock);
       
   629 
       
   630 beach:
       
   631   GST_OBJECT_UNLOCK (ximagesink);
       
   632 
       
   633   if (ximage->ximagesink) {
       
   634     /* Release the ref to our sink */
       
   635     ximage->ximagesink = NULL;
       
   636     gst_object_unref (ximagesink);
       
   637   }
       
   638 
       
   639   return;
       
   640 }
       
   641 
       
   642 /* We are called with the x_lock taken */
       
   643 static void
       
   644 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
       
   645     GstXWindow * xwindow, GstVideoRectangle rect)
       
   646 {
       
   647   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   648   g_return_if_fail (xwindow != NULL);
       
   649 
       
   650   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
       
   651       ximagesink->xcontext->black);
       
   652 
       
   653   /* Left border */
       
   654   if (rect.x > 0) {
       
   655     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
       
   656         0, 0, rect.x, xwindow->height);
       
   657   }
       
   658 
       
   659   /* Right border */
       
   660   if ((rect.x + rect.w) < xwindow->width) {
       
   661     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
       
   662         rect.x + rect.w, 0, xwindow->width, xwindow->height);
       
   663   }
       
   664 
       
   665   /* Top border */
       
   666   if (rect.y > 0) {
       
   667     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
       
   668         0, 0, xwindow->width, rect.y);
       
   669   }
       
   670 
       
   671   /* Bottom border */
       
   672   if ((rect.y + rect.h) < xwindow->height) {
       
   673     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
       
   674         0, rect.y + rect.h, xwindow->width, xwindow->height);
       
   675   }
       
   676 }
       
   677 
       
   678 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
       
   679 static gboolean
       
   680 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
       
   681 {
       
   682   GstVideoRectangle src, dst, result;
       
   683   gboolean draw_border = FALSE;
       
   684 
       
   685   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
       
   686 
       
   687   /* We take the flow_lock. If expose is in there we don't want to run
       
   688      concurrently from the data flow thread */
       
   689   g_mutex_lock (ximagesink->flow_lock);
       
   690 
       
   691   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
       
   692     g_mutex_unlock (ximagesink->flow_lock);
       
   693     return FALSE;
       
   694   }
       
   695 
       
   696   /* Draw borders when displaying the first frame. After this
       
   697      draw borders only on expose event. */
       
   698   if (!ximagesink->cur_image) {
       
   699     draw_border = TRUE;
       
   700   }
       
   701 
       
   702   /* Store a reference to the last image we put, lose the previous one */
       
   703   if (ximage && ximagesink->cur_image != ximage) {
       
   704     if (ximagesink->cur_image) {
       
   705       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
       
   706       gst_buffer_unref (ximagesink->cur_image);
       
   707     }
       
   708     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
       
   709     ximagesink->cur_image =
       
   710         GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
       
   711   }
       
   712 
       
   713   /* Expose sends a NULL image, we take the latest frame */
       
   714   if (!ximage) {
       
   715     draw_border = TRUE;
       
   716     if (ximagesink->cur_image) {
       
   717       ximage = ximagesink->cur_image;
       
   718     } else {
       
   719       g_mutex_unlock (ximagesink->flow_lock);
       
   720       return TRUE;
       
   721     }
       
   722   }
       
   723 
       
   724   gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
       
   725 
       
   726   src.w = ximage->width;
       
   727   src.h = ximage->height;
       
   728   dst.w = ximagesink->xwindow->width;
       
   729   dst.h = ximagesink->xwindow->height;
       
   730 
       
   731   gst_video_sink_center_rect (src, dst, &result, FALSE);
       
   732 
       
   733   g_mutex_lock (ximagesink->x_lock);
       
   734 
       
   735   if (draw_border) {
       
   736     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
       
   737         result);
       
   738   }
       
   739 #ifdef HAVE_XSHM
       
   740   if (ximagesink->xcontext->use_xshm) {
       
   741     GST_LOG_OBJECT (ximagesink,
       
   742         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
       
   743         ximage, 0, 0, result.x, result.y, result.w, result.h,
       
   744         ximagesink->xwindow->width, ximagesink->xwindow->height);
       
   745     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
       
   746         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
       
   747         result.w, result.h, FALSE);
       
   748   } else
       
   749 #endif /* HAVE_XSHM */
       
   750   {
       
   751     GST_LOG_OBJECT (ximagesink,
       
   752         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
       
   753         ximage, 0, 0, result.x, result.y, result.w, result.h,
       
   754         ximagesink->xwindow->width, ximagesink->xwindow->height);
       
   755     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
       
   756         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
       
   757         result.w, result.h);
       
   758   }
       
   759 
       
   760   XSync (ximagesink->xcontext->disp, FALSE);
       
   761 
       
   762   g_mutex_unlock (ximagesink->x_lock);
       
   763 
       
   764   g_mutex_unlock (ximagesink->flow_lock);
       
   765 
       
   766   return TRUE;
       
   767 }
       
   768 
       
   769 static gboolean
       
   770 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
       
   771     GstXWindow * window)
       
   772 {
       
   773   Atom hints_atom = None;
       
   774   MotifWmHints *hints;
       
   775 
       
   776   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
       
   777   g_return_val_if_fail (window != NULL, FALSE);
       
   778 
       
   779   g_mutex_lock (ximagesink->x_lock);
       
   780 
       
   781   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
       
   782   if (hints_atom == None) {
       
   783     g_mutex_unlock (ximagesink->x_lock);
       
   784     return FALSE;
       
   785   }
       
   786 
       
   787   hints = g_malloc0 (sizeof (MotifWmHints));
       
   788 
       
   789   hints->flags |= MWM_HINTS_DECORATIONS;
       
   790   hints->decorations = 1 << 0;
       
   791 
       
   792   XChangeProperty (ximagesink->xcontext->disp, window->win,
       
   793       hints_atom, hints_atom, 32, PropModeReplace,
       
   794       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
       
   795 
       
   796   XSync (ximagesink->xcontext->disp, FALSE);
       
   797 
       
   798   g_mutex_unlock (ximagesink->x_lock);
       
   799 
       
   800   g_free (hints);
       
   801 
       
   802   return TRUE;
       
   803 }
       
   804 
       
   805 /* This function handles a GstXWindow creation */
       
   806 static GstXWindow *
       
   807 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
       
   808 {
       
   809   GstXWindow *xwindow = NULL;
       
   810   XGCValues values;
       
   811 
       
   812   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
       
   813 
       
   814   xwindow = g_new0 (GstXWindow, 1);
       
   815 
       
   816   xwindow->width = width;
       
   817   xwindow->height = height;
       
   818   xwindow->internal = TRUE;
       
   819 
       
   820   g_mutex_lock (ximagesink->x_lock);
       
   821 
       
   822   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
       
   823       ximagesink->xcontext->root,
       
   824       0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
       
   825 
       
   826   /* We have to do that to prevent X from redrawing the background on 
       
   827      ConfigureNotify. This takes away flickering of video when resizing. */
       
   828   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
       
   829 
       
   830   if (ximagesink->handle_events) {
       
   831     Atom wm_delete;
       
   832 
       
   833     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
       
   834         StructureNotifyMask | PointerMotionMask | KeyPressMask |
       
   835         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
       
   836 
       
   837     /* Tell the window manager we'd like delete client messages instead of
       
   838      * being killed */
       
   839     wm_delete = XInternAtom (ximagesink->xcontext->disp,
       
   840         "WM_DELETE_WINDOW", False);
       
   841     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
       
   842         &wm_delete, 1);
       
   843   }
       
   844 
       
   845   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
       
   846       0, &values);
       
   847 
       
   848   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
       
   849 
       
   850   XSync (ximagesink->xcontext->disp, FALSE);
       
   851 
       
   852   g_mutex_unlock (ximagesink->x_lock);
       
   853 
       
   854   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
       
   855 
       
   856   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
       
   857 
       
   858   return xwindow;
       
   859 }
       
   860 
       
   861 /* This function destroys a GstXWindow */
       
   862 static void
       
   863 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
       
   864     GstXWindow * xwindow)
       
   865 {
       
   866   g_return_if_fail (xwindow != NULL);
       
   867   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   868 
       
   869   g_mutex_lock (ximagesink->x_lock);
       
   870 
       
   871   /* If we did not create that window we just free the GC and let it live */
       
   872   if (xwindow->internal)
       
   873     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
       
   874   else
       
   875     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
       
   876 
       
   877   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
       
   878 
       
   879   XSync (ximagesink->xcontext->disp, FALSE);
       
   880 
       
   881   g_mutex_unlock (ximagesink->x_lock);
       
   882 
       
   883   g_free (xwindow);
       
   884 }
       
   885 
       
   886 static void
       
   887 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
       
   888     GstXWindow * xwindow)
       
   889 {
       
   890   XWindowAttributes attr;
       
   891 
       
   892   g_return_if_fail (xwindow != NULL);
       
   893   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   894 
       
   895   /* Update the window geometry */
       
   896   g_mutex_lock (ximagesink->x_lock);
       
   897 
       
   898   XGetWindowAttributes (ximagesink->xcontext->disp,
       
   899       ximagesink->xwindow->win, &attr);
       
   900 
       
   901   ximagesink->xwindow->width = attr.width;
       
   902   ximagesink->xwindow->height = attr.height;
       
   903 
       
   904   g_mutex_unlock (ximagesink->x_lock);
       
   905 }
       
   906 
       
   907 static void
       
   908 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
       
   909 {
       
   910   g_return_if_fail (xwindow != NULL);
       
   911   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   912 
       
   913   g_mutex_lock (ximagesink->x_lock);
       
   914 
       
   915   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
       
   916       ximagesink->xcontext->black);
       
   917 
       
   918   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
       
   919       0, 0, xwindow->width, xwindow->height);
       
   920 
       
   921   XSync (ximagesink->xcontext->disp, FALSE);
       
   922 
       
   923   g_mutex_unlock (ximagesink->x_lock);
       
   924 }
       
   925 
       
   926 /* This function handles XEvents that might be in the queue. It generates
       
   927    GstEvent that will be sent upstream in the pipeline to handle interactivity
       
   928    and navigation.*/
       
   929 static void
       
   930 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
       
   931 {
       
   932   XEvent e;
       
   933   guint pointer_x = 0, pointer_y = 0;
       
   934   gboolean pointer_moved = FALSE;
       
   935   gboolean exposed = FALSE, configured = FALSE;
       
   936 
       
   937   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
   938 
       
   939   /* Then we get all pointer motion events, only the last position is
       
   940      interesting. */
       
   941   g_mutex_lock (ximagesink->flow_lock);
       
   942   g_mutex_lock (ximagesink->x_lock);
       
   943   while (XCheckWindowEvent (ximagesink->xcontext->disp,
       
   944           ximagesink->xwindow->win, PointerMotionMask, &e)) {
       
   945     g_mutex_unlock (ximagesink->x_lock);
       
   946     g_mutex_unlock (ximagesink->flow_lock);
       
   947 
       
   948     switch (e.type) {
       
   949       case MotionNotify:
       
   950         pointer_x = e.xmotion.x;
       
   951         pointer_y = e.xmotion.y;
       
   952         pointer_moved = TRUE;
       
   953         break;
       
   954       default:
       
   955         break;
       
   956     }
       
   957     g_mutex_lock (ximagesink->flow_lock);
       
   958     g_mutex_lock (ximagesink->x_lock);
       
   959   }
       
   960 
       
   961   if (pointer_moved) {
       
   962     g_mutex_unlock (ximagesink->x_lock);
       
   963     g_mutex_unlock (ximagesink->flow_lock);
       
   964 
       
   965     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
       
   966         pointer_x, pointer_y);
       
   967     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
       
   968         "mouse-move", 0, pointer_x, pointer_y);
       
   969 
       
   970     g_mutex_lock (ximagesink->flow_lock);
       
   971     g_mutex_lock (ximagesink->x_lock);
       
   972   }
       
   973 
       
   974   /* We get all remaining events on our window to throw them upstream */
       
   975   while (XCheckWindowEvent (ximagesink->xcontext->disp,
       
   976           ximagesink->xwindow->win,
       
   977           KeyPressMask | KeyReleaseMask |
       
   978           ButtonPressMask | ButtonReleaseMask, &e)) {
       
   979     KeySym keysym;
       
   980 
       
   981     /* We lock only for the X function call */
       
   982     g_mutex_unlock (ximagesink->x_lock);
       
   983     g_mutex_unlock (ximagesink->flow_lock);
       
   984 
       
   985     switch (e.type) {
       
   986       case ButtonPress:
       
   987         /* Mouse button pressed/released over our window. We send upstream
       
   988            events for interactivity/navigation */
       
   989         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
       
   990             e.xbutton.button, e.xbutton.x, e.xbutton.x);
       
   991         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
       
   992             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
       
   993         break;
       
   994       case ButtonRelease:
       
   995         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
       
   996             e.xbutton.button, e.xbutton.x, e.xbutton.x);
       
   997         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
       
   998             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
       
   999         break;
       
  1000       case KeyPress:
       
  1001       case KeyRelease:
       
  1002         /* Key pressed/released over our window. We send upstream
       
  1003            events for interactivity/navigation */
       
  1004         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
       
  1005             e.xkey.keycode, e.xkey.x, e.xkey.x);
       
  1006         g_mutex_lock (ximagesink->x_lock);
       
  1007         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
       
  1008             e.xkey.keycode, 0);
       
  1009         g_mutex_unlock (ximagesink->x_lock);
       
  1010         if (keysym != NoSymbol) {
       
  1011           char *key_str = NULL;
       
  1012 
       
  1013           g_mutex_lock (ximagesink->x_lock);
       
  1014           key_str = XKeysymToString (keysym);
       
  1015           g_mutex_unlock (ximagesink->x_lock);
       
  1016           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
       
  1017               e.type == KeyPress ? "key-press" : "key-release", key_str);
       
  1018 
       
  1019         } else {
       
  1020           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
       
  1021               e.type == KeyPress ? "key-press" : "key-release", "unknown");
       
  1022         }
       
  1023         break;
       
  1024       default:
       
  1025         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
       
  1026             e.type);
       
  1027     }
       
  1028     g_mutex_lock (ximagesink->flow_lock);
       
  1029     g_mutex_lock (ximagesink->x_lock);
       
  1030   }
       
  1031 
       
  1032   while (XCheckWindowEvent (ximagesink->xcontext->disp,
       
  1033           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
       
  1034     switch (e.type) {
       
  1035       case Expose:
       
  1036         exposed = TRUE;
       
  1037         break;
       
  1038       case ConfigureNotify:
       
  1039         configured = TRUE;
       
  1040         break;
       
  1041       default:
       
  1042         break;
       
  1043     }
       
  1044   }
       
  1045 
       
  1046   if (ximagesink->handle_expose && (exposed || configured)) {
       
  1047     g_mutex_unlock (ximagesink->x_lock);
       
  1048     g_mutex_unlock (ximagesink->flow_lock);
       
  1049 
       
  1050     gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
       
  1051 
       
  1052     g_mutex_lock (ximagesink->flow_lock);
       
  1053     g_mutex_lock (ximagesink->x_lock);
       
  1054   }
       
  1055 
       
  1056   /* Handle Display events */
       
  1057   while (XPending (ximagesink->xcontext->disp)) {
       
  1058     XNextEvent (ximagesink->xcontext->disp, &e);
       
  1059 
       
  1060     switch (e.type) {
       
  1061       case ClientMessage:{
       
  1062         Atom wm_delete;
       
  1063 
       
  1064         wm_delete = XInternAtom (ximagesink->xcontext->disp,
       
  1065             "WM_DELETE_WINDOW", False);
       
  1066         if (wm_delete == (Atom) e.xclient.data.l[0]) {
       
  1067           /* Handle window deletion by posting an error on the bus */
       
  1068           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
       
  1069               ("Output window was closed"), (NULL));
       
  1070 
       
  1071           g_mutex_unlock (ximagesink->x_lock);
       
  1072           gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
       
  1073           ximagesink->xwindow = NULL;
       
  1074           g_mutex_lock (ximagesink->x_lock);
       
  1075         }
       
  1076         break;
       
  1077       }
       
  1078       default:
       
  1079         break;
       
  1080     }
       
  1081   }
       
  1082 
       
  1083   g_mutex_unlock (ximagesink->x_lock);
       
  1084   g_mutex_unlock (ximagesink->flow_lock);
       
  1085 }
       
  1086 
       
  1087 static gpointer
       
  1088 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
       
  1089 {
       
  1090   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
       
  1091 
       
  1092   GST_OBJECT_LOCK (ximagesink);
       
  1093   while (ximagesink->running) {
       
  1094     GST_OBJECT_UNLOCK (ximagesink);
       
  1095 
       
  1096     if (ximagesink->xwindow) {
       
  1097       gst_ximagesink_handle_xevents (ximagesink);
       
  1098     }
       
  1099     g_usleep (100000);
       
  1100 
       
  1101     GST_OBJECT_LOCK (ximagesink);
       
  1102   }
       
  1103   GST_OBJECT_UNLOCK (ximagesink);
       
  1104 
       
  1105   return NULL;
       
  1106 }
       
  1107 
       
  1108 /* This function calculates the pixel aspect ratio based on the properties
       
  1109  * in the xcontext structure and stores it there. */
       
  1110 static void
       
  1111 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
       
  1112 {
       
  1113   static const gint par[][2] = {
       
  1114     {1, 1},                     /* regular screen */
       
  1115     {16, 15},                   /* PAL TV */
       
  1116     {11, 10},                   /* 525 line Rec.601 video */
       
  1117     {54, 59},                   /* 625 line Rec.601 video */
       
  1118     {64, 45},                   /* 1280x1024 on 16:9 display */
       
  1119     {5, 3},                     /* 1280x1024 on 4:3 display */
       
  1120     {4, 3}                      /*  800x600 on 16:9 display */
       
  1121   };
       
  1122   gint i;
       
  1123   gint index;
       
  1124   gdouble ratio;
       
  1125   gdouble delta;
       
  1126 
       
  1127 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
       
  1128 
       
  1129   /* first calculate the "real" ratio based on the X values;
       
  1130    * which is the "physical" w/h divided by the w/h in pixels of the display */
       
  1131   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
       
  1132       / (xcontext->heightmm * xcontext->width);
       
  1133 
       
  1134   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
       
  1135    * override here */
       
  1136   if (xcontext->width == 720 && xcontext->height == 576) {
       
  1137     ratio = 4.0 * 576 / (3.0 * 720);
       
  1138   }
       
  1139   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
       
  1140 
       
  1141   /* now find the one from par[][2] with the lowest delta to the real one */
       
  1142   delta = DELTA (0);
       
  1143   index = 0;
       
  1144 
       
  1145   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
       
  1146     gdouble this_delta = DELTA (i);
       
  1147 
       
  1148     if (this_delta < delta) {
       
  1149       index = i;
       
  1150       delta = this_delta;
       
  1151     }
       
  1152   }
       
  1153 
       
  1154   GST_DEBUG ("Decided on index %d (%d/%d)", index,
       
  1155       par[index][0], par[index][1]);
       
  1156 
       
  1157   g_free (xcontext->par);
       
  1158   xcontext->par = g_new0 (GValue, 1);
       
  1159   g_value_init (xcontext->par, GST_TYPE_FRACTION);
       
  1160   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
       
  1161   GST_DEBUG ("set xcontext PAR to %d/%d",
       
  1162       gst_value_get_fraction_numerator (xcontext->par),
       
  1163       gst_value_get_fraction_denominator (xcontext->par));
       
  1164 }
       
  1165 
       
  1166 /* This function gets the X Display and global info about it. Everything is
       
  1167    stored in our object and will be cleaned when the object is disposed. Note
       
  1168    here that caps for supported format are generated without any window or
       
  1169    image creation */
       
  1170 static GstXContext *
       
  1171 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
       
  1172 {
       
  1173   GstXContext *xcontext = NULL;
       
  1174   XPixmapFormatValues *px_formats = NULL;
       
  1175   gint nb_formats = 0, i;
       
  1176 
       
  1177   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
       
  1178 
       
  1179   xcontext = g_new0 (GstXContext, 1);
       
  1180 
       
  1181   g_mutex_lock (ximagesink->x_lock);
       
  1182 
       
  1183   xcontext->disp = XOpenDisplay (ximagesink->display_name);
       
  1184 
       
  1185   if (!xcontext->disp) {
       
  1186     g_mutex_unlock (ximagesink->x_lock);
       
  1187     g_free (xcontext);
       
  1188     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
  1189         ("Could not initialise X output"), ("Could not open display"));
       
  1190     return NULL;
       
  1191   }
       
  1192 
       
  1193   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
       
  1194   xcontext->screen_num = DefaultScreen (xcontext->disp);
       
  1195   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
       
  1196   xcontext->root = DefaultRootWindow (xcontext->disp);
       
  1197   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
       
  1198   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
       
  1199   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
       
  1200 
       
  1201   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
       
  1202   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
       
  1203   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
       
  1204   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
       
  1205 
       
  1206   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
       
  1207       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
       
  1208 
       
  1209   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
       
  1210 
       
  1211   /* We get supported pixmap formats at supported depth */
       
  1212   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
       
  1213 
       
  1214   if (!px_formats) {
       
  1215     XCloseDisplay (xcontext->disp);
       
  1216     g_mutex_unlock (ximagesink->x_lock);
       
  1217     g_free (xcontext->par);
       
  1218     g_free (xcontext);
       
  1219     return NULL;
       
  1220   }
       
  1221 
       
  1222   /* We get bpp value corresponding to our running depth */
       
  1223   for (i = 0; i < nb_formats; i++) {
       
  1224     if (px_formats[i].depth == xcontext->depth)
       
  1225       xcontext->bpp = px_formats[i].bits_per_pixel;
       
  1226   }
       
  1227 
       
  1228   XFree (px_formats);
       
  1229 
       
  1230   xcontext->endianness =
       
  1231       (ImageByteOrder (xcontext->disp) ==
       
  1232       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
       
  1233 
       
  1234   /* Search for XShm extension support */
       
  1235 #ifdef HAVE_XSHM
       
  1236   if (XShmQueryExtension (xcontext->disp) &&
       
  1237       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
       
  1238     xcontext->use_xshm = TRUE;
       
  1239     GST_DEBUG ("ximagesink is using XShm extension");
       
  1240   } else
       
  1241 #endif
       
  1242   {
       
  1243     xcontext->use_xshm = FALSE;
       
  1244     GST_DEBUG ("ximagesink is not using XShm extension");
       
  1245   }
       
  1246 
       
  1247   /* our caps system handles 24/32bpp RGB as big-endian. */
       
  1248   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
       
  1249       xcontext->endianness == G_LITTLE_ENDIAN) {
       
  1250     xcontext->endianness = G_BIG_ENDIAN;
       
  1251     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
       
  1252     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
       
  1253     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
       
  1254     if (xcontext->bpp == 24) {
       
  1255       xcontext->visual->red_mask >>= 8;
       
  1256       xcontext->visual->green_mask >>= 8;
       
  1257       xcontext->visual->blue_mask >>= 8;
       
  1258     }
       
  1259   }
       
  1260 
       
  1261   /* update object's par with calculated one if not set yet */
       
  1262   if (!ximagesink->par) {
       
  1263     ximagesink->par = g_new0 (GValue, 1);
       
  1264     gst_value_init_and_copy (ximagesink->par, xcontext->par);
       
  1265     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
       
  1266   }
       
  1267   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
       
  1268       "bpp", G_TYPE_INT, xcontext->bpp,
       
  1269       "depth", G_TYPE_INT, xcontext->depth,
       
  1270       "endianness", G_TYPE_INT, xcontext->endianness,
       
  1271       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
       
  1272       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
       
  1273       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
       
  1274       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
       
  1275       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
       
  1276       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
       
  1277   if (ximagesink->par) {
       
  1278     int nom, den;
       
  1279 
       
  1280     nom = gst_value_get_fraction_numerator (ximagesink->par);
       
  1281     den = gst_value_get_fraction_denominator (ximagesink->par);
       
  1282     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
       
  1283         GST_TYPE_FRACTION, nom, den, NULL);
       
  1284   }
       
  1285 
       
  1286   g_mutex_unlock (ximagesink->x_lock);
       
  1287 
       
  1288   /* Setup our event listening thread */
       
  1289   GST_OBJECT_LOCK (ximagesink);
       
  1290   ximagesink->running = TRUE;
       
  1291   ximagesink->event_thread = g_thread_create (
       
  1292       (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
       
  1293   GST_OBJECT_UNLOCK (ximagesink);
       
  1294 
       
  1295   return xcontext;
       
  1296 }
       
  1297 
       
  1298 /* This function cleans the X context. Closing the Display and unrefing the
       
  1299    caps for supported formats. */
       
  1300 static void
       
  1301 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
       
  1302 {
       
  1303   GstXContext *xcontext;
       
  1304 
       
  1305   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
       
  1306 
       
  1307   GST_OBJECT_LOCK (ximagesink);
       
  1308   if (ximagesink->xcontext == NULL) {
       
  1309     GST_OBJECT_UNLOCK (ximagesink);
       
  1310     return;
       
  1311   }
       
  1312 
       
  1313   /* Take the xcontext reference and NULL it while we
       
  1314    * clean it up, so that any buffer-alloced buffers 
       
  1315    * arriving after this will be freed correctly */
       
  1316   xcontext = ximagesink->xcontext;
       
  1317   ximagesink->xcontext = NULL;
       
  1318 
       
  1319   GST_OBJECT_UNLOCK (ximagesink);
       
  1320 
       
  1321   gst_caps_unref (xcontext->caps);
       
  1322   g_free (xcontext->par);
       
  1323   g_free (ximagesink->par);
       
  1324   ximagesink->par = NULL;
       
  1325 
       
  1326   g_mutex_lock (ximagesink->x_lock);
       
  1327 
       
  1328   XCloseDisplay (xcontext->disp);
       
  1329 
       
  1330   g_mutex_unlock (ximagesink->x_lock);
       
  1331 
       
  1332   g_free (xcontext);
       
  1333 }
       
  1334 
       
  1335 static void
       
  1336 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
       
  1337 {
       
  1338 
       
  1339   g_mutex_lock (ximagesink->pool_lock);
       
  1340 
       
  1341   while (ximagesink->buffer_pool) {
       
  1342     GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
       
  1343 
       
  1344     ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
       
  1345         ximagesink->buffer_pool);
       
  1346     gst_ximage_buffer_free (ximage);
       
  1347   }
       
  1348 
       
  1349   g_mutex_unlock (ximagesink->pool_lock);
       
  1350 }
       
  1351 
       
  1352 /* Element stuff */
       
  1353 
       
  1354 static GstCaps *
       
  1355 gst_ximagesink_getcaps (GstBaseSink * bsink)
       
  1356 {
       
  1357   GstXImageSink *ximagesink;
       
  1358   GstCaps *caps;
       
  1359   int i;
       
  1360 
       
  1361   ximagesink = GST_XIMAGESINK (bsink);
       
  1362 
       
  1363   if (ximagesink->xcontext)
       
  1364     return gst_caps_ref (ximagesink->xcontext->caps);
       
  1365 
       
  1366   /* get a template copy and add the pixel aspect ratio */
       
  1367   caps =
       
  1368       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
       
  1369           sinkpad));
       
  1370   for (i = 0; i < gst_caps_get_size (caps); ++i) {
       
  1371     GstStructure *structure = gst_caps_get_structure (caps, i);
       
  1372 
       
  1373     if (ximagesink->par) {
       
  1374       int nom, den;
       
  1375 
       
  1376       nom = gst_value_get_fraction_numerator (ximagesink->par);
       
  1377       den = gst_value_get_fraction_denominator (ximagesink->par);
       
  1378       gst_structure_set (structure, "pixel-aspect-ratio",
       
  1379           GST_TYPE_FRACTION, nom, den, NULL);
       
  1380     }
       
  1381   }
       
  1382 
       
  1383   return caps;
       
  1384 }
       
  1385 
       
  1386 static gboolean
       
  1387 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
       
  1388 {
       
  1389   GstXImageSink *ximagesink;
       
  1390   gboolean ret = TRUE;
       
  1391   GstStructure *structure;
       
  1392   GstCaps *intersection;
       
  1393   const GValue *par;
       
  1394   gint new_width, new_height;
       
  1395   const GValue *fps;
       
  1396 
       
  1397   ximagesink = GST_XIMAGESINK (bsink);
       
  1398 
       
  1399   if (!ximagesink->xcontext)
       
  1400     return FALSE;
       
  1401 
       
  1402   GST_DEBUG_OBJECT (ximagesink,
       
  1403       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
       
  1404       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
       
  1405 
       
  1406   /* We intersect those caps with our template to make sure they are correct */
       
  1407   intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
       
  1408   GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
       
  1409       intersection);
       
  1410   if (gst_caps_is_empty (intersection)) {
       
  1411     gst_caps_unref (intersection);
       
  1412     return FALSE;
       
  1413   }
       
  1414 
       
  1415   gst_caps_unref (intersection);
       
  1416 
       
  1417   structure = gst_caps_get_structure (caps, 0);
       
  1418 
       
  1419   ret &= gst_structure_get_int (structure, "width", &new_width);
       
  1420   ret &= gst_structure_get_int (structure, "height", &new_height);
       
  1421   fps = gst_structure_get_value (structure, "framerate");
       
  1422   ret &= (fps != NULL);
       
  1423   if (!ret)
       
  1424     return FALSE;
       
  1425 
       
  1426   /* if the caps contain pixel-aspect-ratio, they have to match ours,
       
  1427    * otherwise linking should fail */
       
  1428   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
       
  1429   if (par) {
       
  1430     if (ximagesink->par) {
       
  1431       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
       
  1432         goto wrong_aspect;
       
  1433       }
       
  1434     } else if (ximagesink->xcontext->par) {
       
  1435       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
       
  1436         goto wrong_aspect;
       
  1437       }
       
  1438     }
       
  1439   }
       
  1440 
       
  1441   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
       
  1442   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
       
  1443   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
       
  1444   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
       
  1445 
       
  1446   /* Notify application to set xwindow id now */
       
  1447   g_mutex_lock (ximagesink->flow_lock);
       
  1448   if (!ximagesink->xwindow) {
       
  1449     g_mutex_unlock (ximagesink->flow_lock);
       
  1450     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
       
  1451   } else {
       
  1452     g_mutex_unlock (ximagesink->flow_lock);
       
  1453   }
       
  1454 
       
  1455   /* Creating our window and our image */
       
  1456   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
       
  1457       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
       
  1458     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
       
  1459         ("Invalid image size."));
       
  1460     return FALSE;
       
  1461   }
       
  1462 
       
  1463   g_mutex_lock (ximagesink->flow_lock);
       
  1464   if (!ximagesink->xwindow) {
       
  1465     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
       
  1466         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
       
  1467   }
       
  1468   g_mutex_unlock (ximagesink->flow_lock);
       
  1469 
       
  1470   /* If our ximage has changed we destroy it, next chain iteration will create
       
  1471      a new one */
       
  1472   if ((ximagesink->ximage) &&
       
  1473       ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
       
  1474           (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
       
  1475     GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
       
  1476         ximagesink->ximage);
       
  1477     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
       
  1478     ximagesink->ximage = NULL;
       
  1479   }
       
  1480 
       
  1481   return TRUE;
       
  1482 
       
  1483   /* ERRORS */
       
  1484 wrong_aspect:
       
  1485   {
       
  1486     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
       
  1487     return FALSE;
       
  1488   }
       
  1489 }
       
  1490 
       
  1491 static GstStateChangeReturn
       
  1492 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
       
  1493 {
       
  1494   GstXImageSink *ximagesink;
       
  1495   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
       
  1496   GstXContext *xcontext = NULL;
       
  1497 
       
  1498   ximagesink = GST_XIMAGESINK (element);
       
  1499 
       
  1500   switch (transition) {
       
  1501     case GST_STATE_CHANGE_NULL_TO_READY:
       
  1502 
       
  1503       /* Initializing the XContext */
       
  1504       if (ximagesink->xcontext == NULL) {
       
  1505         xcontext = gst_ximagesink_xcontext_get (ximagesink);
       
  1506         if (xcontext == NULL) {
       
  1507           ret = GST_STATE_CHANGE_FAILURE;
       
  1508           goto beach;
       
  1509         }
       
  1510         GST_OBJECT_LOCK (ximagesink);
       
  1511         if (xcontext)
       
  1512           ximagesink->xcontext = xcontext;
       
  1513         GST_OBJECT_UNLOCK (ximagesink);
       
  1514       }
       
  1515 
       
  1516       /* call XSynchronize with the current value of synchronous */
       
  1517       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
       
  1518           ximagesink->synchronous ? "TRUE" : "FALSE");
       
  1519       g_mutex_lock (ximagesink->x_lock);
       
  1520       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
       
  1521       g_mutex_unlock (ximagesink->x_lock);
       
  1522       break;
       
  1523     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1524       g_mutex_lock (ximagesink->flow_lock);
       
  1525       if (ximagesink->xwindow)
       
  1526         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
       
  1527       g_mutex_unlock (ximagesink->flow_lock);
       
  1528       break;
       
  1529     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       
  1530       break;
       
  1531     default:
       
  1532       break;
       
  1533   }
       
  1534 
       
  1535   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
  1536 
       
  1537   switch (transition) {
       
  1538     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
  1539       break;
       
  1540     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1541       ximagesink->fps_n = 0;
       
  1542       ximagesink->fps_d = 1;
       
  1543       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
       
  1544       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
       
  1545       break;
       
  1546     case GST_STATE_CHANGE_READY_TO_NULL:
       
  1547       gst_ximagesink_reset (ximagesink);
       
  1548       break;
       
  1549     default:
       
  1550       break;
       
  1551   }
       
  1552 
       
  1553 beach:
       
  1554   return ret;
       
  1555 }
       
  1556 
       
  1557 static void
       
  1558 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
       
  1559     GstClockTime * start, GstClockTime * end)
       
  1560 {
       
  1561   GstXImageSink *ximagesink;
       
  1562 
       
  1563   ximagesink = GST_XIMAGESINK (bsink);
       
  1564 
       
  1565   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
       
  1566     *start = GST_BUFFER_TIMESTAMP (buf);
       
  1567     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
       
  1568       *end = *start + GST_BUFFER_DURATION (buf);
       
  1569     } else {
       
  1570       if (ximagesink->fps_n > 0) {
       
  1571         *end = *start +
       
  1572             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
       
  1573             ximagesink->fps_n);
       
  1574       }
       
  1575     }
       
  1576   }
       
  1577 }
       
  1578 
       
  1579 static GstFlowReturn
       
  1580 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
       
  1581 {
       
  1582   GstXImageSink *ximagesink;
       
  1583 
       
  1584   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
       
  1585 
       
  1586   ximagesink = GST_XIMAGESINK (bsink);
       
  1587 
       
  1588   /* If this buffer has been allocated using our buffer management we simply
       
  1589      put the ximage which is in the PRIVATE pointer */
       
  1590   if (GST_IS_XIMAGE_BUFFER (buf)) {
       
  1591     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
       
  1592     if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
       
  1593       goto no_window;
       
  1594   } else {
       
  1595     /* Else we have to copy the data into our private image, */
       
  1596     /* if we have one... */
       
  1597     GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
       
  1598     if (!ximagesink->ximage) {
       
  1599       GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
       
  1600       ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
       
  1601           GST_BUFFER_CAPS (buf));
       
  1602       if (!ximagesink->ximage)
       
  1603         /* The create method should have posted an informative error */
       
  1604         goto no_ximage;
       
  1605 
       
  1606       if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
       
  1607         GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
       
  1608             ("Failed to create output image buffer of %dx%d pixels",
       
  1609                 ximagesink->ximage->width, ximagesink->ximage->height),
       
  1610             ("XServer allocated buffer size did not match input buffer"));
       
  1611 
       
  1612         gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
       
  1613         ximagesink->ximage = NULL;
       
  1614         goto no_ximage;
       
  1615       }
       
  1616     }
       
  1617     memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
       
  1618         MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
       
  1619     if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
       
  1620       goto no_window;
       
  1621   }
       
  1622 
       
  1623   return GST_FLOW_OK;
       
  1624 
       
  1625   /* ERRORS */
       
  1626 no_ximage:
       
  1627   {
       
  1628     /* No image available. That's very bad ! */
       
  1629     GST_DEBUG ("could not create image");
       
  1630     return GST_FLOW_ERROR;
       
  1631   }
       
  1632 no_window:
       
  1633   {
       
  1634     /* No Window available to put our image into */
       
  1635     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
       
  1636     return GST_FLOW_ERROR;
       
  1637   }
       
  1638 }
       
  1639 
       
  1640 /* Buffer management
       
  1641  *
       
  1642  * The buffer_alloc function must either return a buffer with given size and
       
  1643  * caps or create a buffer with different caps attached to the buffer. This
       
  1644  * last option is called reverse negotiation, ie, where the sink suggests a
       
  1645  * different format from the upstream peer. 
       
  1646  *
       
  1647  * We try to do reverse negotiation when our geometry changes and we like a
       
  1648  * resized buffer.
       
  1649  */
       
  1650 static GstFlowReturn
       
  1651 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
       
  1652     GstCaps * caps, GstBuffer ** buf)
       
  1653 {
       
  1654   GstXImageSink *ximagesink;
       
  1655   GstXImageBuffer *ximage = NULL;
       
  1656   GstStructure *structure = NULL;
       
  1657   GstFlowReturn ret = GST_FLOW_OK;
       
  1658   GstCaps *alloc_caps;
       
  1659   gboolean alloc_unref = FALSE;
       
  1660   gint width, height;
       
  1661 
       
  1662   ximagesink = GST_XIMAGESINK (bsink);
       
  1663 
       
  1664   GST_LOG_OBJECT (ximagesink,
       
  1665       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
       
  1666       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
       
  1667 
       
  1668   /* assume we're going to alloc what was requested, keep track of
       
  1669    * wheter we need to unref or not. When we suggest a new format 
       
  1670    * upstream we will create a new caps that we need to unref. */
       
  1671   alloc_caps = caps;
       
  1672   alloc_unref = FALSE;
       
  1673 
       
  1674   /* get struct to see what is requested */
       
  1675   structure = gst_caps_get_structure (caps, 0);
       
  1676 
       
  1677   if (gst_structure_get_int (structure, "width", &width) &&
       
  1678       gst_structure_get_int (structure, "height", &height)) {
       
  1679     GstVideoRectangle dst, src, result;
       
  1680 
       
  1681     src.w = width;
       
  1682     src.h = height;
       
  1683 
       
  1684     /* We take the flow_lock because the window might go away */
       
  1685     g_mutex_lock (ximagesink->flow_lock);
       
  1686     if (!ximagesink->xwindow) {
       
  1687       g_mutex_unlock (ximagesink->flow_lock);
       
  1688       goto alloc;
       
  1689     }
       
  1690 
       
  1691     /* What is our geometry */
       
  1692     gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
       
  1693     dst.w = ximagesink->xwindow->width;
       
  1694     dst.h = ximagesink->xwindow->height;
       
  1695 
       
  1696     g_mutex_unlock (ximagesink->flow_lock);
       
  1697 
       
  1698     if (ximagesink->keep_aspect) {
       
  1699       GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
       
  1700           "negotiation");
       
  1701       gst_video_sink_center_rect (src, dst, &result, TRUE);
       
  1702     } else {
       
  1703       GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
       
  1704           "ignoring aspect ratio");
       
  1705       result.x = result.y = 0;
       
  1706       result.w = dst.w;
       
  1707       result.h = dst.h;
       
  1708     }
       
  1709 
       
  1710     /* We would like another geometry */
       
  1711     if (width != result.w || height != result.h) {
       
  1712       int nom, den;
       
  1713       GstCaps *desired_caps;
       
  1714       GstStructure *desired_struct;
       
  1715 
       
  1716       /* make a copy of the incomming caps to create the new
       
  1717        * suggestion. We can't use make_writable because we might
       
  1718        * then destroy the original caps which we still need when the
       
  1719        * peer does not accept the suggestion. */
       
  1720       desired_caps = gst_caps_copy (caps);
       
  1721       desired_struct = gst_caps_get_structure (desired_caps, 0);
       
  1722 
       
  1723       GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
       
  1724       gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
       
  1725       gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
       
  1726 
       
  1727       /* PAR property overrides the X calculated one */
       
  1728       if (ximagesink->par) {
       
  1729         nom = gst_value_get_fraction_numerator (ximagesink->par);
       
  1730         den = gst_value_get_fraction_denominator (ximagesink->par);
       
  1731         gst_structure_set (desired_struct, "pixel-aspect-ratio",
       
  1732             GST_TYPE_FRACTION, nom, den, NULL);
       
  1733       } else if (ximagesink->xcontext->par) {
       
  1734         nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
       
  1735         den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
       
  1736         gst_structure_set (desired_struct, "pixel-aspect-ratio",
       
  1737             GST_TYPE_FRACTION, nom, den, NULL);
       
  1738       }
       
  1739 
       
  1740       /* see if peer accepts our new suggestion, if there is no peer, this 
       
  1741        * function returns true. */
       
  1742       if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
       
  1743               desired_caps)) {
       
  1744         gint bpp;
       
  1745 
       
  1746         bpp = size / height / width;
       
  1747         /* we will not alloc a buffer of the new suggested caps. Make sure
       
  1748          * we also unref this new caps after we set it on the buffer. */
       
  1749         alloc_caps = desired_caps;
       
  1750         alloc_unref = TRUE;
       
  1751         width = result.w;
       
  1752         height = result.h;
       
  1753         size = bpp * width * height;
       
  1754         GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT
       
  1755             " buffer size is now %d bytes", desired_caps, size);
       
  1756       } else {
       
  1757         GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
       
  1758             desired_caps);
       
  1759         /* we alloc a buffer with the original incomming caps */
       
  1760         width = GST_VIDEO_SINK_WIDTH (ximagesink);
       
  1761         height = GST_VIDEO_SINK_HEIGHT (ximagesink);
       
  1762       }
       
  1763     }
       
  1764   }
       
  1765 
       
  1766 alloc:
       
  1767   /* Inspect our buffer pool */
       
  1768   g_mutex_lock (ximagesink->pool_lock);
       
  1769   while (ximagesink->buffer_pool) {
       
  1770     ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
       
  1771 
       
  1772     if (ximage) {
       
  1773       /* Removing from the pool */
       
  1774       ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
       
  1775           ximagesink->buffer_pool);
       
  1776 
       
  1777       /* If the ximage is invalid for our need, destroy */
       
  1778       if ((ximage->width != width) || (ximage->height != height)) {
       
  1779         gst_ximage_buffer_free (ximage);
       
  1780         ximage = NULL;
       
  1781       } else {
       
  1782         /* We found a suitable ximage */
       
  1783         break;
       
  1784       }
       
  1785     }
       
  1786   }
       
  1787   g_mutex_unlock (ximagesink->pool_lock);
       
  1788 
       
  1789   /* We haven't found anything, creating a new one */
       
  1790   if (!ximage) {
       
  1791     ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
       
  1792   }
       
  1793   /* Now we should have a ximage, set appropriate caps on it */
       
  1794   if (ximage) {
       
  1795     gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
       
  1796   }
       
  1797 
       
  1798   /* could be our new reffed suggestion or the original unreffed caps */
       
  1799   if (alloc_unref)
       
  1800     gst_caps_unref (alloc_caps);
       
  1801 
       
  1802   *buf = GST_BUFFER_CAST (ximage);
       
  1803 
       
  1804   return ret;
       
  1805 }
       
  1806 
       
  1807 /* Interfaces stuff */
       
  1808 
       
  1809 static gboolean
       
  1810 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
       
  1811 {
       
  1812   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
       
  1813   return TRUE;
       
  1814 }
       
  1815 
       
  1816 static void
       
  1817 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
       
  1818 {
       
  1819   klass->supported = gst_ximagesink_interface_supported;
       
  1820 }
       
  1821 
       
  1822 static void
       
  1823 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
       
  1824     GstStructure * structure)
       
  1825 {
       
  1826   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
       
  1827   GstEvent *event;
       
  1828   gint x_offset, y_offset;
       
  1829   gdouble x, y;
       
  1830   GstPad *pad = NULL;
       
  1831 
       
  1832   event = gst_event_new_navigation (structure);
       
  1833 
       
  1834   /* We are not converting the pointer coordinates as there's no hardware
       
  1835      scaling done here. The only possible scaling is done by videoscale and
       
  1836      videoscale will have to catch those events and tranform the coordinates
       
  1837      to match the applied scaling. So here we just add the offset if the image
       
  1838      is centered in the window.  */
       
  1839 
       
  1840   /* We take the flow_lock while we look at the window */
       
  1841   g_mutex_lock (ximagesink->flow_lock);
       
  1842 
       
  1843   if (!ximagesink->xwindow) {
       
  1844     g_mutex_unlock (ximagesink->flow_lock);
       
  1845     return;
       
  1846   }
       
  1847 
       
  1848   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
       
  1849   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
       
  1850 
       
  1851   g_mutex_unlock (ximagesink->flow_lock);
       
  1852 
       
  1853   if (gst_structure_get_double (structure, "pointer_x", &x)) {
       
  1854     x -= x_offset / 2;
       
  1855     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
       
  1856   }
       
  1857   if (gst_structure_get_double (structure, "pointer_y", &y)) {
       
  1858     y -= y_offset / 2;
       
  1859     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
       
  1860   }
       
  1861 
       
  1862   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
       
  1863 
       
  1864   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
       
  1865     gst_pad_send_event (pad, event);
       
  1866 
       
  1867     gst_object_unref (pad);
       
  1868   }
       
  1869 }
       
  1870 
       
  1871 static void
       
  1872 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
       
  1873 {
       
  1874   iface->send_event = gst_ximagesink_navigation_send_event;
       
  1875 }
       
  1876 
       
  1877 static void
       
  1878 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
       
  1879 {
       
  1880   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
       
  1881   GstXWindow *xwindow = NULL;
       
  1882   XWindowAttributes attr;
       
  1883 
       
  1884   /* We acquire the stream lock while setting this window in the element.
       
  1885      We are basically cleaning tons of stuff replacing the old window, putting
       
  1886      images while we do that would surely crash */
       
  1887   g_mutex_lock (ximagesink->flow_lock);
       
  1888 
       
  1889   /* If we already use that window return */
       
  1890   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
       
  1891     g_mutex_unlock (ximagesink->flow_lock);
       
  1892     return;
       
  1893   }
       
  1894 
       
  1895   /* If the element has not initialized the X11 context try to do so */
       
  1896   if (!ximagesink->xcontext &&
       
  1897       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
       
  1898     g_mutex_unlock (ximagesink->flow_lock);
       
  1899     /* we have thrown a GST_ELEMENT_ERROR now */
       
  1900     return;
       
  1901   }
       
  1902 
       
  1903   /* If a window is there already we destroy it */
       
  1904   if (ximagesink->xwindow) {
       
  1905     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
       
  1906     ximagesink->xwindow = NULL;
       
  1907   }
       
  1908 
       
  1909   /* If the xid is 0 we go back to an internal window */
       
  1910   if (xwindow_id == 0) {
       
  1911     /* If no width/height caps nego did not happen window will be created
       
  1912        during caps nego then */
       
  1913     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
       
  1914       xwindow = gst_ximagesink_xwindow_new (ximagesink,
       
  1915           GST_VIDEO_SINK_WIDTH (ximagesink),
       
  1916           GST_VIDEO_SINK_HEIGHT (ximagesink));
       
  1917     }
       
  1918   } else {
       
  1919     xwindow = g_new0 (GstXWindow, 1);
       
  1920 
       
  1921     xwindow->win = xwindow_id;
       
  1922 
       
  1923     /* We get window geometry, set the event we want to receive,
       
  1924        and create a GC */
       
  1925     g_mutex_lock (ximagesink->x_lock);
       
  1926     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
       
  1927     xwindow->width = attr.width;
       
  1928     xwindow->height = attr.height;
       
  1929     xwindow->internal = FALSE;
       
  1930     if (ximagesink->handle_events) {
       
  1931       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
       
  1932           StructureNotifyMask | PointerMotionMask | KeyPressMask |
       
  1933           KeyReleaseMask);
       
  1934     }
       
  1935 
       
  1936     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
       
  1937     g_mutex_unlock (ximagesink->x_lock);
       
  1938   }
       
  1939 
       
  1940   if (xwindow)
       
  1941     ximagesink->xwindow = xwindow;
       
  1942 
       
  1943   g_mutex_unlock (ximagesink->flow_lock);
       
  1944 }
       
  1945 
       
  1946 static void
       
  1947 gst_ximagesink_expose (GstXOverlay * overlay)
       
  1948 {
       
  1949   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
       
  1950 
       
  1951   gst_ximagesink_ximage_put (ximagesink, NULL);
       
  1952 }
       
  1953 
       
  1954 static void
       
  1955 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
       
  1956     gboolean handle_events)
       
  1957 {
       
  1958   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
       
  1959 
       
  1960   ximagesink->handle_events = handle_events;
       
  1961 
       
  1962   g_mutex_lock (ximagesink->flow_lock);
       
  1963 
       
  1964   if (G_UNLIKELY (!ximagesink->xwindow)) {
       
  1965     g_mutex_unlock (ximagesink->flow_lock);
       
  1966     return;
       
  1967   }
       
  1968 
       
  1969   g_mutex_lock (ximagesink->x_lock);
       
  1970 
       
  1971   if (handle_events) {
       
  1972     if (ximagesink->xwindow->internal) {
       
  1973       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
       
  1974           ExposureMask | StructureNotifyMask | PointerMotionMask |
       
  1975           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
       
  1976     } else {
       
  1977       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
       
  1978           ExposureMask | StructureNotifyMask | PointerMotionMask |
       
  1979           KeyPressMask | KeyReleaseMask);
       
  1980     }
       
  1981   } else {
       
  1982     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
       
  1983   }
       
  1984 
       
  1985   g_mutex_unlock (ximagesink->x_lock);
       
  1986 
       
  1987   g_mutex_unlock (ximagesink->flow_lock);
       
  1988 }
       
  1989 
       
  1990 static void
       
  1991 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
       
  1992 {
       
  1993   iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
       
  1994   iface->expose = gst_ximagesink_expose;
       
  1995   iface->handle_events = gst_ximagesink_set_event_handling;
       
  1996 }
       
  1997 
       
  1998 /* =========================================== */
       
  1999 /*                                             */
       
  2000 /*              Init & Class init              */
       
  2001 /*                                             */
       
  2002 /* =========================================== */
       
  2003 
       
  2004 static void
       
  2005 gst_ximagesink_set_property (GObject * object, guint prop_id,
       
  2006     const GValue * value, GParamSpec * pspec)
       
  2007 {
       
  2008   GstXImageSink *ximagesink;
       
  2009 
       
  2010   g_return_if_fail (GST_IS_XIMAGESINK (object));
       
  2011 
       
  2012   ximagesink = GST_XIMAGESINK (object);
       
  2013 
       
  2014   switch (prop_id) {
       
  2015     case PROP_DISPLAY:
       
  2016       ximagesink->display_name = g_strdup (g_value_get_string (value));
       
  2017       break;
       
  2018     case PROP_SYNCHRONOUS:
       
  2019       ximagesink->synchronous = g_value_get_boolean (value);
       
  2020       if (ximagesink->xcontext) {
       
  2021         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
       
  2022             ximagesink->synchronous ? "TRUE" : "FALSE");
       
  2023         g_mutex_lock (ximagesink->x_lock);
       
  2024         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
       
  2025         g_mutex_unlock (ximagesink->x_lock);
       
  2026       }
       
  2027       break;
       
  2028     case PROP_FORCE_ASPECT_RATIO:
       
  2029       ximagesink->keep_aspect = g_value_get_boolean (value);
       
  2030       break;
       
  2031     case PROP_PIXEL_ASPECT_RATIO:
       
  2032     {
       
  2033       GValue *tmp;
       
  2034 
       
  2035       tmp = g_new0 (GValue, 1);
       
  2036       g_value_init (tmp, GST_TYPE_FRACTION);
       
  2037 
       
  2038       if (!g_value_transform (value, tmp)) {
       
  2039         GST_WARNING_OBJECT (ximagesink,
       
  2040             "Could not transform string to aspect ratio");
       
  2041         g_free (tmp);
       
  2042       } else {
       
  2043         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
       
  2044             gst_value_get_fraction_numerator (tmp),
       
  2045             gst_value_get_fraction_denominator (tmp));
       
  2046         g_free (ximagesink->par);
       
  2047         ximagesink->par = tmp;
       
  2048       }
       
  2049     }
       
  2050       break;
       
  2051     case PROP_HANDLE_EVENTS:
       
  2052       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
       
  2053           g_value_get_boolean (value));
       
  2054       break;
       
  2055     case PROP_HANDLE_EXPOSE:
       
  2056       ximagesink->handle_expose = g_value_get_boolean (value);
       
  2057       break;
       
  2058     default:
       
  2059       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
  2060       break;
       
  2061   }
       
  2062 }
       
  2063 
       
  2064 static void
       
  2065 gst_ximagesink_get_property (GObject * object, guint prop_id,
       
  2066     GValue * value, GParamSpec * pspec)
       
  2067 {
       
  2068   GstXImageSink *ximagesink;
       
  2069 
       
  2070   g_return_if_fail (GST_IS_XIMAGESINK (object));
       
  2071 
       
  2072   ximagesink = GST_XIMAGESINK (object);
       
  2073 
       
  2074   switch (prop_id) {
       
  2075     case PROP_DISPLAY:
       
  2076       g_value_set_string (value, ximagesink->display_name);
       
  2077       break;
       
  2078     case PROP_SYNCHRONOUS:
       
  2079       g_value_set_boolean (value, ximagesink->synchronous);
       
  2080       break;
       
  2081     case PROP_FORCE_ASPECT_RATIO:
       
  2082       g_value_set_boolean (value, ximagesink->keep_aspect);
       
  2083       break;
       
  2084     case PROP_PIXEL_ASPECT_RATIO:
       
  2085       if (ximagesink->par)
       
  2086         g_value_transform (ximagesink->par, value);
       
  2087       break;
       
  2088     case PROP_HANDLE_EVENTS:
       
  2089       g_value_set_boolean (value, ximagesink->handle_events);
       
  2090       break;
       
  2091     case PROP_HANDLE_EXPOSE:
       
  2092       g_value_set_boolean (value, ximagesink->handle_expose);
       
  2093       break;
       
  2094     default:
       
  2095       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
  2096       break;
       
  2097   }
       
  2098 }
       
  2099 
       
  2100 static void
       
  2101 gst_ximagesink_reset (GstXImageSink * ximagesink)
       
  2102 {
       
  2103   GThread *thread;
       
  2104 
       
  2105   GST_OBJECT_LOCK (ximagesink);
       
  2106   ximagesink->running = FALSE;
       
  2107   /* grab thread and mark it as NULL */
       
  2108   thread = ximagesink->event_thread;
       
  2109   ximagesink->event_thread = NULL;
       
  2110   GST_OBJECT_UNLOCK (ximagesink);
       
  2111 
       
  2112   /* Wait for our event thread to finish before we clean up our stuff. */
       
  2113   if (thread)
       
  2114     g_thread_join (thread);
       
  2115 
       
  2116   if (ximagesink->ximage) {
       
  2117     gst_buffer_unref (ximagesink->ximage);
       
  2118     ximagesink->ximage = NULL;
       
  2119   }
       
  2120   if (ximagesink->cur_image) {
       
  2121     gst_buffer_unref (ximagesink->cur_image);
       
  2122     ximagesink->cur_image = NULL;
       
  2123   }
       
  2124 
       
  2125   gst_ximagesink_bufferpool_clear (ximagesink);
       
  2126 
       
  2127   g_mutex_lock (ximagesink->flow_lock);
       
  2128   if (ximagesink->xwindow) {
       
  2129     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
       
  2130     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
       
  2131     ximagesink->xwindow = NULL;
       
  2132   }
       
  2133   g_mutex_unlock (ximagesink->flow_lock);
       
  2134 
       
  2135   gst_ximagesink_xcontext_clear (ximagesink);
       
  2136 }
       
  2137 
       
  2138 static void
       
  2139 gst_ximagesink_finalize (GObject * object)
       
  2140 {
       
  2141   GstXImageSink *ximagesink;
       
  2142 
       
  2143   ximagesink = GST_XIMAGESINK (object);
       
  2144 
       
  2145   gst_ximagesink_reset (ximagesink);
       
  2146 
       
  2147   if (ximagesink->display_name) {
       
  2148     g_free (ximagesink->display_name);
       
  2149     ximagesink->display_name = NULL;
       
  2150   }
       
  2151   if (ximagesink->par) {
       
  2152     g_free (ximagesink->par);
       
  2153     ximagesink->par = NULL;
       
  2154   }
       
  2155   if (ximagesink->x_lock) {
       
  2156     g_mutex_free (ximagesink->x_lock);
       
  2157     ximagesink->x_lock = NULL;
       
  2158   }
       
  2159   if (ximagesink->flow_lock) {
       
  2160     g_mutex_free (ximagesink->flow_lock);
       
  2161     ximagesink->flow_lock = NULL;
       
  2162   }
       
  2163   if (ximagesink->pool_lock) {
       
  2164     g_mutex_free (ximagesink->pool_lock);
       
  2165     ximagesink->pool_lock = NULL;
       
  2166   }
       
  2167 
       
  2168   G_OBJECT_CLASS (parent_class)->finalize (object);
       
  2169 }
       
  2170 
       
  2171 static void
       
  2172 gst_ximagesink_init (GstXImageSink * ximagesink)
       
  2173 {
       
  2174   ximagesink->display_name = NULL;
       
  2175   ximagesink->xcontext = NULL;
       
  2176   ximagesink->xwindow = NULL;
       
  2177   ximagesink->ximage = NULL;
       
  2178   ximagesink->cur_image = NULL;
       
  2179 
       
  2180   ximagesink->event_thread = NULL;
       
  2181   ximagesink->running = FALSE;
       
  2182 
       
  2183   ximagesink->fps_n = 0;
       
  2184   ximagesink->fps_d = 1;
       
  2185 
       
  2186   ximagesink->x_lock = g_mutex_new ();
       
  2187   ximagesink->flow_lock = g_mutex_new ();
       
  2188 
       
  2189   ximagesink->par = NULL;
       
  2190 
       
  2191   ximagesink->pool_lock = g_mutex_new ();
       
  2192   ximagesink->buffer_pool = NULL;
       
  2193 
       
  2194   ximagesink->synchronous = FALSE;
       
  2195   ximagesink->keep_aspect = FALSE;
       
  2196   ximagesink->handle_events = TRUE;
       
  2197   ximagesink->handle_expose = TRUE;
       
  2198 }
       
  2199 
       
  2200 static void
       
  2201 gst_ximagesink_base_init (gpointer g_class)
       
  2202 {
       
  2203   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
  2204 
       
  2205   gst_element_class_set_details (element_class, &gst_ximagesink_details);
       
  2206 
       
  2207   gst_element_class_add_pad_template (element_class,
       
  2208       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
       
  2209 }
       
  2210 
       
  2211 static void
       
  2212 gst_ximagesink_class_init (GstXImageSinkClass * klass)
       
  2213 {
       
  2214   GObjectClass *gobject_class;
       
  2215   GstElementClass *gstelement_class;
       
  2216   GstBaseSinkClass *gstbasesink_class;
       
  2217 
       
  2218   gobject_class = (GObjectClass *) klass;
       
  2219   gstelement_class = (GstElementClass *) klass;
       
  2220   gstbasesink_class = (GstBaseSinkClass *) klass;
       
  2221 
       
  2222   parent_class = g_type_class_peek_parent (klass);
       
  2223 
       
  2224   gobject_class->finalize = gst_ximagesink_finalize;
       
  2225   gobject_class->set_property = gst_ximagesink_set_property;
       
  2226   gobject_class->get_property = gst_ximagesink_get_property;
       
  2227 
       
  2228   g_object_class_install_property (gobject_class, PROP_DISPLAY,
       
  2229       g_param_spec_string ("display", "Display", "X Display name",
       
  2230           NULL, G_PARAM_READWRITE));
       
  2231   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
       
  2232       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
       
  2233           "the X display in synchronous mode. (used only for debugging)", FALSE,
       
  2234           G_PARAM_READWRITE));
       
  2235   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
       
  2236       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
       
  2237           "When enabled, reverse caps negotiation (scaling) will respect "
       
  2238           "original aspect ratio", FALSE, G_PARAM_READWRITE));
       
  2239   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
       
  2240       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
       
  2241           "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
       
  2242   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
       
  2243       g_param_spec_boolean ("handle-events", "Handle XEvents",
       
  2244           "When enabled, XEvents will be selected and handled", TRUE,
       
  2245           G_PARAM_READWRITE));
       
  2246   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
       
  2247       g_param_spec_boolean ("handle-expose", "Handle expose", "When enabled, "
       
  2248           "the current frame will always be drawn in response to X Expose "
       
  2249           "events", TRUE, G_PARAM_READWRITE));
       
  2250 
       
  2251   gstelement_class->change_state = gst_ximagesink_change_state;
       
  2252 
       
  2253   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
       
  2254   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
       
  2255   gstbasesink_class->buffer_alloc =
       
  2256       GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
       
  2257   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
       
  2258   gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
       
  2259   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
       
  2260 }
       
  2261 
       
  2262 /* ============================================================= */
       
  2263 /*                                                               */
       
  2264 /*                       Public Methods                          */
       
  2265 /*                                                               */
       
  2266 /* ============================================================= */
       
  2267 
       
  2268 /* =========================================== */
       
  2269 /*                                             */
       
  2270 /*          Object typing & Creation           */
       
  2271 /*                                             */
       
  2272 /* =========================================== */
       
  2273 
       
  2274 GType
       
  2275 gst_ximagesink_get_type (void)
       
  2276 {
       
  2277   static GType ximagesink_type = 0;
       
  2278 
       
  2279   if (!ximagesink_type) {
       
  2280     static const GTypeInfo ximagesink_info = {
       
  2281       sizeof (GstXImageSinkClass),
       
  2282       gst_ximagesink_base_init,
       
  2283       NULL,
       
  2284       (GClassInitFunc) gst_ximagesink_class_init,
       
  2285       NULL,
       
  2286       NULL,
       
  2287       sizeof (GstXImageSink),
       
  2288       0,
       
  2289       (GInstanceInitFunc) gst_ximagesink_init,
       
  2290     };
       
  2291     static const GInterfaceInfo iface_info = {
       
  2292       (GInterfaceInitFunc) gst_ximagesink_interface_init,
       
  2293       NULL,
       
  2294       NULL,
       
  2295     };
       
  2296     static const GInterfaceInfo navigation_info = {
       
  2297       (GInterfaceInitFunc) gst_ximagesink_navigation_init,
       
  2298       NULL,
       
  2299       NULL,
       
  2300     };
       
  2301     static const GInterfaceInfo overlay_info = {
       
  2302       (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
       
  2303       NULL,
       
  2304       NULL,
       
  2305     };
       
  2306 
       
  2307     ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
       
  2308         "GstXImageSink", &ximagesink_info, 0);
       
  2309 
       
  2310     g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
       
  2311         &iface_info);
       
  2312     g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
       
  2313         &navigation_info);
       
  2314     g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
       
  2315         &overlay_info);
       
  2316 
       
  2317     /* register type and create class in a more safe place instead of at
       
  2318      * runtime since the type registration and class creation is not
       
  2319      * threadsafe. */
       
  2320     g_type_class_ref (gst_ximage_buffer_get_type ());
       
  2321   }
       
  2322 
       
  2323   return ximagesink_type;
       
  2324 }