• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-ximagesink
22  * @title: ximagesink
23  *
24  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
25  * display. This element can receive a Window ID from the application through
26  * the #GstVideoOverlay interface and will then render video frames in this
27  * drawable. If no Window ID was provided by the application, the element will
28  * create its own internal window and render into it.
29  *
30  * ## Scaling
31  *
32  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33  * reverse caps negotiation to try to get scaled video frames for the drawable.
34  * This is accomplished by asking the peer pad if it accepts some different caps
35  * which in most cases implies that there is a scaling element in the pipeline,
36  * or that an element generating the video frames can generate them with a
37  * different geometry. This mechanism is handled during buffer allocations, for
38  * each allocation request the video sink will check the drawable geometry, look
39  * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40  * desired video frames and then check that the peer pad accept those new caps.
41  * If it does it will then allocate a buffer in video memory with this new
42  * geometry and return it with the new caps.
43  *
44  * ## Events
45  *
46  * XImageSink creates a thread to handle events coming from the drawable. There
47  * are several kind of events that can be grouped in 2 big categories: input
48  * events and window state related events. Input events will be translated to
49  * navigation events and pushed upstream for other elements to react on them.
50  * This includes events such as pointer moves, key press/release, clicks etc...
51  * Other events are used to handle the drawable appearance even when the data
52  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53  * paused, it will receive expose events from the drawable and draw the latest
54  * frame with correct borders/aspect-ratio.
55  *
56  * ## Pixel aspect ratio
57  *
58  * When changing state to GST_STATE_READY, XImageSink will open a connection to
59  * the display specified in the #GstXImageSink:display property or the default
60  * display if nothing specified. Once this connection is open it will inspect
61  * the display configuration including the physical display geometry and
62  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
63  * video sink will set the calculated pixel aspect ratio on the caps to make
64  * sure that incoming video frames will have the correct pixel aspect ratio for
65  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
66  * then possible to enforce a specific pixel aspect ratio using the
67  * #GstXImageSink:pixel-aspect-ratio property.
68  *
69  * ## Examples
70  * |[
71  * gst-launch-1.0 -v videotestsrc ! queue ! ximagesink
72  * ]|
73  *  A pipeline to test reverse negotiation. When the test video signal appears
74  * you can resize the window and see that scaled buffers of the desired size are
75  * going to arrive with a short delay. This illustrates how buffers of desired
76  * size are allocated along the way. If you take away the queue, scaling will
77  * happen almost immediately.
78  * |[
79  * gst-launch-1.0 -v videotestsrc ! navigationtest ! videoconvert ! ximagesink
80  * ]|
81  *  A pipeline to test navigation events.
82  * While moving the mouse pointer over the test signal you will see a black box
83  * following the mouse pointer. If you press the mouse button somewhere on the
84  * video and release it somewhere else a green box will appear where you pressed
85  * the button and a red one where you released it. (The navigationtest element
86  * is part of gst-plugins-good.)
87  * |[
88  * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
89  * ]|
90  *  This is faking a 4/3 pixel aspect ratio caps on video frames produced by
91  * videotestsrc, in most cases the pixel aspect ratio of the display will be
92  * 1/1. This means that videoscale will have to do the scaling to convert
93  * incoming frames to a size that will match the display pixel aspect ratio
94  * (from 320x240 to 320x180 in this case). Note that you might have to escape
95  * some characters for your shell like '\(fraction\)'.
96  *
97  */
98 
99 #ifdef HAVE_CONFIG_H
100 #include "config.h"
101 #endif
102 
103 /* Our interfaces */
104 #include <gst/video/navigation.h>
105 #include <gst/video/videooverlay.h>
106 
107 #include <gst/video/gstvideometa.h>
108 
109 /* Object header */
110 #include "ximagesink.h"
111 
112 /* Debugging category */
113 #include <gst/gstinfo.h>
114 
115 /* for XkbKeycodeToKeysym */
116 #include <X11/XKBlib.h>
117 
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_x_image_pool);
119 GST_DEBUG_CATEGORY (gst_debug_x_image_sink);
120 GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
121 
122 #define GST_CAT_DEFAULT gst_debug_x_image_sink
123 
124 typedef struct
125 {
126   unsigned long flags;
127   unsigned long functions;
128   unsigned long decorations;
129   long input_mode;
130   unsigned long status;
131 }
132 MotifWmHints, MwmHints;
133 
134 #define MWM_HINTS_DECORATIONS   (1L << 1)
135 
136 static void gst_x_image_sink_reset (GstXImageSink * ximagesink);
137 static void gst_x_image_sink_xwindow_update_geometry (GstXImageSink *
138     ximagesink);
139 static void gst_x_image_sink_expose (GstVideoOverlay * overlay);
140 
141 static GstStaticPadTemplate gst_x_image_sink_sink_template_factory =
142 GST_STATIC_PAD_TEMPLATE ("sink",
143     GST_PAD_SINK,
144     GST_PAD_ALWAYS,
145     GST_STATIC_CAPS ("video/x-raw, "
146         "framerate = (fraction) [ 0, MAX ], "
147         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
148     );
149 
150 enum
151 {
152   PROP_0,
153   PROP_DISPLAY,
154   PROP_SYNCHRONOUS,
155   PROP_PIXEL_ASPECT_RATIO,
156   PROP_FORCE_ASPECT_RATIO,
157   PROP_HANDLE_EVENTS,
158   PROP_HANDLE_EXPOSE,
159   PROP_WINDOW_WIDTH,
160   PROP_WINDOW_HEIGHT
161 };
162 
163 /* ============================================================= */
164 /*                                                               */
165 /*                       Public Methods                          */
166 /*                                                               */
167 /* ============================================================= */
168 
169 /* =========================================== */
170 /*                                             */
171 /*          Object typing & Creation           */
172 /*                                             */
173 /* =========================================== */
174 static void gst_x_image_sink_navigation_init (GstNavigationInterface * iface);
175 static void gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface *
176     iface);
177 #define gst_x_image_sink_parent_class parent_class
178 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_x_image_sink, GST_TYPE_VIDEO_SINK,
179     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
180         gst_x_image_sink_navigation_init);
181     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
182         gst_x_image_sink_video_overlay_init));
183 
184 #define _do_init \
185   GST_DEBUG_CATEGORY_INIT (gst_debug_x_image_sink, "ximagesink", 0, "ximagesink element");\
186   GST_DEBUG_CATEGORY_INIT (gst_debug_x_image_pool, "ximagepool", 0, "ximagepool object");\
187   GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
188 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (ximagesink, "ximagesink",
189     GST_RANK_SECONDARY, GST_TYPE_X_IMAGE_SINK, _do_init);
190 
191 /* ============================================================= */
192 /*                                                               */
193 /*                       Private Methods                         */
194 /*                                                               */
195 /* ============================================================= */
196 
197 /* X11 stuff */
198 
199 /* We are called with the x_lock taken */
200 static void
gst_x_image_sink_xwindow_draw_borders(GstXImageSink * ximagesink,GstXWindow * xwindow,GstVideoRectangle rect)201 gst_x_image_sink_xwindow_draw_borders (GstXImageSink * ximagesink,
202     GstXWindow * xwindow, GstVideoRectangle rect)
203 {
204   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
205   g_return_if_fail (xwindow != NULL);
206 
207   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
208       ximagesink->xcontext->black);
209 
210   /* Left border */
211   if (rect.x > 0) {
212     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
213         0, 0, rect.x, xwindow->height);
214   }
215 
216   /* Right border */
217   if ((rect.x + rect.w) < xwindow->width) {
218     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
219         rect.x + rect.w, 0, xwindow->width, xwindow->height);
220   }
221 
222   /* Top border */
223   if (rect.y > 0) {
224     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
225         0, 0, xwindow->width, rect.y);
226   }
227 
228   /* Bottom border */
229   if ((rect.y + rect.h) < xwindow->height) {
230     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
231         0, rect.y + rect.h, xwindow->width, xwindow->height);
232   }
233 }
234 
235 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
236 static gboolean
gst_x_image_sink_ximage_put(GstXImageSink * ximagesink,GstBuffer * ximage)237 gst_x_image_sink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
238 {
239   GstXImageMemory *mem;
240   GstVideoCropMeta *crop;
241   GstVideoRectangle src = { 0, };
242   GstVideoRectangle dst = { 0, };
243   GstVideoRectangle result;
244   gboolean draw_border = FALSE;
245 
246   /* We take the flow_lock. If expose is in there we don't want to run
247      concurrently from the data flow thread */
248   g_mutex_lock (&ximagesink->flow_lock);
249 
250   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
251     g_mutex_unlock (&ximagesink->flow_lock);
252     return FALSE;
253   }
254 
255   /* Draw borders when displaying the first frame. After this
256      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
257   if (!ximagesink->cur_image || ximagesink->draw_border) {
258     draw_border = TRUE;
259   }
260 
261   /* Store a reference to the last image we put, lose the previous one */
262   if (ximage && ximagesink->cur_image != ximage) {
263     if (ximagesink->cur_image) {
264       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
265       gst_buffer_unref (ximagesink->cur_image);
266     }
267     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
268     ximagesink->cur_image = gst_buffer_ref (ximage);
269   }
270 
271   /* Expose sends a NULL image, we take the latest frame */
272   if (!ximage) {
273     draw_border = TRUE;
274     if (ximagesink->cur_image) {
275       ximage = ximagesink->cur_image;
276     } else {
277       g_mutex_unlock (&ximagesink->flow_lock);
278       return TRUE;
279     }
280   }
281 
282   mem = (GstXImageMemory *) gst_buffer_peek_memory (ximage, 0);
283   crop = gst_buffer_get_video_crop_meta (ximage);
284 
285   if (crop) {
286     src.x = crop->x + mem->x;
287     src.y = crop->y + mem->y;
288     src.w = crop->width;
289     src.h = crop->height;
290     GST_LOG_OBJECT (ximagesink,
291         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
292   } else {
293     src.x = mem->x;
294     src.y = mem->y;
295     src.w = mem->width;
296     src.h = mem->height;
297   }
298   dst.w = ximagesink->xwindow->width;
299   dst.h = ximagesink->xwindow->height;
300 
301   gst_video_sink_center_rect (src, dst, &result, FALSE);
302 
303   g_mutex_lock (&ximagesink->x_lock);
304 
305   if (draw_border) {
306     gst_x_image_sink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
307         result);
308     ximagesink->draw_border = FALSE;
309   }
310 #ifdef HAVE_XSHM
311   if (ximagesink->xcontext->use_xshm) {
312     GST_LOG_OBJECT (ximagesink,
313         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
314         ximage, 0, 0, result.x, result.y, result.w, result.h,
315         ximagesink->xwindow->width, ximagesink->xwindow->height);
316     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
317         ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
318         result.w, result.h, FALSE);
319   } else
320 #endif /* HAVE_XSHM */
321   {
322     GST_LOG_OBJECT (ximagesink,
323         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
324         ximage, 0, 0, result.x, result.y, result.w, result.h,
325         ximagesink->xwindow->width, ximagesink->xwindow->height);
326     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
327         ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
328         result.w, result.h);
329   }
330 
331   XSync (ximagesink->xcontext->disp, FALSE);
332 
333   g_mutex_unlock (&ximagesink->x_lock);
334 
335   g_mutex_unlock (&ximagesink->flow_lock);
336 
337   return TRUE;
338 }
339 
340 static gboolean
gst_x_image_sink_xwindow_decorate(GstXImageSink * ximagesink,GstXWindow * window)341 gst_x_image_sink_xwindow_decorate (GstXImageSink * ximagesink,
342     GstXWindow * window)
343 {
344   Atom hints_atom = None;
345   MotifWmHints *hints;
346 
347   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), FALSE);
348   g_return_val_if_fail (window != NULL, FALSE);
349 
350   g_mutex_lock (&ximagesink->x_lock);
351 
352   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
353       True);
354   if (hints_atom == None) {
355     g_mutex_unlock (&ximagesink->x_lock);
356     return FALSE;
357   }
358 
359   hints = g_malloc0 (sizeof (MotifWmHints));
360 
361   hints->flags |= MWM_HINTS_DECORATIONS;
362   hints->decorations = 1 << 0;
363 
364   XChangeProperty (ximagesink->xcontext->disp, window->win,
365       hints_atom, hints_atom, 32, PropModeReplace,
366       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
367 
368   XSync (ximagesink->xcontext->disp, FALSE);
369 
370   g_mutex_unlock (&ximagesink->x_lock);
371 
372   g_free (hints);
373 
374   return TRUE;
375 }
376 
377 static void
gst_x_image_sink_xwindow_set_title(GstXImageSink * ximagesink,GstXWindow * xwindow,const gchar * media_title)378 gst_x_image_sink_xwindow_set_title (GstXImageSink * ximagesink,
379     GstXWindow * xwindow, const gchar * media_title)
380 {
381   if (media_title) {
382     g_free (ximagesink->media_title);
383     ximagesink->media_title = g_strdup (media_title);
384   }
385   if (xwindow) {
386     /* we have a window */
387     if (xwindow->internal) {
388       XTextProperty xproperty;
389       XClassHint *hint = XAllocClassHint ();
390       const gchar *app_name;
391       const gchar *title = NULL;
392       gchar *title_mem = NULL;
393 
394       /* set application name as a title */
395       app_name = g_get_application_name ();
396 
397       if (app_name && ximagesink->media_title) {
398         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
399             app_name, NULL);
400       } else if (app_name) {
401         title = app_name;
402       } else if (ximagesink->media_title) {
403         title = ximagesink->media_title;
404       }
405 
406       if (title) {
407         if ((XStringListToTextProperty (((char **) &title), 1,
408                     &xproperty)) != 0) {
409           Atom _NET_WM_NAME =
410               XInternAtom (ximagesink->xcontext->disp, "_NET_WM_NAME", 0);
411           Atom UTF8_STRING =
412               XInternAtom (ximagesink->xcontext->disp, "UTF8_STRING", 0);
413           XChangeProperty (ximagesink->xcontext->disp, xwindow->win,
414               _NET_WM_NAME, UTF8_STRING, 8, 0, (unsigned char *) title,
415               strlen (title));
416           XSync (ximagesink->xcontext->disp, False);
417 
418           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
419           XFree (xproperty.value);
420         }
421 
422         g_free (title_mem);
423       }
424 
425       if (hint) {
426         hint->res_name = (char *) app_name;
427         hint->res_class = (char *) "GStreamer";
428         XSetClassHint (ximagesink->xcontext->disp, xwindow->win, hint);
429       }
430       XFree (hint);
431     }
432   }
433 }
434 
435 /* This function handles a GstXWindow creation */
436 static GstXWindow *
gst_x_image_sink_xwindow_new(GstXImageSink * ximagesink,gint width,gint height)437 gst_x_image_sink_xwindow_new (GstXImageSink * ximagesink, gint width,
438     gint height)
439 {
440   GstXWindow *xwindow = NULL;
441   XGCValues values;
442 
443   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
444 
445   xwindow = g_new0 (GstXWindow, 1);
446 
447   xwindow->width = width;
448   xwindow->height = height;
449   xwindow->internal = TRUE;
450 
451   g_mutex_lock (&ximagesink->x_lock);
452 
453   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
454       ximagesink->xcontext->root,
455       0, 0, width, height, 0, 0, ximagesink->xcontext->black);
456 
457   /* We have to do that to prevent X from redrawing the background on
458      ConfigureNotify. This takes away flickering of video when resizing. */
459   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
460 
461   /* set application name as a title */
462   gst_x_image_sink_xwindow_set_title (ximagesink, xwindow, NULL);
463 
464   if (ximagesink->handle_events) {
465     Atom wm_delete;
466 
467     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
468         StructureNotifyMask | PointerMotionMask | KeyPressMask |
469         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
470 
471     /* Tell the window manager we'd like delete client messages instead of
472      * being killed */
473     wm_delete = XInternAtom (ximagesink->xcontext->disp,
474         "WM_DELETE_WINDOW", False);
475     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
476         &wm_delete, 1);
477   }
478 
479   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
480       0, &values);
481 
482   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
483 
484   XSync (ximagesink->xcontext->disp, FALSE);
485 
486   g_mutex_unlock (&ximagesink->x_lock);
487 
488   gst_x_image_sink_xwindow_decorate (ximagesink, xwindow);
489 
490   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
491       xwindow->win);
492 
493   return xwindow;
494 }
495 
496 /* This function destroys a GstXWindow */
497 static void
gst_x_image_sink_xwindow_destroy(GstXImageSink * ximagesink,GstXWindow * xwindow)498 gst_x_image_sink_xwindow_destroy (GstXImageSink * ximagesink,
499     GstXWindow * xwindow)
500 {
501   g_return_if_fail (xwindow != NULL);
502   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
503 
504   g_mutex_lock (&ximagesink->x_lock);
505 
506   /* If we did not create that window we just free the GC and let it live */
507   if (xwindow->internal)
508     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
509   else
510     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
511 
512   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
513 
514   XSync (ximagesink->xcontext->disp, FALSE);
515 
516   g_mutex_unlock (&ximagesink->x_lock);
517 
518   g_free (xwindow);
519 }
520 
521 static void
gst_x_image_sink_xwindow_update_geometry(GstXImageSink * ximagesink)522 gst_x_image_sink_xwindow_update_geometry (GstXImageSink * ximagesink)
523 {
524   XWindowAttributes attr;
525   gboolean reconfigure;
526 
527   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
528 
529   /* Update the window geometry */
530   g_mutex_lock (&ximagesink->x_lock);
531   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
532     g_mutex_unlock (&ximagesink->x_lock);
533     return;
534   }
535 
536   XGetWindowAttributes (ximagesink->xcontext->disp,
537       ximagesink->xwindow->win, &attr);
538 
539   /* Check if we would suggest a different width/height now */
540   reconfigure = (ximagesink->xwindow->width != attr.width)
541       || (ximagesink->xwindow->height != attr.height);
542   ximagesink->xwindow->width = attr.width;
543   ximagesink->xwindow->height = attr.height;
544 
545   g_mutex_unlock (&ximagesink->x_lock);
546 
547   if (reconfigure)
548     gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
549         gst_event_new_reconfigure ());
550 }
551 
552 static void
gst_x_image_sink_xwindow_clear(GstXImageSink * ximagesink,GstXWindow * xwindow)553 gst_x_image_sink_xwindow_clear (GstXImageSink * ximagesink,
554     GstXWindow * xwindow)
555 {
556   g_return_if_fail (xwindow != NULL);
557   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
558 
559   g_mutex_lock (&ximagesink->x_lock);
560 
561   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
562       ximagesink->xcontext->black);
563 
564   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
565       0, 0, xwindow->width, xwindow->height);
566 
567   XSync (ximagesink->xcontext->disp, FALSE);
568 
569   g_mutex_unlock (&ximagesink->x_lock);
570 }
571 
572 /* This function handles XEvents that might be in the queue. It generates
573    GstEvent that will be sent upstream in the pipeline to handle interactivity
574    and navigation.*/
575 static void
gst_x_image_sink_handle_xevents(GstXImageSink * ximagesink)576 gst_x_image_sink_handle_xevents (GstXImageSink * ximagesink)
577 {
578   XEvent e;
579   gint pointer_x = 0, pointer_y = 0;
580   gboolean pointer_moved = FALSE;
581   gboolean exposed = FALSE, configured = FALSE;
582 
583   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
584 
585   /* Then we get all pointer motion events, only the last position is
586      interesting. */
587   g_mutex_lock (&ximagesink->flow_lock);
588   g_mutex_lock (&ximagesink->x_lock);
589   while (XCheckWindowEvent (ximagesink->xcontext->disp,
590           ximagesink->xwindow->win, PointerMotionMask, &e)) {
591     g_mutex_unlock (&ximagesink->x_lock);
592     g_mutex_unlock (&ximagesink->flow_lock);
593 
594     switch (e.type) {
595       case MotionNotify:
596         pointer_x = e.xmotion.x;
597         pointer_y = e.xmotion.y;
598         pointer_moved = TRUE;
599         break;
600       default:
601         break;
602     }
603     g_mutex_lock (&ximagesink->flow_lock);
604     g_mutex_lock (&ximagesink->x_lock);
605   }
606 
607   if (pointer_moved) {
608     g_mutex_unlock (&ximagesink->x_lock);
609     g_mutex_unlock (&ximagesink->flow_lock);
610 
611     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
612         pointer_x, pointer_y);
613     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
614         "mouse-move", 0, pointer_x, pointer_y);
615 
616     g_mutex_lock (&ximagesink->flow_lock);
617     g_mutex_lock (&ximagesink->x_lock);
618   }
619 
620   /* We get all remaining events on our window to throw them upstream */
621   while (XCheckWindowEvent (ximagesink->xcontext->disp,
622           ximagesink->xwindow->win,
623           KeyPressMask | KeyReleaseMask |
624           ButtonPressMask | ButtonReleaseMask, &e)) {
625     KeySym keysym;
626     const char *key_str = NULL;
627 
628     /* We lock only for the X function call */
629     g_mutex_unlock (&ximagesink->x_lock);
630     g_mutex_unlock (&ximagesink->flow_lock);
631 
632     switch (e.type) {
633       case ButtonPress:
634         /* Mouse button pressed/released over our window. We send upstream
635            events for interactivity/navigation */
636         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
637             e.xbutton.button, e.xbutton.x, e.xbutton.x);
638         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
639             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
640         break;
641       case ButtonRelease:
642         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
643             e.xbutton.button, e.xbutton.x, e.xbutton.x);
644         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
645             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
646         break;
647       case KeyPress:
648       case KeyRelease:
649         /* Key pressed/released over our window. We send upstream
650            events for interactivity/navigation */
651         g_mutex_lock (&ximagesink->x_lock);
652         if (ximagesink->xcontext->use_xkb) {
653           keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
654               e.xkey.keycode, 0, 0);
655         } else {
656           keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
657               e.xkey.keycode, 0);
658         }
659         if (keysym != NoSymbol) {
660           key_str = XKeysymToString (keysym);
661         } else {
662           key_str = "unknown";
663         }
664         g_mutex_unlock (&ximagesink->x_lock);
665         GST_DEBUG_OBJECT (ximagesink,
666             "key %d pressed over window at %d,%d (%s)",
667             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
668         gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
669             e.type == KeyPress ? "key-press" : "key-release", key_str);
670         break;
671       default:
672         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
673             e.type);
674     }
675     g_mutex_lock (&ximagesink->flow_lock);
676     g_mutex_lock (&ximagesink->x_lock);
677   }
678 
679   /* Handle Expose */
680   while (XCheckWindowEvent (ximagesink->xcontext->disp,
681           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
682     switch (e.type) {
683       case Expose:
684         exposed = TRUE;
685         break;
686       case ConfigureNotify:
687         g_mutex_unlock (&ximagesink->x_lock);
688         gst_x_image_sink_xwindow_update_geometry (ximagesink);
689         g_mutex_lock (&ximagesink->x_lock);
690         configured = TRUE;
691         break;
692       default:
693         break;
694     }
695   }
696 
697   if (ximagesink->handle_expose && (exposed || configured)) {
698     g_mutex_unlock (&ximagesink->x_lock);
699     g_mutex_unlock (&ximagesink->flow_lock);
700 
701     gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
702 
703     g_mutex_lock (&ximagesink->flow_lock);
704     g_mutex_lock (&ximagesink->x_lock);
705   }
706 
707   /* Handle Display events */
708   while (XPending (ximagesink->xcontext->disp)) {
709     XNextEvent (ximagesink->xcontext->disp, &e);
710 
711     switch (e.type) {
712       case ClientMessage:{
713         Atom wm_delete;
714 
715         wm_delete = XInternAtom (ximagesink->xcontext->disp,
716             "WM_DELETE_WINDOW", False);
717         if (wm_delete == (Atom) e.xclient.data.l[0]) {
718           /* Handle window deletion by posting an error on the bus */
719           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
720               ("Output window was closed"), (NULL));
721 
722           g_mutex_unlock (&ximagesink->x_lock);
723           gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
724           ximagesink->xwindow = NULL;
725           g_mutex_lock (&ximagesink->x_lock);
726         }
727         break;
728       }
729       default:
730         break;
731     }
732   }
733 
734   g_mutex_unlock (&ximagesink->x_lock);
735   g_mutex_unlock (&ximagesink->flow_lock);
736 }
737 
738 static gpointer
gst_x_image_sink_event_thread(GstXImageSink * ximagesink)739 gst_x_image_sink_event_thread (GstXImageSink * ximagesink)
740 {
741   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
742 
743   GST_OBJECT_LOCK (ximagesink);
744   while (ximagesink->running) {
745     GST_OBJECT_UNLOCK (ximagesink);
746 
747     if (ximagesink->xwindow) {
748       gst_x_image_sink_handle_xevents (ximagesink);
749     }
750     /* FIXME: do we want to align this with the framerate or anything else? */
751     g_usleep (G_USEC_PER_SEC / 20);
752 
753     GST_OBJECT_LOCK (ximagesink);
754   }
755   GST_OBJECT_UNLOCK (ximagesink);
756 
757   return NULL;
758 }
759 
760 static void
gst_x_image_sink_manage_event_thread(GstXImageSink * ximagesink)761 gst_x_image_sink_manage_event_thread (GstXImageSink * ximagesink)
762 {
763   GThread *thread = NULL;
764 
765   /* don't start the thread too early */
766   if (ximagesink->xcontext == NULL) {
767     return;
768   }
769 
770   GST_OBJECT_LOCK (ximagesink);
771   if (ximagesink->handle_expose || ximagesink->handle_events) {
772     if (!ximagesink->event_thread) {
773       /* Setup our event listening thread */
774       GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
775           ximagesink->handle_expose, ximagesink->handle_events);
776       ximagesink->running = TRUE;
777       ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
778           (GThreadFunc) gst_x_image_sink_event_thread, ximagesink, NULL);
779     }
780   } else {
781     if (ximagesink->event_thread) {
782       GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
783           ximagesink->handle_expose, ximagesink->handle_events);
784       ximagesink->running = FALSE;
785       /* grab thread and mark it as NULL */
786       thread = ximagesink->event_thread;
787       ximagesink->event_thread = NULL;
788     }
789   }
790   GST_OBJECT_UNLOCK (ximagesink);
791 
792   /* Wait for our event thread to finish */
793   if (thread)
794     g_thread_join (thread);
795 
796 }
797 
798 
799 /* This function calculates the pixel aspect ratio based on the properties
800  * in the xcontext structure and stores it there. */
801 static void
gst_x_image_sink_calculate_pixel_aspect_ratio(GstXContext * xcontext)802 gst_x_image_sink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
803 {
804   static const gint par[][2] = {
805     {1, 1},                     /* regular screen */
806     {16, 15},                   /* PAL TV */
807     {11, 10},                   /* 525 line Rec.601 video */
808     {54, 59},                   /* 625 line Rec.601 video */
809     {64, 45},                   /* 1280x1024 on 16:9 display */
810     {5, 3},                     /* 1280x1024 on 4:3 display */
811     {4, 3}                      /*  800x600 on 16:9 display */
812   };
813   gint i;
814   gint index;
815   gdouble ratio;
816   gdouble delta;
817 
818 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
819 
820   /* first calculate the "real" ratio based on the X values;
821    * which is the "physical" w/h divided by the w/h in pixels of the display */
822   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
823       / (xcontext->heightmm * xcontext->width);
824 
825   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
826    * override here */
827   if (xcontext->width == 720 && xcontext->height == 576) {
828     ratio = 4.0 * 576 / (3.0 * 720);
829   }
830   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
831 
832   /* now find the one from par[][2] with the lowest delta to the real one */
833   delta = DELTA (0);
834   index = 0;
835 
836   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
837     gdouble this_delta = DELTA (i);
838 
839     if (this_delta < delta) {
840       index = i;
841       delta = this_delta;
842     }
843   }
844 
845   GST_DEBUG ("Decided on index %d (%d/%d)", index,
846       par[index][0], par[index][1]);
847 
848   g_free (xcontext->par);
849   xcontext->par = g_new0 (GValue, 1);
850   g_value_init (xcontext->par, GST_TYPE_FRACTION);
851   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
852   GST_DEBUG ("set xcontext PAR to %d/%d",
853       gst_value_get_fraction_numerator (xcontext->par),
854       gst_value_get_fraction_denominator (xcontext->par));
855 }
856 
857 /* This function gets the X Display and global info about it. Everything is
858    stored in our object and will be cleaned when the object is disposed. Note
859    here that caps for supported format are generated without any window or
860    image creation */
861 static GstXContext *
gst_x_image_sink_xcontext_get(GstXImageSink * ximagesink)862 gst_x_image_sink_xcontext_get (GstXImageSink * ximagesink)
863 {
864   GstXContext *xcontext = NULL;
865   XPixmapFormatValues *px_formats = NULL;
866   gint nb_formats = 0, i;
867   gint endianness;
868   GstVideoFormat vformat;
869   guint32 alpha_mask;
870   int opcode, event, err;
871   int major = XkbMajorVersion;
872   int minor = XkbMinorVersion;
873 
874   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
875 
876   xcontext = g_new0 (GstXContext, 1);
877 
878   g_mutex_lock (&ximagesink->x_lock);
879 
880   xcontext->disp = XOpenDisplay (ximagesink->display_name);
881 
882   if (!xcontext->disp) {
883     g_mutex_unlock (&ximagesink->x_lock);
884     g_free (xcontext);
885     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
886         ("Could not initialise X output"), ("Could not open display"));
887     return NULL;
888   }
889 
890   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
891   xcontext->screen_num = DefaultScreen (xcontext->disp);
892   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
893   xcontext->root = DefaultRootWindow (xcontext->disp);
894   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
895   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
896   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
897 
898   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
899   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
900   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
901   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
902 
903   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
904       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
905 
906   gst_x_image_sink_calculate_pixel_aspect_ratio (xcontext);
907 
908   /* We get supported pixmap formats at supported depth */
909   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
910 
911   if (!px_formats) {
912     XCloseDisplay (xcontext->disp);
913     g_mutex_unlock (&ximagesink->x_lock);
914     g_free (xcontext->par);
915     g_free (xcontext);
916     GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
917         ("Could not get supported pixmap formats"), (NULL));
918     return NULL;
919   }
920 
921   /* We get bpp value corresponding to our running depth */
922   for (i = 0; i < nb_formats; i++) {
923     if (px_formats[i].depth == xcontext->depth)
924       xcontext->bpp = px_formats[i].bits_per_pixel;
925   }
926 
927   XFree (px_formats);
928 
929   endianness = (ImageByteOrder (xcontext->disp) ==
930       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
931 
932   /* Search for XShm extension support */
933 #ifdef HAVE_XSHM
934   if (XShmQueryExtension (xcontext->disp) &&
935       gst_x_image_sink_check_xshm_calls (ximagesink, xcontext)) {
936     xcontext->use_xshm = TRUE;
937     GST_DEBUG ("ximagesink is using XShm extension");
938   } else
939 #endif /* HAVE_XSHM */
940   {
941     xcontext->use_xshm = FALSE;
942     GST_DEBUG ("ximagesink is not using XShm extension");
943   }
944   if (XkbQueryExtension (xcontext->disp, &opcode, &event, &err, &major, &minor)) {
945     xcontext->use_xkb = TRUE;
946     GST_DEBUG ("ximagesink is using Xkb extension");
947   } else {
948     xcontext->use_xkb = FALSE;
949     GST_DEBUG ("ximagesink is not using Xkb extension");
950   }
951 
952   /* extrapolate alpha mask */
953   if (xcontext->depth == 32) {
954     alpha_mask = ~(xcontext->visual->red_mask
955         | xcontext->visual->green_mask | xcontext->visual->blue_mask);
956   } else {
957     alpha_mask = 0;
958   }
959 
960   vformat =
961       gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
962       xcontext->visual->red_mask, xcontext->visual->green_mask,
963       xcontext->visual->blue_mask, alpha_mask);
964 
965   if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
966     goto unknown_format;
967 
968   /* update object's par with calculated one if not set yet */
969   if (!ximagesink->par) {
970     ximagesink->par = g_new0 (GValue, 1);
971     gst_value_init_and_copy (ximagesink->par, xcontext->par);
972     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
973   }
974   xcontext->caps = gst_caps_new_simple ("video/x-raw",
975       "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
976       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
977       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
978       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
979   if (ximagesink->par) {
980     int nom, den;
981 
982     nom = gst_value_get_fraction_numerator (ximagesink->par);
983     den = gst_value_get_fraction_denominator (ximagesink->par);
984     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
985         GST_TYPE_FRACTION, nom, den, NULL);
986   }
987 
988   g_mutex_unlock (&ximagesink->x_lock);
989 
990   return xcontext;
991 
992   /* ERRORS */
993 unknown_format:
994   {
995     GST_ERROR_OBJECT (ximagesink, "unknown format");
996     return NULL;
997   }
998 }
999 
1000 /* This function cleans the X context. Closing the Display and unrefing the
1001    caps for supported formats. */
1002 static void
gst_x_image_sink_xcontext_clear(GstXImageSink * ximagesink)1003 gst_x_image_sink_xcontext_clear (GstXImageSink * ximagesink)
1004 {
1005   GstXContext *xcontext;
1006 
1007   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1008 
1009   GST_OBJECT_LOCK (ximagesink);
1010   if (ximagesink->xcontext == NULL) {
1011     GST_OBJECT_UNLOCK (ximagesink);
1012     return;
1013   }
1014 
1015   /* Take the xcontext reference and NULL it while we
1016    * clean it up, so that any buffer-alloced buffers
1017    * arriving after this will be freed correctly */
1018   xcontext = ximagesink->xcontext;
1019   ximagesink->xcontext = NULL;
1020 
1021   GST_OBJECT_UNLOCK (ximagesink);
1022 
1023   gst_caps_unref (xcontext->caps);
1024   g_free (xcontext->par);
1025   g_free (ximagesink->par);
1026   ximagesink->par = NULL;
1027 
1028   if (xcontext->last_caps)
1029     gst_caps_replace (&xcontext->last_caps, NULL);
1030 
1031   g_mutex_lock (&ximagesink->x_lock);
1032 
1033   XCloseDisplay (xcontext->disp);
1034 
1035   g_mutex_unlock (&ximagesink->x_lock);
1036 
1037   g_free (xcontext);
1038 }
1039 
1040 /* Element stuff */
1041 
1042 static GstCaps *
gst_x_image_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)1043 gst_x_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1044 {
1045   GstXImageSink *ximagesink;
1046   GstCaps *caps;
1047   int i;
1048 
1049   ximagesink = GST_X_IMAGE_SINK (bsink);
1050 
1051   g_mutex_lock (&ximagesink->x_lock);
1052   if (ximagesink->xcontext) {
1053     GstCaps *caps;
1054 
1055     caps = gst_caps_ref (ximagesink->xcontext->caps);
1056 
1057     if (filter) {
1058       GstCaps *intersection;
1059 
1060       intersection =
1061           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1062       gst_caps_unref (caps);
1063       caps = intersection;
1064     }
1065 
1066     if (gst_caps_is_empty (caps)) {
1067       g_mutex_unlock (&ximagesink->x_lock);
1068       return caps;
1069     }
1070 
1071     if (ximagesink->xwindow && ximagesink->xwindow->width) {
1072       GstStructure *s0, *s1;
1073 
1074       caps = gst_caps_make_writable (caps);
1075 
1076       /* There can only be a single structure because the xcontext
1077        * caps only have a single structure */
1078       s0 = gst_caps_get_structure (caps, 0);
1079       s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1080 
1081       gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1082           "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1083       gst_caps_append_structure (caps, s1);
1084 
1085       /* This will not change the order but will remove the
1086        * fixed width/height caps again if not possible
1087        * upstream */
1088       if (filter) {
1089         GstCaps *intersection;
1090 
1091         intersection =
1092             gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1093         gst_caps_unref (caps);
1094         caps = intersection;
1095       }
1096     }
1097 
1098     g_mutex_unlock (&ximagesink->x_lock);
1099     return caps;
1100   }
1101   g_mutex_unlock (&ximagesink->x_lock);
1102 
1103   /* get a template copy and add the pixel aspect ratio */
1104   caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1105   if (ximagesink->par) {
1106     caps = gst_caps_make_writable (caps);
1107     for (i = 0; i < gst_caps_get_size (caps); ++i) {
1108       GstStructure *structure = gst_caps_get_structure (caps, i);
1109       int nom, den;
1110 
1111       nom = gst_value_get_fraction_numerator (ximagesink->par);
1112       den = gst_value_get_fraction_denominator (ximagesink->par);
1113       gst_structure_set (structure, "pixel-aspect-ratio",
1114           GST_TYPE_FRACTION, nom, den, NULL);
1115     }
1116   }
1117 
1118   if (filter) {
1119     GstCaps *intersection;
1120 
1121     intersection =
1122         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1123     gst_caps_unref (caps);
1124     caps = intersection;
1125   }
1126 
1127   return caps;
1128 }
1129 
1130 static GstBufferPool *
gst_x_image_sink_create_pool(GstXImageSink * ximagesink,GstCaps * caps,gsize size,gint min)1131 gst_x_image_sink_create_pool (GstXImageSink * ximagesink, GstCaps * caps,
1132     gsize size, gint min)
1133 {
1134   static GstAllocationParams params = { 0, 15, 0, 0, };
1135   GstBufferPool *pool;
1136   GstStructure *config;
1137 
1138   /* create a new pool for the new configuration */
1139   pool = gst_ximage_buffer_pool_new (ximagesink);
1140 
1141   config = gst_buffer_pool_get_config (pool);
1142   gst_buffer_pool_config_set_params (config, caps, size, min, 0);
1143   gst_buffer_pool_config_set_allocator (config, NULL, &params);
1144 
1145   if (!gst_buffer_pool_set_config (pool, config))
1146     goto config_failed;
1147 
1148   return pool;
1149 
1150 config_failed:
1151   {
1152     GST_WARNING_OBJECT (ximagesink, "failed setting config");
1153     gst_object_unref (pool);
1154     return NULL;
1155   }
1156 }
1157 
1158 static gboolean
gst_x_image_sink_setcaps(GstBaseSink * bsink,GstCaps * caps)1159 gst_x_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1160 {
1161   GstXImageSink *ximagesink;
1162   GstStructure *structure;
1163   GstVideoInfo info;
1164   GstBufferPool *newpool, *oldpool;
1165   const GValue *par;
1166 
1167   ximagesink = GST_X_IMAGE_SINK (bsink);
1168 
1169   if (!ximagesink->xcontext)
1170     return FALSE;
1171 
1172   GST_DEBUG_OBJECT (ximagesink,
1173       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1174       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1175 
1176   /* We intersect those caps with our template to make sure they are correct */
1177   if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1178     goto incompatible_caps;
1179 
1180   if (!gst_video_info_from_caps (&info, caps))
1181     goto invalid_format;
1182 
1183   structure = gst_caps_get_structure (caps, 0);
1184   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1185    * otherwise linking should fail */
1186   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1187   if (par) {
1188     if (ximagesink->par) {
1189       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1190         goto wrong_aspect;
1191       }
1192     } else if (ximagesink->xcontext->par) {
1193       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1194         goto wrong_aspect;
1195       }
1196     }
1197   }
1198 
1199   GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1200   GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1201   ximagesink->fps_n = info.fps_n;
1202   ximagesink->fps_d = info.fps_d;
1203 
1204   /* Notify application to set xwindow id now */
1205   g_mutex_lock (&ximagesink->flow_lock);
1206   if (!ximagesink->xwindow) {
1207     g_mutex_unlock (&ximagesink->flow_lock);
1208     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1209   } else {
1210     g_mutex_unlock (&ximagesink->flow_lock);
1211   }
1212 
1213   /* Creating our window and our image */
1214   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1215       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1216     goto invalid_size;
1217 
1218   g_mutex_lock (&ximagesink->flow_lock);
1219   if (!ximagesink->xwindow) {
1220     ximagesink->xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1221         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1222   }
1223 
1224   ximagesink->info = info;
1225 
1226   /* Remember to draw borders for next frame */
1227   ximagesink->draw_border = TRUE;
1228 
1229   /* create a new internal pool for the new configuration */
1230   newpool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 2);
1231 
1232   /* we don't activate the internal pool yet as it may not be needed */
1233   oldpool = ximagesink->pool;
1234   ximagesink->pool = newpool;
1235   g_mutex_unlock (&ximagesink->flow_lock);
1236 
1237   /* deactivate and unref the old internal pool */
1238   if (oldpool) {
1239     gst_buffer_pool_set_active (oldpool, FALSE);
1240     gst_object_unref (oldpool);
1241   }
1242 
1243   return TRUE;
1244 
1245   /* ERRORS */
1246 incompatible_caps:
1247   {
1248     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1249     return FALSE;
1250   }
1251 invalid_format:
1252   {
1253     GST_ERROR_OBJECT (ximagesink, "caps invalid");
1254     return FALSE;
1255   }
1256 wrong_aspect:
1257   {
1258     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1259     return FALSE;
1260   }
1261 invalid_size:
1262   {
1263     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1264         ("Invalid image size."));
1265     return FALSE;
1266   }
1267 }
1268 
1269 static GstStateChangeReturn
gst_x_image_sink_change_state(GstElement * element,GstStateChange transition)1270 gst_x_image_sink_change_state (GstElement * element, GstStateChange transition)
1271 {
1272   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1273   GstXImageSink *ximagesink;
1274   GstXContext *xcontext = NULL;
1275 
1276   ximagesink = GST_X_IMAGE_SINK (element);
1277 
1278   switch (transition) {
1279     case GST_STATE_CHANGE_NULL_TO_READY:
1280       /* Initializing the XContext */
1281       if (ximagesink->xcontext == NULL) {
1282         xcontext = gst_x_image_sink_xcontext_get (ximagesink);
1283         if (xcontext == NULL) {
1284           ret = GST_STATE_CHANGE_FAILURE;
1285           goto beach;
1286         }
1287         GST_OBJECT_LOCK (ximagesink);
1288         if (xcontext)
1289           ximagesink->xcontext = xcontext;
1290         GST_OBJECT_UNLOCK (ximagesink);
1291       }
1292 
1293       /* call XSynchronize with the current value of synchronous */
1294       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1295           ximagesink->synchronous ? "TRUE" : "FALSE");
1296       g_mutex_lock (&ximagesink->x_lock);
1297       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1298       g_mutex_unlock (&ximagesink->x_lock);
1299       gst_x_image_sink_manage_event_thread (ximagesink);
1300       break;
1301     case GST_STATE_CHANGE_READY_TO_PAUSED:
1302       g_mutex_lock (&ximagesink->flow_lock);
1303       if (ximagesink->xwindow)
1304         gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1305       g_mutex_unlock (&ximagesink->flow_lock);
1306       break;
1307     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1308       break;
1309     default:
1310       break;
1311   }
1312 
1313   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1314 
1315   switch (transition) {
1316     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1317       break;
1318     case GST_STATE_CHANGE_PAUSED_TO_READY:
1319       ximagesink->fps_n = 0;
1320       ximagesink->fps_d = 1;
1321       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1322       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1323       g_mutex_lock (&ximagesink->flow_lock);
1324       if (ximagesink->pool)
1325         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1326       g_mutex_unlock (&ximagesink->flow_lock);
1327       break;
1328     case GST_STATE_CHANGE_READY_TO_NULL:
1329       gst_x_image_sink_reset (ximagesink);
1330       break;
1331     default:
1332       break;
1333   }
1334 
1335 beach:
1336   return ret;
1337 }
1338 
1339 static void
gst_x_image_sink_get_times(GstBaseSink * bsink,GstBuffer * buf,GstClockTime * start,GstClockTime * end)1340 gst_x_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1341     GstClockTime * start, GstClockTime * end)
1342 {
1343   GstXImageSink *ximagesink;
1344 
1345   ximagesink = GST_X_IMAGE_SINK (bsink);
1346 
1347   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1348     *start = GST_BUFFER_TIMESTAMP (buf);
1349     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1350       *end = *start + GST_BUFFER_DURATION (buf);
1351     } else {
1352       if (ximagesink->fps_n > 0) {
1353         *end = *start +
1354             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1355             ximagesink->fps_n);
1356       }
1357     }
1358   }
1359 }
1360 
1361 static GstFlowReturn
gst_x_image_sink_show_frame(GstVideoSink * vsink,GstBuffer * buf)1362 gst_x_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1363 {
1364   GstFlowReturn res;
1365   GstXImageSink *ximagesink;
1366   GstXImageMemory *mem;
1367   GstBuffer *to_put = NULL;
1368 
1369   ximagesink = GST_X_IMAGE_SINK (vsink);
1370 
1371   if (gst_buffer_n_memory (buf) == 1
1372       && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1373       && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1374       && mem->sink == ximagesink) {
1375     /* If this buffer has been allocated using our buffer management we simply
1376        put the ximage which is in the PRIVATE pointer */
1377     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1378     to_put = buf;
1379     res = GST_FLOW_OK;
1380   } else {
1381     GstVideoFrame src, dest;
1382     GstBufferPoolAcquireParams params = { 0, };
1383 
1384     /* Else we have to copy the data into our private image, */
1385     /* if we have one... */
1386     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1387 
1388     /* an internal pool should have been created in setcaps */
1389     if (G_UNLIKELY (ximagesink->pool == NULL))
1390       goto no_pool;
1391 
1392     if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1393       goto activate_failed;
1394 
1395     /* take a buffer from our pool, if there is no buffer in the pool something
1396      * is seriously wrong, waiting for the pool here might deadlock when we try
1397      * to go to PAUSED because we never flush the pool. */
1398     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1399     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, &params);
1400     if (res != GST_FLOW_OK)
1401       goto no_buffer;
1402 
1403     GST_CAT_LOG_OBJECT (CAT_PERFORMANCE, ximagesink,
1404         "slow copy into bufferpool buffer %p", to_put);
1405 
1406     if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1407       goto invalid_buffer;
1408 
1409     if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1410       gst_video_frame_unmap (&src);
1411       goto invalid_buffer;
1412     }
1413 
1414     gst_video_frame_copy (&dest, &src);
1415 
1416     gst_video_frame_unmap (&dest);
1417     gst_video_frame_unmap (&src);
1418   }
1419 
1420   if (!gst_x_image_sink_ximage_put (ximagesink, to_put))
1421     goto no_window;
1422 
1423 done:
1424   if (to_put != buf)
1425     gst_buffer_unref (to_put);
1426 
1427   return res;
1428 
1429   /* ERRORS */
1430 no_pool:
1431   {
1432     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1433         ("Internal error: can't allocate images"),
1434         ("We don't have a bufferpool negotiated"));
1435     return GST_FLOW_ERROR;
1436   }
1437 no_buffer:
1438   {
1439     /* No image available. That's very bad ! */
1440     GST_WARNING_OBJECT (ximagesink, "could not create image");
1441     return GST_FLOW_OK;
1442   }
1443 invalid_buffer:
1444   {
1445     /* No Window available to put our image into */
1446     GST_WARNING_OBJECT (ximagesink, "could not map image");
1447     res = GST_FLOW_OK;
1448     goto done;
1449   }
1450 no_window:
1451   {
1452     /* No Window available to put our image into */
1453     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1454     res = GST_FLOW_ERROR;
1455     goto done;
1456   }
1457 activate_failed:
1458   {
1459     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1460     res = GST_FLOW_ERROR;
1461     goto done;
1462   }
1463 }
1464 
1465 static gboolean
gst_x_image_sink_event(GstBaseSink * sink,GstEvent * event)1466 gst_x_image_sink_event (GstBaseSink * sink, GstEvent * event)
1467 {
1468   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (sink);
1469 
1470   switch (GST_EVENT_TYPE (event)) {
1471     case GST_EVENT_TAG:{
1472       GstTagList *l;
1473       gchar *title = NULL;
1474 
1475       gst_event_parse_tag (event, &l);
1476       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1477 
1478       if (title) {
1479         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1480         gst_x_image_sink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1481             title);
1482 
1483         g_free (title);
1484       }
1485       break;
1486     }
1487     default:
1488       break;
1489   }
1490   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1491 }
1492 
1493 static gboolean
gst_x_image_sink_propose_allocation(GstBaseSink * bsink,GstQuery * query)1494 gst_x_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1495 {
1496   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (bsink);
1497   GstBufferPool *pool = NULL;
1498   GstCaps *caps;
1499   GstVideoInfo info;
1500   guint size;
1501   gboolean need_pool;
1502 
1503   gst_query_parse_allocation (query, &caps, &need_pool);
1504 
1505   if (caps == NULL)
1506     goto no_caps;
1507 
1508   if (!gst_video_info_from_caps (&info, caps))
1509     goto invalid_caps;
1510 
1511   /* the normal size of a frame */
1512   size = info.size;
1513 
1514   if (need_pool) {
1515     pool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 0);
1516 
1517     if (pool == NULL)
1518       goto no_pool;
1519   }
1520 
1521   /* we need at least 2 buffer because we hold on to the last one */
1522   gst_query_add_allocation_pool (query, pool, size, 2, 0);
1523   if (pool)
1524     gst_object_unref (pool);
1525 
1526   /* we also support various metadata */
1527   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1528   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1529 
1530   return TRUE;
1531 
1532   /* ERRORS */
1533 no_caps:
1534   {
1535     GST_DEBUG_OBJECT (bsink, "no caps specified");
1536     return FALSE;
1537   }
1538 invalid_caps:
1539   {
1540     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1541     return FALSE;
1542   }
1543 no_pool:
1544   {
1545     /* Already warned in create_pool */
1546     return FALSE;
1547   }
1548 }
1549 
1550 /* Interfaces stuff */
1551 static void
gst_x_image_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)1552 gst_x_image_sink_navigation_send_event (GstNavigation * navigation,
1553     GstStructure * structure)
1554 {
1555   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (navigation);
1556   GstEvent *event = NULL;
1557   gint x_offset, y_offset;
1558   gdouble x, y;
1559   gboolean handled = FALSE;
1560 
1561   /* We are not converting the pointer coordinates as there's no hardware
1562      scaling done here. The only possible scaling is done by videoscale and
1563      videoscale will have to catch those events and transform the coordinates
1564      to match the applied scaling. So here we just add the offset if the image
1565      is centered in the window.  */
1566 
1567   /* We take the flow_lock while we look at the window */
1568   g_mutex_lock (&ximagesink->flow_lock);
1569 
1570   if (!ximagesink->xwindow) {
1571     g_mutex_unlock (&ximagesink->flow_lock);
1572     gst_structure_free (structure);
1573     return;
1574   }
1575 
1576   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1577   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1578 
1579   g_mutex_unlock (&ximagesink->flow_lock);
1580 
1581   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1582     x -= x_offset / 2;
1583     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1584   }
1585   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1586     y -= y_offset / 2;
1587     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1588   }
1589 
1590   event = gst_event_new_navigation (structure);
1591   if (event) {
1592     gst_event_ref (event);
1593     handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (ximagesink), event);
1594 
1595     if (!handled)
1596       gst_element_post_message (GST_ELEMENT_CAST (ximagesink),
1597           gst_navigation_message_new_event (GST_OBJECT_CAST (ximagesink),
1598               event));
1599 
1600     gst_event_unref (event);
1601   }
1602 }
1603 
1604 static void
gst_x_image_sink_navigation_init(GstNavigationInterface * iface)1605 gst_x_image_sink_navigation_init (GstNavigationInterface * iface)
1606 {
1607   iface->send_event = gst_x_image_sink_navigation_send_event;
1608 }
1609 
1610 static void
gst_x_image_sink_set_window_handle(GstVideoOverlay * overlay,guintptr id)1611 gst_x_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1612 {
1613   XID xwindow_id = id;
1614   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1615   GstXWindow *xwindow = NULL;
1616 
1617   /* We acquire the stream lock while setting this window in the element.
1618      We are basically cleaning tons of stuff replacing the old window, putting
1619      images while we do that would surely crash */
1620   g_mutex_lock (&ximagesink->flow_lock);
1621 
1622   /* If we already use that window return */
1623   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1624     g_mutex_unlock (&ximagesink->flow_lock);
1625     return;
1626   }
1627 
1628   /* If the element has not initialized the X11 context try to do so */
1629   if (!ximagesink->xcontext &&
1630       !(ximagesink->xcontext = gst_x_image_sink_xcontext_get (ximagesink))) {
1631     g_mutex_unlock (&ximagesink->flow_lock);
1632     /* we have thrown a GST_ELEMENT_ERROR now */
1633     return;
1634   }
1635 
1636   /* If a window is there already we destroy it */
1637   if (ximagesink->xwindow) {
1638     gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1639     ximagesink->xwindow = NULL;
1640   }
1641 
1642   /* If the xid is 0 we go back to an internal window */
1643   if (xwindow_id == 0) {
1644     /* If no width/height caps nego did not happen window will be created
1645        during caps nego then */
1646     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1647       xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1648           GST_VIDEO_SINK_WIDTH (ximagesink),
1649           GST_VIDEO_SINK_HEIGHT (ximagesink));
1650     }
1651   } else {
1652     xwindow = g_new0 (GstXWindow, 1);
1653 
1654     xwindow->win = xwindow_id;
1655 
1656     /* We set the events we want to receive and create a GC. */
1657     g_mutex_lock (&ximagesink->x_lock);
1658     xwindow->internal = FALSE;
1659     if (ximagesink->handle_events) {
1660       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1661           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1662           KeyReleaseMask);
1663     }
1664 
1665     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1666     g_mutex_unlock (&ximagesink->x_lock);
1667   }
1668 
1669   if (xwindow) {
1670     ximagesink->xwindow = xwindow;
1671     /* Update the window geometry, possibly generating a reconfigure event. */
1672     gst_x_image_sink_xwindow_update_geometry (ximagesink);
1673   }
1674 
1675   g_mutex_unlock (&ximagesink->flow_lock);
1676 }
1677 
1678 static void
gst_x_image_sink_expose(GstVideoOverlay * overlay)1679 gst_x_image_sink_expose (GstVideoOverlay * overlay)
1680 {
1681   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1682 
1683   gst_x_image_sink_xwindow_update_geometry (ximagesink);
1684   gst_x_image_sink_ximage_put (ximagesink, NULL);
1685 }
1686 
1687 static void
gst_x_image_sink_set_event_handling(GstVideoOverlay * overlay,gboolean handle_events)1688 gst_x_image_sink_set_event_handling (GstVideoOverlay * overlay,
1689     gboolean handle_events)
1690 {
1691   GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1692 
1693   ximagesink->handle_events = handle_events;
1694 
1695   g_mutex_lock (&ximagesink->flow_lock);
1696 
1697   if (G_UNLIKELY (!ximagesink->xwindow)) {
1698     g_mutex_unlock (&ximagesink->flow_lock);
1699     return;
1700   }
1701 
1702   g_mutex_lock (&ximagesink->x_lock);
1703 
1704   if (handle_events) {
1705     if (ximagesink->xwindow->internal) {
1706       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1707           ExposureMask | StructureNotifyMask | PointerMotionMask |
1708           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1709     } else {
1710       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1711           ExposureMask | StructureNotifyMask | PointerMotionMask |
1712           KeyPressMask | KeyReleaseMask);
1713     }
1714   } else {
1715     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1716   }
1717 
1718   g_mutex_unlock (&ximagesink->x_lock);
1719 
1720   g_mutex_unlock (&ximagesink->flow_lock);
1721 }
1722 
1723 static void
gst_x_image_sink_video_overlay_init(GstVideoOverlayInterface * iface)1724 gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1725 {
1726   iface->set_window_handle = gst_x_image_sink_set_window_handle;
1727   iface->expose = gst_x_image_sink_expose;
1728   iface->handle_events = gst_x_image_sink_set_event_handling;
1729 }
1730 
1731 /* =========================================== */
1732 /*                                             */
1733 /*              Init & Class init              */
1734 /*                                             */
1735 /* =========================================== */
1736 
1737 static void
gst_x_image_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1738 gst_x_image_sink_set_property (GObject * object, guint prop_id,
1739     const GValue * value, GParamSpec * pspec)
1740 {
1741   GstXImageSink *ximagesink;
1742 
1743   g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1744 
1745   ximagesink = GST_X_IMAGE_SINK (object);
1746 
1747   switch (prop_id) {
1748     case PROP_DISPLAY:
1749       ximagesink->display_name = g_strdup (g_value_get_string (value));
1750       break;
1751     case PROP_SYNCHRONOUS:
1752       ximagesink->synchronous = g_value_get_boolean (value);
1753       if (ximagesink->xcontext) {
1754         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1755             ximagesink->synchronous ? "TRUE" : "FALSE");
1756         g_mutex_lock (&ximagesink->x_lock);
1757         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1758         g_mutex_unlock (&ximagesink->x_lock);
1759       }
1760       break;
1761     case PROP_FORCE_ASPECT_RATIO:
1762       ximagesink->keep_aspect = g_value_get_boolean (value);
1763       break;
1764     case PROP_PIXEL_ASPECT_RATIO:
1765     {
1766       GValue *tmp;
1767 
1768       tmp = g_new0 (GValue, 1);
1769       g_value_init (tmp, GST_TYPE_FRACTION);
1770 
1771       if (!g_value_transform (value, tmp)) {
1772         GST_WARNING_OBJECT (ximagesink,
1773             "Could not transform string to aspect ratio");
1774         g_free (tmp);
1775       } else {
1776         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1777             gst_value_get_fraction_numerator (tmp),
1778             gst_value_get_fraction_denominator (tmp));
1779         g_free (ximagesink->par);
1780         ximagesink->par = tmp;
1781       }
1782     }
1783       break;
1784     case PROP_HANDLE_EVENTS:
1785       gst_x_image_sink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1786           g_value_get_boolean (value));
1787       gst_x_image_sink_manage_event_thread (ximagesink);
1788       break;
1789     case PROP_HANDLE_EXPOSE:
1790       ximagesink->handle_expose = g_value_get_boolean (value);
1791       gst_x_image_sink_manage_event_thread (ximagesink);
1792       break;
1793     default:
1794       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1795       break;
1796   }
1797 }
1798 
1799 static void
gst_x_image_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1800 gst_x_image_sink_get_property (GObject * object, guint prop_id,
1801     GValue * value, GParamSpec * pspec)
1802 {
1803   GstXImageSink *ximagesink;
1804 
1805   g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1806 
1807   ximagesink = GST_X_IMAGE_SINK (object);
1808 
1809   switch (prop_id) {
1810     case PROP_DISPLAY:
1811       g_value_set_string (value, ximagesink->display_name);
1812       break;
1813     case PROP_SYNCHRONOUS:
1814       g_value_set_boolean (value, ximagesink->synchronous);
1815       break;
1816     case PROP_FORCE_ASPECT_RATIO:
1817       g_value_set_boolean (value, ximagesink->keep_aspect);
1818       break;
1819     case PROP_PIXEL_ASPECT_RATIO:
1820       if (ximagesink->par)
1821         g_value_transform (ximagesink->par, value);
1822       break;
1823     case PROP_HANDLE_EVENTS:
1824       g_value_set_boolean (value, ximagesink->handle_events);
1825       break;
1826     case PROP_HANDLE_EXPOSE:
1827       g_value_set_boolean (value, ximagesink->handle_expose);
1828       break;
1829     case PROP_WINDOW_WIDTH:
1830       if (ximagesink->xwindow)
1831         g_value_set_uint64 (value, ximagesink->xwindow->width);
1832       else
1833         g_value_set_uint64 (value, 0);
1834       break;
1835     case PROP_WINDOW_HEIGHT:
1836       if (ximagesink->xwindow)
1837         g_value_set_uint64 (value, ximagesink->xwindow->height);
1838       else
1839         g_value_set_uint64 (value, 0);
1840       break;
1841     default:
1842       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1843       break;
1844   }
1845 }
1846 
1847 static void
gst_x_image_sink_reset(GstXImageSink * ximagesink)1848 gst_x_image_sink_reset (GstXImageSink * ximagesink)
1849 {
1850   GThread *thread;
1851 
1852   GST_OBJECT_LOCK (ximagesink);
1853   ximagesink->running = FALSE;
1854   /* grab thread and mark it as NULL */
1855   thread = ximagesink->event_thread;
1856   ximagesink->event_thread = NULL;
1857   GST_OBJECT_UNLOCK (ximagesink);
1858 
1859   /* Wait for our event thread to finish before we clean up our stuff. */
1860   if (thread)
1861     g_thread_join (thread);
1862 
1863   if (ximagesink->cur_image) {
1864     gst_buffer_unref (ximagesink->cur_image);
1865     ximagesink->cur_image = NULL;
1866   }
1867 
1868   g_mutex_lock (&ximagesink->flow_lock);
1869 
1870   if (ximagesink->pool) {
1871     gst_object_unref (ximagesink->pool);
1872     ximagesink->pool = NULL;
1873   }
1874 
1875   if (ximagesink->xwindow) {
1876     gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1877     gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1878     ximagesink->xwindow = NULL;
1879   }
1880   g_mutex_unlock (&ximagesink->flow_lock);
1881 
1882   gst_x_image_sink_xcontext_clear (ximagesink);
1883 }
1884 
1885 static void
gst_x_image_sink_finalize(GObject * object)1886 gst_x_image_sink_finalize (GObject * object)
1887 {
1888   GstXImageSink *ximagesink;
1889 
1890   ximagesink = GST_X_IMAGE_SINK (object);
1891 
1892   gst_x_image_sink_reset (ximagesink);
1893 
1894   if (ximagesink->display_name) {
1895     g_free (ximagesink->display_name);
1896     ximagesink->display_name = NULL;
1897   }
1898   if (ximagesink->par) {
1899     g_free (ximagesink->par);
1900     ximagesink->par = NULL;
1901   }
1902   g_mutex_clear (&ximagesink->x_lock);
1903   g_mutex_clear (&ximagesink->flow_lock);
1904 
1905   g_free (ximagesink->media_title);
1906 
1907   G_OBJECT_CLASS (parent_class)->finalize (object);
1908 }
1909 
1910 static void
gst_x_image_sink_init(GstXImageSink * ximagesink)1911 gst_x_image_sink_init (GstXImageSink * ximagesink)
1912 {
1913   ximagesink->display_name = NULL;
1914   ximagesink->xcontext = NULL;
1915   ximagesink->xwindow = NULL;
1916   ximagesink->cur_image = NULL;
1917 
1918   ximagesink->event_thread = NULL;
1919   ximagesink->running = FALSE;
1920 
1921   ximagesink->fps_n = 0;
1922   ximagesink->fps_d = 1;
1923 
1924   g_mutex_init (&ximagesink->x_lock);
1925   g_mutex_init (&ximagesink->flow_lock);
1926 
1927   ximagesink->par = NULL;
1928 
1929   ximagesink->pool = NULL;
1930 
1931   ximagesink->synchronous = FALSE;
1932   ximagesink->keep_aspect = TRUE;
1933   ximagesink->handle_events = TRUE;
1934   ximagesink->handle_expose = TRUE;
1935 }
1936 
1937 static void
gst_x_image_sink_class_init(GstXImageSinkClass * klass)1938 gst_x_image_sink_class_init (GstXImageSinkClass * klass)
1939 {
1940   GObjectClass *gobject_class;
1941   GstElementClass *gstelement_class;
1942   GstBaseSinkClass *gstbasesink_class;
1943   GstVideoSinkClass *videosink_class;
1944 
1945   gobject_class = (GObjectClass *) klass;
1946   gstelement_class = (GstElementClass *) klass;
1947   gstbasesink_class = (GstBaseSinkClass *) klass;
1948   videosink_class = (GstVideoSinkClass *) klass;
1949 
1950   gobject_class->finalize = gst_x_image_sink_finalize;
1951   gobject_class->set_property = gst_x_image_sink_set_property;
1952   gobject_class->get_property = gst_x_image_sink_get_property;
1953 
1954   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1955       g_param_spec_string ("display", "Display", "X Display name",
1956           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1958       g_param_spec_boolean ("synchronous", "Synchronous",
1959           "When enabled, runs the X display in synchronous mode. "
1960           "(unrelated to A/V sync, used only for debugging)", FALSE,
1961           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1962   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1963       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1964           "When enabled, reverse caps negotiation (scaling) will respect "
1965           "original aspect ratio", TRUE,
1966           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1967   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1968       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1969           "The pixel aspect ratio of the device", "1/1",
1970           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1971   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1972       g_param_spec_boolean ("handle-events", "Handle XEvents",
1973           "When enabled, XEvents will be selected and handled", TRUE,
1974           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1975   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1976       g_param_spec_boolean ("handle-expose", "Handle expose",
1977           "When enabled, "
1978           "the current frame will always be drawn in response to X Expose "
1979           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1980 
1981   /**
1982    * GstXImageSink:window-width
1983    *
1984    * Actual width of the video window.
1985    */
1986   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1987       g_param_spec_uint64 ("window-width", "window-width",
1988           "Width of the window", 0, G_MAXUINT64, 0,
1989           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1990 
1991   /**
1992    * GstXImageSink:window-height
1993    *
1994    * Actual height of the video window.
1995    */
1996   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1997       g_param_spec_uint64 ("window-height", "window-height",
1998           "Height of the window", 0, G_MAXUINT64, 0,
1999           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2000 
2001   gst_element_class_set_static_metadata (gstelement_class,
2002       "Video sink", "Sink/Video",
2003       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2004 
2005   gst_element_class_add_static_pad_template (gstelement_class,
2006       &gst_x_image_sink_sink_template_factory);
2007 
2008   gstelement_class->change_state = gst_x_image_sink_change_state;
2009 
2010   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_getcaps);
2011   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_setcaps);
2012   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_x_image_sink_get_times);
2013   gstbasesink_class->propose_allocation =
2014       GST_DEBUG_FUNCPTR (gst_x_image_sink_propose_allocation);
2015   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_x_image_sink_event);
2016 
2017   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_x_image_sink_show_frame);
2018 }
2019