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