• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *               <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-xvimagesink
23  * @title: xvimagesink
24  *
25  * XvImageSink renders video frames to a drawable (XWindow) on a local display
26  * using the XVideo extension. Rendering to a remote display is theoretically
27  * possible but i doubt that the XVideo extension is actually available when
28  * connecting to a remote display. This element can receive a Window ID from the
29  * application through the #GstVideoOverlay interface and will then render
30  * video frames in this drawable. If no Window ID was provided by the
31  * application, the element will create its own internal window and render
32  * into it.
33  *
34  * ## Scaling
35  *
36  * The XVideo extension, when it's available, handles hardware accelerated
37  * scaling of video frames. This means that the element will just accept
38  * incoming video frames no matter their geometry and will then put them to the
39  * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40  * property it is possible to enforce scaling with a constant aspect ratio,
41  * which means drawing black borders around the video frame.
42  *
43  * ## Events
44  *
45  * XvImageSink creates a thread to handle events coming from the drawable. There
46  * are several kind of events that can be grouped in 2 big categories: input
47  * events and window state related events. Input events will be translated to
48  * navigation events and pushed upstream for other elements to react on them.
49  * This includes events such as pointer moves, key press/release, clicks etc...
50  * Other events are used to handle the drawable appearance even when the data
51  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
52  * paused, it will receive expose events from the drawable and draw the latest
53  * frame with correct borders/aspect-ratio.
54  *
55  * ## Pixel aspect ratio
56  *
57  * When changing state to GST_STATE_READY, XvImageSink will open a connection to
58  * the display specified in the #GstXvImageSink:display property or the
59  * default display if nothing specified. Once this connection is open it will
60  * inspect the display configuration including the physical display geometry and
61  * then calculate the pixel aspect ratio. When receiving video frames with a
62  * different pixel aspect ratio, XvImageSink will use hardware scaling to
63  * display the video frames correctly on display's pixel aspect ratio.
64  * Sometimes the calculated pixel aspect ratio can be wrong, it is
65  * then possible to enforce a specific pixel aspect ratio using the
66  * #GstXvImageSink:pixel-aspect-ratio property.
67  *
68  * ## Examples
69  * |[
70  * gst-launch-1.0 -v videotestsrc ! xvimagesink
71  * ]|
72  *  A pipeline to test hardware scaling.
73  * When the test video signal appears you can resize the window and see that
74  * video frames are scaled through hardware (no extra CPU cost). By default
75  * the image will never be distorted when scaled, instead black borders will
76  * be added if needed.
77  * |[
78  * gst-launch-1.0 -v videotestsrc ! xvimagesink force-aspect-ratio=false
79  * ]|
80  *  Same pipeline with #GstXvImageSink:force-aspect-ratio property set to
81  * false. You can observe that no borders are drawn around the scaled image
82  * now and it will be distorted to fill the entire frame instead of respecting
83  * the aspect ratio.
84  * |[
85  * gst-launch-1.0 -v videotestsrc ! navigationtest ! xvimagesink
86  * ]|
87  *  A pipeline to test navigation events.
88  * While moving the mouse pointer over the test signal you will see a black box
89  * following the mouse pointer. If you press the mouse button somewhere on the
90  * video and release it somewhere else a green box will appear where you pressed
91  * the button and a red one where you released it. (The navigationtest element
92  * is part of gst-plugins-good.) You can observe here that even if the images
93  * are scaled through hardware the pointer coordinates are converted back to the
94  * original video frame geometry so that the box can be drawn to the correct
95  * position. This also handles borders correctly, limiting coordinates to the
96  * image area
97  * |[
98  * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=4/3 ! xvimagesink
99  * ]|
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 XvImageSink 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).
105  * |[
106  * gst-launch-1.0 -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
107  * ]|
108  *  Demonstrates how to use the colorbalance interface.
109  *
110  */
111 
112 /* for developers: there are two useful tools : xvinfo and xvattr */
113 
114 #ifdef HAVE_CONFIG_H
115 #include "config.h"
116 #endif
117 
118 /* Our interfaces */
119 #include <gst/video/navigation.h>
120 #include <gst/video/videooverlay.h>
121 #include <gst/video/colorbalance.h>
122 /* Helper functions */
123 #include <gst/video/gstvideometa.h>
124 
125 /* Object header */
126 #include "xvimagesink.h"
127 #include "xvimageallocator.h"
128 
129 /* Debugging category */
130 #include <gst/gstinfo.h>
131 
132 /* for XkbKeycodeToKeysym */
133 #include <X11/XKBlib.h>
134 
135 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_context);
136 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_image_pool);
137 GST_DEBUG_CATEGORY (gst_debug_xv_image_sink);
138 
139 #define GST_CAT_DEFAULT gst_debug_xv_image_sink
140 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
141 
142 typedef struct
143 {
144   unsigned long flags;
145   unsigned long functions;
146   unsigned long decorations;
147   long input_mode;
148   unsigned long status;
149 }
150 MotifWmHints, MwmHints;
151 
152 #define MWM_HINTS_DECORATIONS   (1L << 1)
153 
154 static gboolean gst_xv_image_sink_open (GstXvImageSink * xvimagesink);
155 static void gst_xv_image_sink_close (GstXvImageSink * xvimagesink);
156 static void gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink *
157     xvimagesink);
158 static void gst_xv_image_sink_expose (GstVideoOverlay * overlay);
159 
160 /* Default template - initiated with class struct to allow gst-register to work
161    without X running */
162 static GstStaticPadTemplate gst_xv_image_sink_sink_template_factory =
163 GST_STATIC_PAD_TEMPLATE ("sink",
164     GST_PAD_SINK,
165     GST_PAD_ALWAYS,
166     GST_STATIC_CAPS ("video/x-raw, "
167         "framerate = (fraction) [ 0, MAX ], "
168         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
169     );
170 
171 enum
172 {
173   PROP_0,
174   PROP_CONTRAST,
175   PROP_BRIGHTNESS,
176   PROP_HUE,
177   PROP_SATURATION,
178   PROP_DISPLAY,
179   PROP_SYNCHRONOUS,
180   PROP_PIXEL_ASPECT_RATIO,
181   PROP_FORCE_ASPECT_RATIO,
182   PROP_HANDLE_EVENTS,
183   PROP_DEVICE,
184   PROP_DEVICE_NAME,
185   PROP_HANDLE_EXPOSE,
186   PROP_DOUBLE_BUFFER,
187   PROP_AUTOPAINT_COLORKEY,
188   PROP_COLORKEY,
189   PROP_DRAW_BORDERS,
190   PROP_WINDOW_WIDTH,
191   PROP_WINDOW_HEIGHT,
192   PROP_LAST
193 };
194 
195 /* ============================================================= */
196 /*                                                               */
197 /*                       Public Methods                          */
198 /*                                                               */
199 /* ============================================================= */
200 
201 /* =========================================== */
202 /*                                             */
203 /*          Object typing & Creation           */
204 /*                                             */
205 /* =========================================== */
206 static void gst_xv_image_sink_navigation_init (GstNavigationInterface * iface);
207 static void gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface *
208     iface);
209 static void gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface *
210     iface);
211 #define gst_xv_image_sink_parent_class parent_class
212 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xv_image_sink, GST_TYPE_VIDEO_SINK,
213     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
214         gst_xv_image_sink_navigation_init);
215     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
216         gst_xv_image_sink_video_overlay_init);
217     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
218         gst_xv_image_sink_colorbalance_init));
219 #define _do_init \
220   GST_DEBUG_CATEGORY_INIT (gst_debug_xv_context, "xcontext", 0, "xcontext miniobject");\
221   GST_DEBUG_CATEGORY_INIT (gst_debug_xv_image_sink, "xvimagesink", 0, "ximagesink element");\
222   GST_DEBUG_CATEGORY_INIT (gst_debug_xv_image_pool, "xvimagepool", 0, "ximagepool object");\
223 
224 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (xvimagesink, "xvimagesink",
225     GST_RANK_PRIMARY, GST_TYPE_XV_IMAGE_SINK, _do_init);
226 
227 /* ============================================================= */
228 /*                                                               */
229 /*                       Private Methods                         */
230 /*                                                               */
231 /* ============================================================= */
232 
233 
234 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
235  * if no window was available  */
236 static gboolean
gst_xv_image_sink_xvimage_put(GstXvImageSink * xvimagesink,GstBuffer * xvimage)237 gst_xv_image_sink_xvimage_put (GstXvImageSink * xvimagesink,
238     GstBuffer * xvimage)
239 {
240   GstXvImageMemory *mem;
241   GstVideoCropMeta *crop;
242   GstVideoRectangle result;
243   gboolean draw_border = FALSE;
244   GstVideoRectangle src = { 0, };
245   GstVideoRectangle dst = { 0, };
246   GstVideoRectangle mem_crop;
247   GstXWindow *xwindow;
248 
249   /* We take the flow_lock. If expose is in there we don't want to run
250      concurrently from the data flow thread */
251   g_mutex_lock (&xvimagesink->flow_lock);
252 
253   if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
254     g_mutex_unlock (&xvimagesink->flow_lock);
255     return FALSE;
256   }
257 
258   /* Draw borders when displaying the first frame. After this
259      draw borders only on expose event or after a size change. */
260   if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
261     draw_border = xvimagesink->draw_borders;
262     xvimagesink->redraw_border = FALSE;
263   }
264 
265   /* Store a reference to the last image we put, lose the previous one */
266   if (xvimage && xvimagesink->cur_image != xvimage) {
267     if (xvimagesink->cur_image) {
268       GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
269       gst_buffer_unref (xvimagesink->cur_image);
270     }
271     GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
272     xvimagesink->cur_image = gst_buffer_ref (xvimage);
273   }
274 
275   /* Expose sends a NULL image, we take the latest frame */
276   if (!xvimage) {
277     if (xvimagesink->cur_image) {
278       draw_border = TRUE;
279       xvimage = xvimagesink->cur_image;
280     } else {
281       g_mutex_unlock (&xvimagesink->flow_lock);
282       return TRUE;
283     }
284   }
285 
286   mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
287   gst_xvimage_memory_get_crop (mem, &mem_crop);
288 
289   crop = gst_buffer_get_video_crop_meta (xvimage);
290 
291   if (crop) {
292     src.x = crop->x + mem_crop.x;
293     src.y = crop->y + mem_crop.y;
294     src.w = crop->width;
295     src.h = crop->height;
296     GST_LOG_OBJECT (xvimagesink,
297         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
298   } else {
299     src = mem_crop;
300   }
301 
302   if (xvimagesink->keep_aspect) {
303     GstVideoRectangle s;
304 
305     /* We take the size of the source material as it was negotiated and
306      * corrected for DAR. This size can be different from the cropped size in
307      * which case the image will be scaled to fit the negotiated size. */
308     s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
309     s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
310     dst.w = xwindow->render_rect.w;
311     dst.h = xwindow->render_rect.h;
312 
313     gst_video_sink_center_rect (s, dst, &result, TRUE);
314     result.x += xwindow->render_rect.x;
315     result.y += xwindow->render_rect.y;
316   } else {
317     memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
318   }
319 
320   gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
321 
322   g_mutex_unlock (&xvimagesink->flow_lock);
323 
324   return TRUE;
325 }
326 
327 static void
gst_xv_image_sink_xwindow_set_title(GstXvImageSink * xvimagesink,GstXWindow * xwindow,const gchar * media_title)328 gst_xv_image_sink_xwindow_set_title (GstXvImageSink * xvimagesink,
329     GstXWindow * xwindow, const gchar * media_title)
330 {
331   if (media_title) {
332     g_free (xvimagesink->media_title);
333     xvimagesink->media_title = g_strdup (media_title);
334   }
335   if (xwindow) {
336     /* we have a window */
337     const gchar *app_name;
338     const gchar *title = NULL;
339     gchar *title_mem = NULL;
340 
341     /* set application name as a title */
342     app_name = g_get_application_name ();
343 
344     if (app_name && xvimagesink->media_title) {
345       title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
346           app_name, NULL);
347     } else if (app_name) {
348       title = app_name;
349     } else if (xvimagesink->media_title) {
350       title = xvimagesink->media_title;
351     }
352 
353     gst_xwindow_set_title (xwindow, title);
354     g_free (title_mem);
355   }
356 }
357 
358 /* This function handles a GstXWindow creation
359  * The width and height are the actual pixel size on the display */
360 static GstXWindow *
gst_xv_image_sink_xwindow_new(GstXvImageSink * xvimagesink,gint width,gint height)361 gst_xv_image_sink_xwindow_new (GstXvImageSink * xvimagesink,
362     gint width, gint height)
363 {
364   GstXWindow *xwindow = NULL;
365   GstXvContext *context;
366 
367   g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
368 
369   context = xvimagesink->context;
370 
371   xwindow = gst_xvcontext_create_xwindow (context, width, height);
372 
373   /* set application name as a title */
374   gst_xv_image_sink_xwindow_set_title (xvimagesink, xwindow, NULL);
375 
376   gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
377 
378   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
379       xwindow->win);
380 
381   return xwindow;
382 }
383 
384 static void
gst_xv_image_sink_xwindow_update_geometry(GstXvImageSink * xvimagesink)385 gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
386 {
387   g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
388 
389   /* Update the window geometry */
390   g_mutex_lock (&xvimagesink->flow_lock);
391   if (G_LIKELY (xvimagesink->xwindow))
392     gst_xwindow_update_geometry (xvimagesink->xwindow);
393   g_mutex_unlock (&xvimagesink->flow_lock);
394 }
395 
396 /* This function commits our internal colorbalance settings to our grabbed Xv
397    port. If the context is not initialized yet it simply returns */
398 static void
gst_xv_image_sink_update_colorbalance(GstXvImageSink * xvimagesink)399 gst_xv_image_sink_update_colorbalance (GstXvImageSink * xvimagesink)
400 {
401   GstXvContext *context;
402 
403   g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
404 
405   /* If we haven't initialized the X context we can't update anything */
406   if ((context = xvimagesink->context) == NULL)
407     return;
408 
409   gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
410 }
411 
412 /* This function handles XEvents that might be in the queue. It generates
413    GstEvent that will be sent upstream in the pipeline to handle interactivity
414    and navigation. It will also listen for configure events on the window to
415    trigger caps renegotiation so on the fly software scaling can work. */
416 static void
gst_xv_image_sink_handle_xevents(GstXvImageSink * xvimagesink)417 gst_xv_image_sink_handle_xevents (GstXvImageSink * xvimagesink)
418 {
419   XEvent e;
420   gint pointer_x = 0, pointer_y = 0;
421   gboolean pointer_moved = FALSE;
422   gboolean exposed = FALSE, configured = FALSE;
423 
424   g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
425 
426   /* Handle Interaction, produces navigation events */
427 
428   /* We get all pointer motion events, only the last position is
429      interesting. */
430   g_mutex_lock (&xvimagesink->flow_lock);
431   g_mutex_lock (&xvimagesink->context->lock);
432   while (XCheckWindowEvent (xvimagesink->context->disp,
433           xvimagesink->xwindow->win, PointerMotionMask, &e)) {
434     g_mutex_unlock (&xvimagesink->context->lock);
435     g_mutex_unlock (&xvimagesink->flow_lock);
436 
437     switch (e.type) {
438       case MotionNotify:
439         pointer_x = e.xmotion.x;
440         pointer_y = e.xmotion.y;
441         pointer_moved = TRUE;
442         break;
443       default:
444         break;
445     }
446     g_mutex_lock (&xvimagesink->flow_lock);
447     g_mutex_lock (&xvimagesink->context->lock);
448   }
449 
450   if (pointer_moved) {
451     g_mutex_unlock (&xvimagesink->context->lock);
452     g_mutex_unlock (&xvimagesink->flow_lock);
453 
454     GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
455         pointer_x, pointer_y);
456     gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
457         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
458 
459     g_mutex_lock (&xvimagesink->flow_lock);
460     g_mutex_lock (&xvimagesink->context->lock);
461   }
462 
463   /* We get all events on our window to throw them upstream */
464   while (XCheckWindowEvent (xvimagesink->context->disp,
465           xvimagesink->xwindow->win,
466           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
467           &e)) {
468     KeySym keysym;
469     const char *key_str = NULL;
470 
471     /* We lock only for the X function call */
472     g_mutex_unlock (&xvimagesink->context->lock);
473     g_mutex_unlock (&xvimagesink->flow_lock);
474 
475     switch (e.type) {
476       case ButtonPress:
477         /* Mouse button pressed over our window. We send upstream
478            events for interactivity/navigation */
479         GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
480             e.xbutton.button, e.xbutton.x, e.xbutton.y);
481         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
482             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
483         break;
484       case ButtonRelease:
485         /* Mouse button released over our window. We send upstream
486            events for interactivity/navigation */
487         GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
488             e.xbutton.button, e.xbutton.x, e.xbutton.y);
489         gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
490             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
491         break;
492       case KeyPress:
493       case KeyRelease:
494         /* Key pressed/released over our window. We send upstream
495            events for interactivity/navigation */
496         g_mutex_lock (&xvimagesink->context->lock);
497         if (xvimagesink->context->use_xkb) {
498           keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
499               e.xkey.keycode, 0, 0);
500         } else {
501           keysym = XKeycodeToKeysym (xvimagesink->context->disp,
502               e.xkey.keycode, 0);
503         }
504         if (keysym != NoSymbol) {
505           key_str = XKeysymToString (keysym);
506         } else {
507           key_str = "unknown";
508         }
509         g_mutex_unlock (&xvimagesink->context->lock);
510         GST_DEBUG_OBJECT (xvimagesink,
511             "key %d pressed over window at %d,%d (%s)",
512             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
513         gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
514             e.type == KeyPress ? "key-press" : "key-release", key_str);
515         break;
516       default:
517         GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
518             e.type);
519     }
520     g_mutex_lock (&xvimagesink->flow_lock);
521     g_mutex_lock (&xvimagesink->context->lock);
522   }
523 
524   /* Handle Expose */
525   while (XCheckWindowEvent (xvimagesink->context->disp,
526           xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
527     switch (e.type) {
528       case Expose:
529         exposed = TRUE;
530         break;
531       case ConfigureNotify:
532         g_mutex_unlock (&xvimagesink->context->lock);
533         g_mutex_unlock (&xvimagesink->flow_lock);
534 
535         gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
536 
537         g_mutex_lock (&xvimagesink->flow_lock);
538         g_mutex_lock (&xvimagesink->context->lock);
539         configured = TRUE;
540         break;
541       default:
542         break;
543     }
544   }
545 
546   if (xvimagesink->handle_expose && (exposed || configured)) {
547     g_mutex_unlock (&xvimagesink->context->lock);
548     g_mutex_unlock (&xvimagesink->flow_lock);
549 
550     gst_xv_image_sink_expose (GST_VIDEO_OVERLAY (xvimagesink));
551 
552     g_mutex_lock (&xvimagesink->flow_lock);
553     g_mutex_lock (&xvimagesink->context->lock);
554   }
555 
556   /* Handle Display events */
557   while (XPending (xvimagesink->context->disp)) {
558     XNextEvent (xvimagesink->context->disp, &e);
559 
560     switch (e.type) {
561       case ClientMessage:{
562         Atom wm_delete;
563 
564         wm_delete = XInternAtom (xvimagesink->context->disp,
565             "WM_DELETE_WINDOW", True);
566         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
567           /* Handle window deletion by posting an error on the bus */
568           GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
569               ("Output window was closed"), (NULL));
570 
571           g_mutex_unlock (&xvimagesink->context->lock);
572           gst_xwindow_destroy (xvimagesink->xwindow);
573           xvimagesink->xwindow = NULL;
574           g_mutex_lock (&xvimagesink->context->lock);
575         }
576         break;
577       }
578       default:
579         break;
580     }
581   }
582 
583   g_mutex_unlock (&xvimagesink->context->lock);
584   g_mutex_unlock (&xvimagesink->flow_lock);
585 }
586 
587 static gpointer
gst_xv_image_sink_event_thread(GstXvImageSink * xvimagesink)588 gst_xv_image_sink_event_thread (GstXvImageSink * xvimagesink)
589 {
590   g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
591 
592   GST_OBJECT_LOCK (xvimagesink);
593   while (xvimagesink->running) {
594     GST_OBJECT_UNLOCK (xvimagesink);
595 
596     if (xvimagesink->xwindow) {
597       gst_xv_image_sink_handle_xevents (xvimagesink);
598     }
599     /* FIXME: do we want to align this with the framerate or anything else? */
600     g_usleep (G_USEC_PER_SEC / 20);
601 
602     GST_OBJECT_LOCK (xvimagesink);
603   }
604   GST_OBJECT_UNLOCK (xvimagesink);
605 
606   return NULL;
607 }
608 
609 static void
gst_xv_image_sink_manage_event_thread(GstXvImageSink * xvimagesink)610 gst_xv_image_sink_manage_event_thread (GstXvImageSink * xvimagesink)
611 {
612   GThread *thread = NULL;
613 
614   /* don't start the thread too early */
615   if (xvimagesink->context == NULL) {
616     return;
617   }
618 
619   GST_OBJECT_LOCK (xvimagesink);
620   if (xvimagesink->handle_expose || xvimagesink->handle_events) {
621     if (!xvimagesink->event_thread) {
622       /* Setup our event listening thread */
623       GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
624           xvimagesink->handle_expose, xvimagesink->handle_events);
625       xvimagesink->running = TRUE;
626       xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
627           (GThreadFunc) gst_xv_image_sink_event_thread, xvimagesink, NULL);
628     }
629   } else {
630     if (xvimagesink->event_thread) {
631       GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
632           xvimagesink->handle_expose, xvimagesink->handle_events);
633       xvimagesink->running = FALSE;
634       /* grab thread and mark it as NULL */
635       thread = xvimagesink->event_thread;
636       xvimagesink->event_thread = NULL;
637     }
638   }
639   GST_OBJECT_UNLOCK (xvimagesink);
640 
641   /* Wait for our event thread to finish */
642   if (thread)
643     g_thread_join (thread);
644 
645 }
646 
647 /* Element stuff */
648 
649 static GstCaps *
gst_xv_image_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)650 gst_xv_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
651 {
652   GstXvImageSink *xvimagesink;
653   GstCaps *caps;
654 
655   xvimagesink = GST_XV_IMAGE_SINK (bsink);
656 
657   if (xvimagesink->context) {
658     if (filter)
659       return gst_caps_intersect_full (filter, xvimagesink->context->caps,
660           GST_CAPS_INTERSECT_FIRST);
661     else
662       return gst_caps_ref (xvimagesink->context->caps);
663   }
664 
665   caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
666   if (filter) {
667     GstCaps *intersection;
668 
669     intersection =
670         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
671     gst_caps_unref (caps);
672     caps = intersection;
673   }
674   return caps;
675 }
676 
677 static GstBufferPool *
gst_xv_image_sink_create_pool(GstXvImageSink * xvimagesink,GstCaps * caps,gsize size,gint min)678 gst_xv_image_sink_create_pool (GstXvImageSink * xvimagesink, GstCaps * caps,
679     gsize size, gint min)
680 {
681   GstBufferPool *pool;
682   GstStructure *config;
683 
684   pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
685 
686   config = gst_buffer_pool_get_config (pool);
687   gst_buffer_pool_config_set_params (config, caps, size, min, 0);
688 
689   if (!gst_buffer_pool_set_config (pool, config))
690     goto config_failed;
691 
692   return pool;
693 
694 config_failed:
695   {
696     GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
697     gst_object_unref (pool);
698     return NULL;
699   }
700 }
701 
702 static gboolean
gst_xv_image_sink_setcaps(GstBaseSink * bsink,GstCaps * caps)703 gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
704 {
705   GstXvImageSink *xvimagesink;
706   GstXvContext *context;
707   GstBufferPool *oldpool;
708   GstVideoInfo info;
709   guint32 im_format = 0;
710   gint video_par_n, video_par_d;        /* video's PAR */
711   gint display_par_n, display_par_d;    /* display's PAR */
712   guint num, den;
713 
714   xvimagesink = GST_XV_IMAGE_SINK (bsink);
715   context = xvimagesink->context;
716 
717   GST_DEBUG_OBJECT (xvimagesink,
718       "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
719       GST_PTR_FORMAT, context->caps, caps);
720 
721   if (!gst_caps_can_intersect (context->caps, caps))
722     goto incompatible_caps;
723 
724   if (!gst_video_info_from_caps (&info, caps))
725     goto invalid_format;
726 
727   xvimagesink->fps_n = info.fps_n;
728   xvimagesink->fps_d = info.fps_d;
729 
730   xvimagesink->video_width = info.width;
731   xvimagesink->video_height = info.height;
732 
733   im_format = gst_xvcontext_get_format_from_info (context, &info);
734   if (im_format == -1)
735     goto invalid_format;
736 
737   gst_xvcontext_set_colorimetry (context, &info.colorimetry);
738 
739   /* get aspect ratio from caps if it's present, and
740    * convert video width and height to a display width and height
741    * using wd / hd = wv / hv * PARv / PARd */
742 
743   /* get video's PAR */
744   video_par_n = info.par_n;
745   video_par_d = info.par_d;
746 
747   /* get display's PAR */
748   if (xvimagesink->par) {
749     display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
750     display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
751   } else {
752     display_par_n = 1;
753     display_par_d = 1;
754   }
755 
756   if (!gst_video_calculate_display_ratio (&num, &den, info.width,
757           info.height, video_par_n, video_par_d, display_par_n, display_par_d))
758     goto no_disp_ratio;
759 
760   GST_DEBUG_OBJECT (xvimagesink,
761       "video width/height: %dx%d, calculated display ratio: %d/%d",
762       info.width, info.height, num, den);
763 
764   /* now find a width x height that respects this display ratio.
765    * prefer those that have one of w/h the same as the incoming video
766    * using wd / hd = num / den */
767 
768   /* start with same height, because of interlaced video */
769   /* check hd / den is an integer scale factor, and scale wd with the PAR */
770   if (info.height % den == 0) {
771     GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
772     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
773         gst_util_uint64_scale_int (info.height, num, den);
774     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
775   } else if (info.width % num == 0) {
776     GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
777     GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
778     GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
779         gst_util_uint64_scale_int (info.width, den, num);
780   } else {
781     GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
782     GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
783         gst_util_uint64_scale_int (info.height, num, den);
784     GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
785   }
786   GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
787       GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
788 
789   /* Notify application to set xwindow id now */
790   g_mutex_lock (&xvimagesink->flow_lock);
791   if (!xvimagesink->xwindow) {
792     g_mutex_unlock (&xvimagesink->flow_lock);
793     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
794   } else {
795     g_mutex_unlock (&xvimagesink->flow_lock);
796   }
797 
798   /* Creating our window and our image with the display size in pixels */
799   if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
800       GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
801     goto no_display_size;
802 
803   g_mutex_lock (&xvimagesink->flow_lock);
804   if (!xvimagesink->xwindow) {
805     xvimagesink->xwindow = gst_xv_image_sink_xwindow_new (xvimagesink,
806         GST_VIDEO_SINK_WIDTH (xvimagesink),
807         GST_VIDEO_SINK_HEIGHT (xvimagesink));
808   }
809 
810   if (xvimagesink->pending_render_rect) {
811     xvimagesink->pending_render_rect = FALSE;
812     gst_xwindow_set_render_rectangle (xvimagesink->xwindow,
813         xvimagesink->render_rect.x, xvimagesink->render_rect.y,
814         xvimagesink->render_rect.w, xvimagesink->render_rect.h);
815   }
816 
817   xvimagesink->info = info;
818 
819   /* After a resize, we want to redraw the borders in case the new frame size
820    * doesn't cover the same area */
821   xvimagesink->redraw_border = TRUE;
822 
823   /* destroy current pool */
824   oldpool = xvimagesink->pool;
825   xvimagesink->pool = NULL;
826   g_mutex_unlock (&xvimagesink->flow_lock);
827 
828   /* deactivate and unref the old internal pool */
829   if (oldpool) {
830     gst_buffer_pool_set_active (oldpool, FALSE);
831     gst_object_unref (oldpool);
832   }
833 
834   return TRUE;
835 
836   /* ERRORS */
837 incompatible_caps:
838   {
839     GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
840     return FALSE;
841   }
842 invalid_format:
843   {
844     GST_DEBUG_OBJECT (xvimagesink,
845         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
846     return FALSE;
847   }
848 no_disp_ratio:
849   {
850     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
851         ("Error calculating the output display ratio of the video."));
852     return FALSE;
853   }
854 no_display_size:
855   {
856     GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
857         ("Error calculating the output display size of the video."));
858     return FALSE;
859   }
860 }
861 
862 static GstStateChangeReturn
gst_xv_image_sink_change_state(GstElement * element,GstStateChange transition)863 gst_xv_image_sink_change_state (GstElement * element, GstStateChange transition)
864 {
865   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
866   GstXvImageSink *xvimagesink;
867 
868   xvimagesink = GST_XV_IMAGE_SINK (element);
869 
870   switch (transition) {
871     case GST_STATE_CHANGE_NULL_TO_READY:
872       if (!gst_xv_image_sink_open (xvimagesink))
873         goto error;
874       break;
875     case GST_STATE_CHANGE_READY_TO_PAUSED:
876       break;
877     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
878       break;
879     case GST_STATE_CHANGE_PAUSED_TO_READY:
880       break;
881     default:
882       break;
883   }
884 
885   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
886 
887   switch (transition) {
888     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
889       break;
890     case GST_STATE_CHANGE_PAUSED_TO_READY:
891       xvimagesink->fps_n = 0;
892       xvimagesink->fps_d = 1;
893       GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
894       GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
895       g_mutex_lock (&xvimagesink->flow_lock);
896       if (xvimagesink->pool)
897         gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
898       g_mutex_unlock (&xvimagesink->flow_lock);
899       break;
900     case GST_STATE_CHANGE_READY_TO_NULL:
901       gst_xv_image_sink_close (xvimagesink);
902       break;
903     default:
904       break;
905   }
906   return ret;
907 
908 error:
909   {
910     return GST_STATE_CHANGE_FAILURE;
911   }
912 }
913 
914 static void
gst_xv_image_sink_get_times(GstBaseSink * bsink,GstBuffer * buf,GstClockTime * start,GstClockTime * end)915 gst_xv_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
916     GstClockTime * start, GstClockTime * end)
917 {
918   GstXvImageSink *xvimagesink;
919 
920   xvimagesink = GST_XV_IMAGE_SINK (bsink);
921 
922   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
923     *start = GST_BUFFER_TIMESTAMP (buf);
924     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
925       *end = *start + GST_BUFFER_DURATION (buf);
926     } else {
927       if (xvimagesink->fps_n > 0) {
928         *end = *start +
929             gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
930             xvimagesink->fps_n);
931       }
932     }
933   }
934 }
935 
936 static GstFlowReturn
gst_xv_image_sink_show_frame(GstVideoSink * vsink,GstBuffer * buf)937 gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
938 {
939   GstFlowReturn res;
940   GstXvImageSink *xvimagesink;
941   GstBuffer *to_put = NULL;
942   GstMemory *mem;
943 
944   xvimagesink = GST_XV_IMAGE_SINK (vsink);
945 
946   if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
947       && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
948     /* If this buffer has been allocated using our buffer management we simply
949        put the ximage which is in the PRIVATE pointer */
950     GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
951         buf);
952     to_put = buf;
953     res = GST_FLOW_OK;
954   } else {
955     GstVideoFrame src, dest;
956     GstBufferPoolAcquireParams params = { 0, };
957     GstVideoCropMeta *crop_meta;
958 
959     /* Else we have to copy the data into our private image, */
960     /* if we have one... */
961     GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
962 
963     if (xvimagesink->pool == NULL) {
964       GstCaps *caps = gst_video_info_to_caps (&xvimagesink->info);
965 
966       GST_DEBUG_OBJECT (xvimagesink, "create new pool");
967       xvimagesink->pool = gst_xv_image_sink_create_pool (xvimagesink, caps,
968           xvimagesink->info.size, 2);
969       if (xvimagesink->pool == NULL)
970         goto no_pool;
971 
972       gst_caps_unref (caps);
973     }
974 
975     if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
976       goto activate_failed;
977 
978     /* take a buffer from our pool, if there is no buffer in the pool something
979      * is seriously wrong, waiting for the pool here might deadlock when we try
980      * to go to PAUSED because we never flush the pool then. */
981     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
982     res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, &params);
983     if (res != GST_FLOW_OK)
984       goto no_buffer;
985 
986     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
987         "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
988 
989     if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
990       goto invalid_buffer;
991 
992     if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
993       gst_video_frame_unmap (&src);
994       goto invalid_buffer;
995     }
996 
997     gst_video_frame_copy (&dest, &src);
998 
999     gst_video_frame_unmap (&dest);
1000     gst_video_frame_unmap (&src);
1001 
1002     if ((crop_meta = gst_buffer_get_video_crop_meta (buf))) {
1003       GstVideoCropMeta *dmeta = gst_buffer_add_video_crop_meta (to_put);
1004 
1005       dmeta->x = crop_meta->x;
1006       dmeta->y = crop_meta->y;
1007       dmeta->width = crop_meta->width;
1008       dmeta->height = crop_meta->height;
1009     }
1010   }
1011 
1012   if (!gst_xv_image_sink_xvimage_put (xvimagesink, to_put))
1013     goto no_window;
1014 
1015 done:
1016   if (to_put != buf)
1017     gst_buffer_unref (to_put);
1018 
1019   return res;
1020 
1021   /* ERRORS */
1022 no_pool:
1023   {
1024     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1025         ("Internal error: can't allocate images"),
1026         ("We don't have a bufferpool negotiated"));
1027     return GST_FLOW_ERROR;
1028   }
1029 no_buffer:
1030   {
1031     /* No image available. That's very bad ! */
1032     GST_WARNING_OBJECT (xvimagesink, "could not create image");
1033     return GST_FLOW_OK;
1034   }
1035 invalid_buffer:
1036   {
1037     /* No Window available to put our image into */
1038     GST_WARNING_OBJECT (xvimagesink, "could not map image");
1039     res = GST_FLOW_OK;
1040     goto done;
1041   }
1042 no_window:
1043   {
1044     /* No Window available to put our image into */
1045     GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1046     res = GST_FLOW_ERROR;
1047     goto done;
1048   }
1049 activate_failed:
1050   {
1051     GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1052     res = GST_FLOW_ERROR;
1053     goto done;
1054   }
1055 }
1056 
1057 static gboolean
gst_xv_image_sink_event(GstBaseSink * sink,GstEvent * event)1058 gst_xv_image_sink_event (GstBaseSink * sink, GstEvent * event)
1059 {
1060   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (sink);
1061 
1062   switch (GST_EVENT_TYPE (event)) {
1063     case GST_EVENT_TAG:{
1064       GstTagList *l;
1065       gchar *title = NULL;
1066 
1067       gst_event_parse_tag (event, &l);
1068       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1069 
1070       if (title) {
1071         GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1072         gst_xv_image_sink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1073             title);
1074 
1075         g_free (title);
1076       }
1077       break;
1078     }
1079     default:
1080       break;
1081   }
1082   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1083 }
1084 
1085 static gboolean
gst_xv_image_sink_propose_allocation(GstBaseSink * bsink,GstQuery * query)1086 gst_xv_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1087 {
1088   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (bsink);
1089   GstBufferPool *pool = NULL;
1090   GstCaps *caps;
1091   GstVideoInfo info;
1092   guint size;
1093   gboolean need_pool;
1094 
1095   gst_query_parse_allocation (query, &caps, &need_pool);
1096 
1097   if (caps == NULL)
1098     goto no_caps;
1099 
1100   if (!gst_video_info_from_caps (&info, caps))
1101     goto invalid_caps;
1102 
1103   /* the normal size of a frame */
1104   size = info.size;
1105 
1106   if (need_pool) {
1107     GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1108     pool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 0);
1109 
1110     if (pool == NULL)
1111       goto no_pool;
1112   }
1113 
1114   /* update info since allocation frame's wxh might differ from the
1115    * negotiation ones */
1116   xvimagesink->info = info;
1117 
1118   /* we need at least 2 buffer because we hold on to the last one */
1119   gst_query_add_allocation_pool (query, pool, size, 2, 0);
1120   if (pool)
1121     gst_object_unref (pool);
1122 
1123   /* we also support various metadata */
1124   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1125   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1126 
1127   return TRUE;
1128 
1129   /* ERRORS */
1130 no_caps:
1131   {
1132     GST_DEBUG_OBJECT (bsink, "no caps specified");
1133     return FALSE;
1134   }
1135 invalid_caps:
1136   {
1137     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1138     return FALSE;
1139   }
1140 no_pool:
1141   {
1142     /* Already warned in create_pool */
1143     return FALSE;
1144   }
1145 }
1146 
1147 /* Interfaces stuff */
1148 static void
gst_xv_image_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)1149 gst_xv_image_sink_navigation_send_event (GstNavigation * navigation,
1150     GstStructure * structure)
1151 {
1152   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (navigation);
1153   gboolean handled = FALSE;
1154   GstEvent *event = NULL;
1155 
1156   GstVideoRectangle src = { 0, };
1157   GstVideoRectangle dst = { 0, };
1158   GstVideoRectangle result;
1159   gdouble x, y, xscale = 1.0, yscale = 1.0;
1160   GstXWindow *xwindow;
1161 
1162   /* We take the flow_lock while we look at the window */
1163   g_mutex_lock (&xvimagesink->flow_lock);
1164 
1165   if (!(xwindow = xvimagesink->xwindow)) {
1166     g_mutex_unlock (&xvimagesink->flow_lock);
1167     gst_structure_free (structure);
1168     return;
1169   }
1170 
1171   if (xvimagesink->keep_aspect) {
1172     /* We get the frame position using the calculated geometry from _setcaps
1173        that respect pixel aspect ratios */
1174     src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1175     src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1176     dst.w = xwindow->render_rect.w;
1177     dst.h = xwindow->render_rect.h;
1178 
1179     gst_video_sink_center_rect (src, dst, &result, TRUE);
1180     result.x += xwindow->render_rect.x;
1181     result.y += xwindow->render_rect.y;
1182   } else {
1183     memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1184   }
1185 
1186   g_mutex_unlock (&xvimagesink->flow_lock);
1187 
1188   /* We calculate scaling using the original video frames geometry to include
1189      pixel aspect ratio scaling. */
1190   xscale = (gdouble) xvimagesink->video_width / result.w;
1191   yscale = (gdouble) xvimagesink->video_height / result.h;
1192 
1193   /* Converting pointer coordinates to the non scaled geometry */
1194   if (gst_structure_get_double (structure, "pointer_x", &x)) {
1195     x = MIN (x, result.x + result.w);
1196     x = MAX (x - result.x, 0);
1197     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1198         (gdouble) x * xscale, NULL);
1199   }
1200   if (gst_structure_get_double (structure, "pointer_y", &y)) {
1201     y = MIN (y, result.y + result.h);
1202     y = MAX (y - result.y, 0);
1203     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1204         (gdouble) y * yscale, NULL);
1205   }
1206 
1207   event = gst_event_new_navigation (structure);
1208   if (event) {
1209     gst_event_ref (event);
1210     handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (xvimagesink), event);
1211 
1212     if (!handled)
1213       gst_element_post_message ((GstElement *) xvimagesink,
1214           gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1215 
1216     gst_event_unref (event);
1217   }
1218 }
1219 
1220 static void
gst_xv_image_sink_navigation_init(GstNavigationInterface * iface)1221 gst_xv_image_sink_navigation_init (GstNavigationInterface * iface)
1222 {
1223   iface->send_event = gst_xv_image_sink_navigation_send_event;
1224 }
1225 
1226 static void
gst_xv_image_sink_set_window_handle(GstVideoOverlay * overlay,guintptr id)1227 gst_xv_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1228 {
1229   XID xwindow_id = id;
1230   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1231   GstXWindow *xwindow = NULL;
1232   GstXvContext *context;
1233 
1234   g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1235 
1236   g_mutex_lock (&xvimagesink->flow_lock);
1237 
1238   /* If we already use that window return */
1239   if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1240     g_mutex_unlock (&xvimagesink->flow_lock);
1241     return;
1242   }
1243 
1244   /* If the element has not initialized the X11 context try to do so */
1245   if (!xvimagesink->context &&
1246       !(xvimagesink->context =
1247           gst_xvcontext_new (&xvimagesink->config, NULL))) {
1248     g_mutex_unlock (&xvimagesink->flow_lock);
1249     /* we have thrown a GST_ELEMENT_ERROR now */
1250     return;
1251   }
1252 
1253   context = xvimagesink->context;
1254 
1255   gst_xv_image_sink_update_colorbalance (xvimagesink);
1256 
1257   /* If a window is there already we destroy it */
1258   if (xvimagesink->xwindow) {
1259     gst_xwindow_destroy (xvimagesink->xwindow);
1260     xvimagesink->xwindow = NULL;
1261   }
1262 
1263   /* If the xid is 0 we go back to an internal window */
1264   if (xwindow_id == 0) {
1265     /* If no width/height caps nego did not happen window will be created
1266        during caps nego then */
1267     if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1268         && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1269       xwindow =
1270           gst_xv_image_sink_xwindow_new (xvimagesink,
1271           GST_VIDEO_SINK_WIDTH (xvimagesink),
1272           GST_VIDEO_SINK_HEIGHT (xvimagesink));
1273     }
1274   } else {
1275     xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1276     gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1277   }
1278 
1279   if (xwindow)
1280     xvimagesink->xwindow = xwindow;
1281 
1282   g_mutex_unlock (&xvimagesink->flow_lock);
1283 }
1284 
1285 static void
gst_xv_image_sink_expose(GstVideoOverlay * overlay)1286 gst_xv_image_sink_expose (GstVideoOverlay * overlay)
1287 {
1288   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1289 
1290   GST_DEBUG ("doing expose");
1291   gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
1292   gst_xv_image_sink_xvimage_put (xvimagesink, NULL);
1293 }
1294 
1295 static void
gst_xv_image_sink_set_event_handling(GstVideoOverlay * overlay,gboolean handle_events)1296 gst_xv_image_sink_set_event_handling (GstVideoOverlay * overlay,
1297     gboolean handle_events)
1298 {
1299   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1300 
1301   g_mutex_lock (&xvimagesink->flow_lock);
1302   xvimagesink->handle_events = handle_events;
1303   if (G_LIKELY (xvimagesink->xwindow))
1304     gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1305   g_mutex_unlock (&xvimagesink->flow_lock);
1306 }
1307 
1308 static void
gst_xv_image_sink_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)1309 gst_xv_image_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1310     gint y, gint width, gint height)
1311 {
1312   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1313 
1314   g_mutex_lock (&xvimagesink->flow_lock);
1315   if (G_LIKELY (xvimagesink->xwindow)) {
1316     gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1317         height);
1318   } else {
1319     xvimagesink->render_rect.x = x;
1320     xvimagesink->render_rect.y = y;
1321     xvimagesink->render_rect.w = width;
1322     xvimagesink->render_rect.h = height;
1323     xvimagesink->pending_render_rect = TRUE;
1324   }
1325   g_mutex_unlock (&xvimagesink->flow_lock);
1326 }
1327 
1328 static void
gst_xv_image_sink_video_overlay_init(GstVideoOverlayInterface * iface)1329 gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1330 {
1331   iface->set_window_handle = gst_xv_image_sink_set_window_handle;
1332   iface->expose = gst_xv_image_sink_expose;
1333   iface->handle_events = gst_xv_image_sink_set_event_handling;
1334   iface->set_render_rectangle = gst_xv_image_sink_set_render_rectangle;
1335 }
1336 
1337 static const GList *
gst_xv_image_sink_colorbalance_list_channels(GstColorBalance * balance)1338 gst_xv_image_sink_colorbalance_list_channels (GstColorBalance * balance)
1339 {
1340   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1341 
1342   g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
1343 
1344   if (xvimagesink->context)
1345     return xvimagesink->context->channels_list;
1346   else
1347     return NULL;
1348 }
1349 
1350 static void
gst_xv_image_sink_colorbalance_set_value(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value)1351 gst_xv_image_sink_colorbalance_set_value (GstColorBalance * balance,
1352     GstColorBalanceChannel * channel, gint value)
1353 {
1354   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1355 
1356   g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1357   g_return_if_fail (channel->label != NULL);
1358 
1359   xvimagesink->config.cb_changed = TRUE;
1360 
1361   /* Normalize val to [-1000, 1000] */
1362   value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1363       (double) (channel->max_value - channel->min_value));
1364 
1365   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1366     xvimagesink->config.hue = value;
1367   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1368     xvimagesink->config.saturation = value;
1369   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1370     xvimagesink->config.contrast = value;
1371   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1372     xvimagesink->config.brightness = value;
1373   } else {
1374     g_warning ("got an unknown channel %s", channel->label);
1375     return;
1376   }
1377 
1378   gst_xv_image_sink_update_colorbalance (xvimagesink);
1379 }
1380 
1381 static gint
gst_xv_image_sink_colorbalance_get_value(GstColorBalance * balance,GstColorBalanceChannel * channel)1382 gst_xv_image_sink_colorbalance_get_value (GstColorBalance * balance,
1383     GstColorBalanceChannel * channel)
1384 {
1385   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1386   gint value = 0;
1387 
1388   g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), 0);
1389   g_return_val_if_fail (channel->label != NULL, 0);
1390 
1391   if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1392     value = xvimagesink->config.hue;
1393   } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1394     value = xvimagesink->config.saturation;
1395   } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1396     value = xvimagesink->config.contrast;
1397   } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1398     value = xvimagesink->config.brightness;
1399   } else {
1400     g_warning ("got an unknown channel %s", channel->label);
1401   }
1402 
1403   /* Normalize val to [channel->min_value, channel->max_value] */
1404   value = channel->min_value + (channel->max_value - channel->min_value) *
1405       (value + 1000) / 2000;
1406 
1407   return value;
1408 }
1409 
1410 static GstColorBalanceType
gst_xv_image_sink_colorbalance_get_balance_type(GstColorBalance * balance)1411 gst_xv_image_sink_colorbalance_get_balance_type (GstColorBalance * balance)
1412 {
1413   return GST_COLOR_BALANCE_HARDWARE;
1414 }
1415 
1416 static void
gst_xv_image_sink_colorbalance_init(GstColorBalanceInterface * iface)1417 gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface * iface)
1418 {
1419   iface->list_channels = gst_xv_image_sink_colorbalance_list_channels;
1420   iface->set_value = gst_xv_image_sink_colorbalance_set_value;
1421   iface->get_value = gst_xv_image_sink_colorbalance_get_value;
1422   iface->get_balance_type = gst_xv_image_sink_colorbalance_get_balance_type;
1423 }
1424 
1425 #if 0
1426 static const GList *
1427 gst_xv_image_sink_probe_get_properties (GstPropertyProbe * probe)
1428 {
1429   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1430   static GList *list = NULL;
1431 
1432   if (!list) {
1433     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1434     list =
1435         g_list_append (list, g_object_class_find_property (klass,
1436             "autopaint-colorkey"));
1437     list =
1438         g_list_append (list, g_object_class_find_property (klass,
1439             "double-buffer"));
1440     list =
1441         g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1442   }
1443 
1444   return list;
1445 }
1446 
1447 static void
1448 gst_xv_image_sink_probe_probe_property (GstPropertyProbe * probe,
1449     guint prop_id, const GParamSpec * pspec)
1450 {
1451   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1452 
1453   switch (prop_id) {
1454     case PROP_DEVICE:
1455     case PROP_AUTOPAINT_COLORKEY:
1456     case PROP_DOUBLE_BUFFER:
1457     case PROP_COLORKEY:
1458       GST_DEBUG_OBJECT (xvimagesink,
1459           "probing device list and get capabilities");
1460       if (!xvimagesink->context) {
1461         GST_DEBUG_OBJECT (xvimagesink, "generating context");
1462         xvimagesink->context = gst_xv_image_sink_context_get (xvimagesink);
1463       }
1464       break;
1465     default:
1466       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1467       break;
1468   }
1469 }
1470 
1471 static gboolean
1472 gst_xv_image_sink_probe_needs_probe (GstPropertyProbe * probe,
1473     guint prop_id, const GParamSpec * pspec)
1474 {
1475   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1476   gboolean ret = FALSE;
1477 
1478   switch (prop_id) {
1479     case PROP_DEVICE:
1480     case PROP_AUTOPAINT_COLORKEY:
1481     case PROP_DOUBLE_BUFFER:
1482     case PROP_COLORKEY:
1483       if (xvimagesink->context != NULL) {
1484         ret = FALSE;
1485       } else {
1486         ret = TRUE;
1487       }
1488       break;
1489     default:
1490       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1491       break;
1492   }
1493 
1494   return ret;
1495 }
1496 
1497 static GValueArray *
1498 gst_xv_image_sink_probe_get_values (GstPropertyProbe * probe,
1499     guint prop_id, const GParamSpec * pspec)
1500 {
1501   GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1502   GValueArray *array = NULL;
1503 
1504   if (G_UNLIKELY (!xvimagesink->context)) {
1505     GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1506         "get values");
1507     goto beach;
1508   }
1509 
1510   switch (prop_id) {
1511     case PROP_DEVICE:
1512     {
1513       guint i;
1514       GValue value = { 0 };
1515 
1516       array = g_value_array_new (xvimagesink->context->nb_adaptors);
1517       g_value_init (&value, G_TYPE_STRING);
1518 
1519       for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1520         gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1521 
1522         g_value_set_string (&value, adaptor_id_s);
1523         g_value_array_append (array, &value);
1524         g_free (adaptor_id_s);
1525       }
1526       g_value_unset (&value);
1527       break;
1528     }
1529     case PROP_AUTOPAINT_COLORKEY:
1530       if (xvimagesink->have_autopaint_colorkey) {
1531         GValue value = { 0 };
1532 
1533         array = g_value_array_new (2);
1534         g_value_init (&value, G_TYPE_BOOLEAN);
1535         g_value_set_boolean (&value, FALSE);
1536         g_value_array_append (array, &value);
1537         g_value_set_boolean (&value, TRUE);
1538         g_value_array_append (array, &value);
1539         g_value_unset (&value);
1540       }
1541       break;
1542     case PROP_DOUBLE_BUFFER:
1543       if (xvimagesink->have_double_buffer) {
1544         GValue value = { 0 };
1545 
1546         array = g_value_array_new (2);
1547         g_value_init (&value, G_TYPE_BOOLEAN);
1548         g_value_set_boolean (&value, FALSE);
1549         g_value_array_append (array, &value);
1550         g_value_set_boolean (&value, TRUE);
1551         g_value_array_append (array, &value);
1552         g_value_unset (&value);
1553       }
1554       break;
1555     case PROP_COLORKEY:
1556       if (xvimagesink->have_colorkey) {
1557         GValue value = { 0 };
1558 
1559         array = g_value_array_new (1);
1560         g_value_init (&value, GST_TYPE_INT_RANGE);
1561         gst_value_set_int_range (&value, 0, 0xffffff);
1562         g_value_array_append (array, &value);
1563         g_value_unset (&value);
1564       }
1565       break;
1566     default:
1567       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1568       break;
1569   }
1570 
1571 beach:
1572   return array;
1573 }
1574 
1575 static void
1576 gst_xv_image_sink_property_probe_interface_init (GstPropertyProbeInterface *
1577     iface)
1578 {
1579   iface->get_properties = gst_xv_image_sink_probe_get_properties;
1580   iface->probe_property = gst_xv_image_sink_probe_probe_property;
1581   iface->needs_probe = gst_xv_image_sink_probe_needs_probe;
1582   iface->get_values = gst_xv_image_sink_probe_get_values;
1583 }
1584 #endif
1585 
1586 /* =========================================== */
1587 /*                                             */
1588 /*              Init & Class init              */
1589 /*                                             */
1590 /* =========================================== */
1591 
1592 static void
gst_xv_image_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1593 gst_xv_image_sink_set_property (GObject * object, guint prop_id,
1594     const GValue * value, GParamSpec * pspec)
1595 {
1596   GstXvImageSink *xvimagesink;
1597 
1598   g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1599 
1600   xvimagesink = GST_XV_IMAGE_SINK (object);
1601 
1602   switch (prop_id) {
1603     case PROP_HUE:
1604       xvimagesink->config.hue = g_value_get_int (value);
1605       xvimagesink->config.cb_changed = TRUE;
1606       gst_xv_image_sink_update_colorbalance (xvimagesink);
1607       break;
1608     case PROP_CONTRAST:
1609       xvimagesink->config.contrast = g_value_get_int (value);
1610       xvimagesink->config.cb_changed = TRUE;
1611       gst_xv_image_sink_update_colorbalance (xvimagesink);
1612       break;
1613     case PROP_BRIGHTNESS:
1614       xvimagesink->config.brightness = g_value_get_int (value);
1615       xvimagesink->config.cb_changed = TRUE;
1616       gst_xv_image_sink_update_colorbalance (xvimagesink);
1617       break;
1618     case PROP_SATURATION:
1619       xvimagesink->config.saturation = g_value_get_int (value);
1620       xvimagesink->config.cb_changed = TRUE;
1621       gst_xv_image_sink_update_colorbalance (xvimagesink);
1622       break;
1623     case PROP_DISPLAY:
1624       g_free (xvimagesink->config.display_name);
1625       xvimagesink->config.display_name = g_value_dup_string (value);
1626       break;
1627     case PROP_SYNCHRONOUS:
1628       xvimagesink->synchronous = g_value_get_boolean (value);
1629       if (xvimagesink->context) {
1630         gst_xvcontext_set_synchronous (xvimagesink->context,
1631             xvimagesink->synchronous);
1632       }
1633       break;
1634     case PROP_PIXEL_ASPECT_RATIO:
1635       g_free (xvimagesink->par);
1636       xvimagesink->par = g_new0 (GValue, 1);
1637       g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1638       if (!g_value_transform (value, xvimagesink->par)) {
1639         g_warning ("Could not transform string to aspect ratio");
1640         gst_value_set_fraction (xvimagesink->par, 1, 1);
1641       }
1642       GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1643           gst_value_get_fraction_numerator (xvimagesink->par),
1644           gst_value_get_fraction_denominator (xvimagesink->par));
1645       break;
1646     case PROP_FORCE_ASPECT_RATIO:
1647       xvimagesink->keep_aspect = g_value_get_boolean (value);
1648       break;
1649     case PROP_HANDLE_EVENTS:
1650       gst_xv_image_sink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1651           g_value_get_boolean (value));
1652       gst_xv_image_sink_manage_event_thread (xvimagesink);
1653       break;
1654     case PROP_DEVICE:
1655       xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1656       break;
1657     case PROP_HANDLE_EXPOSE:
1658       xvimagesink->handle_expose = g_value_get_boolean (value);
1659       gst_xv_image_sink_manage_event_thread (xvimagesink);
1660       break;
1661     case PROP_DOUBLE_BUFFER:
1662       xvimagesink->double_buffer = g_value_get_boolean (value);
1663       break;
1664     case PROP_AUTOPAINT_COLORKEY:
1665       xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1666       break;
1667     case PROP_COLORKEY:
1668       xvimagesink->config.colorkey = g_value_get_int (value);
1669       break;
1670     case PROP_DRAW_BORDERS:
1671       xvimagesink->draw_borders = g_value_get_boolean (value);
1672       break;
1673     default:
1674       if (!gst_video_overlay_set_property (object, PROP_LAST, prop_id, value))
1675         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1676       break;
1677   }
1678 }
1679 
1680 static void
gst_xv_image_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1681 gst_xv_image_sink_get_property (GObject * object, guint prop_id,
1682     GValue * value, GParamSpec * pspec)
1683 {
1684   GstXvImageSink *xvimagesink;
1685 
1686   g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1687 
1688   xvimagesink = GST_XV_IMAGE_SINK (object);
1689 
1690   switch (prop_id) {
1691     case PROP_HUE:
1692       g_value_set_int (value, xvimagesink->config.hue);
1693       break;
1694     case PROP_CONTRAST:
1695       g_value_set_int (value, xvimagesink->config.contrast);
1696       break;
1697     case PROP_BRIGHTNESS:
1698       g_value_set_int (value, xvimagesink->config.brightness);
1699       break;
1700     case PROP_SATURATION:
1701       g_value_set_int (value, xvimagesink->config.saturation);
1702       break;
1703     case PROP_DISPLAY:
1704       g_value_set_string (value, xvimagesink->config.display_name);
1705       break;
1706     case PROP_SYNCHRONOUS:
1707       g_value_set_boolean (value, xvimagesink->synchronous);
1708       break;
1709     case PROP_PIXEL_ASPECT_RATIO:
1710       if (xvimagesink->par)
1711         g_value_transform (xvimagesink->par, value);
1712       break;
1713     case PROP_FORCE_ASPECT_RATIO:
1714       g_value_set_boolean (value, xvimagesink->keep_aspect);
1715       break;
1716     case PROP_HANDLE_EVENTS:
1717       g_value_set_boolean (value, xvimagesink->handle_events);
1718       break;
1719     case PROP_DEVICE:
1720     {
1721       char *adaptor_nr_s =
1722           g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1723 
1724       g_value_set_string (value, adaptor_nr_s);
1725       g_free (adaptor_nr_s);
1726       break;
1727     }
1728     case PROP_DEVICE_NAME:
1729       if (xvimagesink->context && xvimagesink->context->adaptors) {
1730         g_value_set_string (value,
1731             xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1732       } else {
1733         g_value_set_string (value, NULL);
1734       }
1735       break;
1736     case PROP_HANDLE_EXPOSE:
1737       g_value_set_boolean (value, xvimagesink->handle_expose);
1738       break;
1739     case PROP_DOUBLE_BUFFER:
1740       g_value_set_boolean (value, xvimagesink->double_buffer);
1741       break;
1742     case PROP_AUTOPAINT_COLORKEY:
1743       g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1744       break;
1745     case PROP_COLORKEY:
1746       g_value_set_int (value, xvimagesink->config.colorkey);
1747       break;
1748     case PROP_DRAW_BORDERS:
1749       g_value_set_boolean (value, xvimagesink->draw_borders);
1750       break;
1751     case PROP_WINDOW_WIDTH:
1752       if (xvimagesink->xwindow)
1753         g_value_set_uint64 (value, xvimagesink->xwindow->width);
1754       else
1755         g_value_set_uint64 (value, 0);
1756       break;
1757     case PROP_WINDOW_HEIGHT:
1758       if (xvimagesink->xwindow)
1759         g_value_set_uint64 (value, xvimagesink->xwindow->height);
1760       else
1761         g_value_set_uint64 (value, 0);
1762       break;
1763     default:
1764       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1765       break;
1766   }
1767 }
1768 
1769 static gboolean
gst_xv_image_sink_open(GstXvImageSink * xvimagesink)1770 gst_xv_image_sink_open (GstXvImageSink * xvimagesink)
1771 {
1772   GError *error = NULL;
1773 
1774   /* Initializing the XvContext unless already done through GstVideoOverlay */
1775   if (!xvimagesink->context) {
1776     GstXvContext *context;
1777     if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1778       goto no_context;
1779 
1780     GST_OBJECT_LOCK (xvimagesink);
1781     xvimagesink->context = context;
1782   } else
1783     GST_OBJECT_LOCK (xvimagesink);
1784   /* make an allocator for this context */
1785   xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1786   GST_OBJECT_UNLOCK (xvimagesink);
1787 
1788   /* update object's par with calculated one if not set yet */
1789   if (!xvimagesink->par) {
1790     xvimagesink->par = g_new0 (GValue, 1);
1791     gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1792     GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1793   }
1794   /* call XSynchronize with the current value of synchronous */
1795   gst_xvcontext_set_synchronous (xvimagesink->context,
1796       xvimagesink->synchronous);
1797   gst_xv_image_sink_update_colorbalance (xvimagesink);
1798   gst_xv_image_sink_manage_event_thread (xvimagesink);
1799 
1800   return TRUE;
1801 
1802 no_context:
1803   {
1804     gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1805         error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1806         g_strdup (error->message), __FILE__, GST_FUNCTION, __LINE__);
1807     g_clear_error (&error);
1808     return FALSE;
1809   }
1810 }
1811 
1812 static void
gst_xv_image_sink_close(GstXvImageSink * xvimagesink)1813 gst_xv_image_sink_close (GstXvImageSink * xvimagesink)
1814 {
1815   GThread *thread;
1816   GstXvContext *context;
1817 
1818   GST_OBJECT_LOCK (xvimagesink);
1819   xvimagesink->running = FALSE;
1820   /* grab thread and mark it as NULL */
1821   thread = xvimagesink->event_thread;
1822   xvimagesink->event_thread = NULL;
1823   GST_OBJECT_UNLOCK (xvimagesink);
1824 
1825   /* Wait for our event thread to finish before we clean up our stuff. */
1826   if (thread)
1827     g_thread_join (thread);
1828 
1829   if (xvimagesink->cur_image) {
1830     gst_buffer_unref (xvimagesink->cur_image);
1831     xvimagesink->cur_image = NULL;
1832   }
1833 
1834   g_mutex_lock (&xvimagesink->flow_lock);
1835 
1836   if (xvimagesink->pool) {
1837     gst_object_unref (xvimagesink->pool);
1838     xvimagesink->pool = NULL;
1839   }
1840 
1841   if (xvimagesink->xwindow) {
1842     gst_xwindow_clear (xvimagesink->xwindow);
1843     gst_xwindow_destroy (xvimagesink->xwindow);
1844     xvimagesink->xwindow = NULL;
1845   }
1846   g_mutex_unlock (&xvimagesink->flow_lock);
1847 
1848   if (xvimagesink->allocator) {
1849     gst_object_unref (xvimagesink->allocator);
1850     xvimagesink->allocator = NULL;
1851   }
1852 
1853   GST_OBJECT_LOCK (xvimagesink);
1854   /* grab context and mark it as NULL */
1855   context = xvimagesink->context;
1856   xvimagesink->context = NULL;
1857   GST_OBJECT_UNLOCK (xvimagesink);
1858 
1859   if (context)
1860     gst_xvcontext_unref (context);
1861 }
1862 
1863 /* Finalize is called only once, dispose can be called multiple times.
1864  * We use mutexes and don't reset stuff to NULL here so let's register
1865  * as a finalize. */
1866 static void
gst_xv_image_sink_finalize(GObject * object)1867 gst_xv_image_sink_finalize (GObject * object)
1868 {
1869   GstXvImageSink *xvimagesink;
1870 
1871   xvimagesink = GST_XV_IMAGE_SINK (object);
1872 
1873   gst_xv_image_sink_close (xvimagesink);
1874 
1875   gst_xvcontext_config_clear (&xvimagesink->config);
1876 
1877   if (xvimagesink->par) {
1878     g_free (xvimagesink->par);
1879     xvimagesink->par = NULL;
1880   }
1881   g_mutex_clear (&xvimagesink->flow_lock);
1882   g_free (xvimagesink->media_title);
1883 
1884   G_OBJECT_CLASS (parent_class)->finalize (object);
1885 }
1886 
1887 static void
gst_xv_image_sink_init(GstXvImageSink * xvimagesink)1888 gst_xv_image_sink_init (GstXvImageSink * xvimagesink)
1889 {
1890   xvimagesink->config.display_name = NULL;
1891   xvimagesink->config.adaptor_nr = 0;
1892   xvimagesink->config.autopaint_colorkey = TRUE;
1893   xvimagesink->config.double_buffer = TRUE;
1894   /* on 16bit displays this becomes r,g,b = 1,2,3
1895    * on 24bit displays this becomes r,g,b = 8,8,16
1896    * as a port atom value */
1897   xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1898   xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1899   xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1900   xvimagesink->config.cb_changed = FALSE;
1901 
1902   xvimagesink->context = NULL;
1903   xvimagesink->xwindow = NULL;
1904   xvimagesink->cur_image = NULL;
1905 
1906   xvimagesink->fps_n = 0;
1907   xvimagesink->fps_d = 0;
1908   xvimagesink->video_width = 0;
1909   xvimagesink->video_height = 0;
1910 
1911   g_mutex_init (&xvimagesink->flow_lock);
1912 
1913   xvimagesink->pool = NULL;
1914 
1915   xvimagesink->synchronous = FALSE;
1916   xvimagesink->running = FALSE;
1917   xvimagesink->keep_aspect = TRUE;
1918   xvimagesink->handle_events = TRUE;
1919   xvimagesink->par = NULL;
1920   xvimagesink->handle_expose = TRUE;
1921 
1922   xvimagesink->draw_borders = TRUE;
1923 }
1924 
1925 static void
gst_xv_image_sink_class_init(GstXvImageSinkClass * klass)1926 gst_xv_image_sink_class_init (GstXvImageSinkClass * klass)
1927 {
1928   GObjectClass *gobject_class;
1929   GstElementClass *gstelement_class;
1930   GstBaseSinkClass *gstbasesink_class;
1931   GstVideoSinkClass *videosink_class;
1932 
1933   gobject_class = (GObjectClass *) klass;
1934   gstelement_class = (GstElementClass *) klass;
1935   gstbasesink_class = (GstBaseSinkClass *) klass;
1936   videosink_class = (GstVideoSinkClass *) klass;
1937 
1938   parent_class = g_type_class_peek_parent (klass);
1939 
1940   gobject_class->set_property = gst_xv_image_sink_set_property;
1941   gobject_class->get_property = gst_xv_image_sink_get_property;
1942 
1943   g_object_class_install_property (gobject_class, PROP_CONTRAST,
1944       g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1945           -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1946   g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1947       g_param_spec_int ("brightness", "Brightness",
1948           "The brightness of the video", -1000, 1000, 0,
1949           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950   g_object_class_install_property (gobject_class, PROP_HUE,
1951       g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1952           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953   g_object_class_install_property (gobject_class, PROP_SATURATION,
1954       g_param_spec_int ("saturation", "Saturation",
1955           "The saturation of the video", -1000, 1000, 0,
1956           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1958       g_param_spec_string ("display", "Display", "X Display name", NULL,
1959           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1960   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1961       g_param_spec_boolean ("synchronous", "Synchronous",
1962           "When enabled, runs the X display in synchronous mode. "
1963           "(unrelated to A/V sync, used only for debugging)", FALSE,
1964           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1965   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1966       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1967           "The pixel aspect ratio of the device", "1/1",
1968           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1969   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1970       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1971           "When enabled, scaling will respect original aspect ratio", TRUE,
1972           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1973   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1974       g_param_spec_boolean ("handle-events", "Handle XEvents",
1975           "When enabled, XEvents will be selected and handled", TRUE,
1976           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1977   g_object_class_install_property (gobject_class, PROP_DEVICE,
1978       g_param_spec_string ("device", "Adaptor number",
1979           "The number of the video adaptor", "0",
1980           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1981   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1982       g_param_spec_string ("device-name", "Adaptor name",
1983           "The name of the video adaptor", NULL,
1984           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1985 
1986   gst_video_overlay_install_properties (gobject_class, PROP_LAST);
1987 
1988   /**
1989    * GstXvImageSink:handle-expose
1990    *
1991    * When enabled, the current frame will always be drawn in response to X
1992    * Expose.
1993    */
1994   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1995       g_param_spec_boolean ("handle-expose", "Handle expose",
1996           "When enabled, "
1997           "the current frame will always be drawn in response to X Expose "
1998           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1999 
2000   /**
2001    * GstXvImageSink:double-buffer
2002    *
2003    * Whether to double-buffer the output.
2004    */
2005   g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2006       g_param_spec_boolean ("double-buffer", "Double-buffer",
2007           "Whether to double-buffer the output", TRUE,
2008           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2009   /**
2010    * GstXvImageSink:autopaint-colorkey
2011    *
2012    * Whether to autofill overlay with colorkey
2013    */
2014   g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2015       g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2016           "Whether to autofill overlay with colorkey", TRUE,
2017           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2018   /**
2019    * GstXvImageSink:colorkey
2020    *
2021    * Color to use for the overlay mask.
2022    */
2023   g_object_class_install_property (gobject_class, PROP_COLORKEY,
2024       g_param_spec_int ("colorkey", "Colorkey",
2025           "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2026           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2027 
2028   /**
2029    * GstXvImageSink:draw-borders
2030    *
2031    * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2032    * unused parts of the video area.
2033    */
2034   g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2035       g_param_spec_boolean ("draw-borders", "Draw Borders",
2036           "Draw black borders to fill unused area in force-aspect-ratio mode",
2037           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2038 
2039   /**
2040    * GstXvImageSink:window-width
2041    *
2042    * Actual width of the video window.
2043    */
2044   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2045       g_param_spec_uint64 ("window-width", "window-width",
2046           "Width of the window", 0, G_MAXUINT64, 0,
2047           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2048 
2049   /**
2050    * GstXvImageSink:window-height
2051    *
2052    * Actual height of the video window.
2053    */
2054   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2055       g_param_spec_uint64 ("window-height", "window-height",
2056           "Height of the window", 0, G_MAXUINT64, 0,
2057           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2058 
2059   gobject_class->finalize = gst_xv_image_sink_finalize;
2060 
2061   gst_element_class_set_static_metadata (gstelement_class,
2062       "Video sink", "Sink/Video",
2063       "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2064 
2065   gst_element_class_add_static_pad_template (gstelement_class,
2066       &gst_xv_image_sink_sink_template_factory);
2067 
2068   gstelement_class->change_state =
2069       GST_DEBUG_FUNCPTR (gst_xv_image_sink_change_state);
2070 
2071   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_getcaps);
2072   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_setcaps);
2073   gstbasesink_class->get_times =
2074       GST_DEBUG_FUNCPTR (gst_xv_image_sink_get_times);
2075   gstbasesink_class->propose_allocation =
2076       GST_DEBUG_FUNCPTR (gst_xv_image_sink_propose_allocation);
2077   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xv_image_sink_event);
2078 
2079   videosink_class->show_frame =
2080       GST_DEBUG_FUNCPTR (gst_xv_image_sink_show_frame);
2081 
2082   GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
2083 }
2084