vaapisink: clean-ups (indentation, drop unused variables).
[platform/upstream/gstreamer.git] / gst / vaapi / gstvaapisink.c
1 /*
2  *  gstvaapisink.c - VA-API video sink
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6  *  Copyright (C) 2011-2014 Intel Corporation
7  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public License
11  *  as published by the Free Software Foundation; either version 2.1
12  *  of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free
21  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301 USA
23  */
24
25 /**
26  * SECTION:gstvaapisink
27  * @short_description: A VA-API based videosink
28  *
29  * vaapisink renders video frames to a drawable (X #Window) on a local
30  * display using the Video Acceleration (VA) API. The element will
31  * create its own internal window and render into it.
32  */
33
34 #include "gst/vaapi/sysdeps.h"
35 #include <gst/gst.h>
36 #include <gst/video/video.h>
37
38 #include <gst/vaapi/gstvaapivalue.h>
39
40 /* Supported interfaces */
41 #if GST_CHECK_VERSION(1,0,0)
42 # include <gst/video/videooverlay.h>
43 # include <gst/video/colorbalance.h>
44 # include <gst/video/navigation.h>
45 #else
46 # include <gst/interfaces/xoverlay.h>
47 # include <gst/interfaces/colorbalance.h>
48
49 # define GST_TYPE_VIDEO_OVERLAY         GST_TYPE_X_OVERLAY
50 # define GST_VIDEO_OVERLAY              GST_X_OVERLAY
51 # define GstVideoOverlay                GstXOverlay
52 # define GstVideoOverlayInterface       GstXOverlayClass
53
54 # define gst_video_overlay_prepare_window_handle(sink) \
55     gst_x_overlay_prepare_xwindow_id(sink)
56 # define gst_video_overlay_got_window_handle(sink, window_handle) \
57     gst_x_overlay_got_window_handle(sink, window_handle)
58
59 # define GstColorBalanceInterface       GstColorBalanceClass
60 #endif
61
62 #include "gstvaapisink.h"
63 #include "gstvaapipluginutil.h"
64 #include "gstvaapivideometa.h"
65 #if GST_CHECK_VERSION(1,0,0)
66 #include "gstvaapivideobufferpool.h"
67 #include "gstvaapivideomemory.h"
68 #endif
69
70 #define GST_PLUGIN_NAME "vaapisink"
71 #define GST_PLUGIN_DESC "A VA-API based videosink"
72
73 GST_DEBUG_CATEGORY_STATIC (gst_debug_vaapisink);
74 #define GST_CAT_DEFAULT gst_debug_vaapisink
75
76 /* Default template */
77 /* *INDENT-OFF* */
78 static const char gst_vaapisink_sink_caps_str[] =
79 #if GST_CHECK_VERSION(1,1,0)
80     GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE,
81         "{ ENCODED, NV12, I420, YV12 }") ";"
82     GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL);
83 #else
84 #if GST_CHECK_VERSION(1,0,0)
85     GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) "; "
86 #else
87     "video/x-raw-yuv, "
88     "width  = (int) [ 1, MAX ], "
89     "height = (int) [ 1, MAX ]; "
90 #endif
91     GST_VAAPI_SURFACE_CAPS;
92 #endif
93 /* *INDENT-ON* */
94
95 static GstStaticPadTemplate gst_vaapisink_sink_factory =
96 GST_STATIC_PAD_TEMPLATE ("sink",
97     GST_PAD_SINK,
98     GST_PAD_ALWAYS,
99     GST_STATIC_CAPS (gst_vaapisink_sink_caps_str));
100
101 static gboolean
102 gst_vaapisink_has_interface (GstVaapiPluginBase * plugin, GType type)
103 {
104   return type == GST_TYPE_VIDEO_OVERLAY || type == GST_TYPE_COLOR_BALANCE;
105 }
106
107 static void
108 gst_vaapisink_video_overlay_iface_init (GstVideoOverlayInterface * iface);
109
110 static void
111 gst_vaapisink_color_balance_iface_init (GstColorBalanceInterface * iface);
112
113 static void
114 gst_vaapisink_navigation_iface_init (GstNavigationInterface * iface);
115
116 G_DEFINE_TYPE_WITH_CODE (GstVaapiSink,
117     gst_vaapisink,
118     GST_TYPE_VIDEO_SINK,
119     GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
120     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
121         gst_vaapisink_video_overlay_iface_init);
122     G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
123         gst_vaapisink_color_balance_iface_init);
124     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
125         gst_vaapisink_navigation_iface_init));
126
127 enum
128 {
129   PROP_0,
130
131   PROP_DISPLAY_TYPE,
132   PROP_DISPLAY_NAME,
133   PROP_FULLSCREEN,
134   PROP_ROTATION,
135   PROP_FORCE_ASPECT_RATIO,
136   PROP_VIEW_ID,
137   PROP_HUE,
138   PROP_SATURATION,
139   PROP_BRIGHTNESS,
140   PROP_CONTRAST,
141
142   N_PROPERTIES
143 };
144
145 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
146 #define DEFAULT_ROTATION                GST_VAAPI_ROTATION_0
147
148 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
149
150 static void
151 gst_vaapisink_video_overlay_expose (GstVideoOverlay * overlay);
152
153 static gboolean
154 gst_vaapisink_reconfigure_window (GstVaapiSink * sink);
155
156 static void
157 gst_vaapisink_set_event_handling (GstVaapiSink * sink, gboolean handle_events);
158
159 static GstFlowReturn
160 gst_vaapisink_show_frame (GstBaseSink * base_sink, GstBuffer * buffer);
161
162 static gboolean
163 gst_vaapisink_ensure_render_rect (GstVaapiSink * sink, guint width,
164     guint height);
165
166 static inline gboolean
167 gst_vaapisink_ensure_display (GstVaapiSink * sink)
168 {
169   return gst_vaapi_plugin_base_ensure_display (GST_VAAPI_PLUGIN_BASE (sink));
170 }
171
172 static inline gboolean
173 gst_vaapisink_render_surface (GstVaapiSink * sink, GstVaapiSurface * surface,
174     const GstVaapiRectangle * surface_rect, guint flags)
175 {
176   return sink->window && gst_vaapi_window_put_surface (sink->window, surface,
177       surface_rect, &sink->display_rect, flags);
178 }
179
180 /* ------------------------------------------------------------------------ */
181 /* --- DRM Backend                                                      --- */
182 /* ------------------------------------------------------------------------ */
183
184 #if USE_DRM
185 #include <gst/vaapi/gstvaapidisplay_drm.h>
186
187 static gboolean
188 gst_vaapisink_drm_create_window (GstVaapiSink * sink, guint width, guint height)
189 {
190   g_return_val_if_fail (sink->window == NULL, FALSE);
191
192   GST_ERROR ("failed to create a window for VA/DRM display");
193   return FALSE;
194 }
195
196 static gboolean
197 gst_vaapisink_drm_render_surface (GstVaapiSink * sink,
198     GstVaapiSurface * surface, const GstVaapiRectangle * surface_rect,
199     guint flags)
200 {
201   return TRUE;
202 }
203
204 static const inline GstVaapiSinkBackend *
205 gst_vaapisink_backend_drm (void)
206 {
207   static const GstVaapiSinkBackend GstVaapiSinkBackendDRM = {
208     .create_window = gst_vaapisink_drm_create_window,
209     .render_surface = gst_vaapisink_drm_render_surface,
210   };
211   return &GstVaapiSinkBackendDRM;
212 }
213 #endif
214
215 /* ------------------------------------------------------------------------ */
216 /* --- X11 Backend                                                      --- */
217 /* ------------------------------------------------------------------------ */
218
219 #if USE_X11
220 #include <gst/vaapi/gstvaapidisplay_x11.h>
221 #include <gst/vaapi/gstvaapiwindow_x11.h>
222
223 #if HAVE_XKBLIB
224 # include <X11/XKBlib.h>
225 #endif
226
227 static inline KeySym
228 x11_keycode_to_keysym (Display * dpy, unsigned int kc)
229 {
230 #if HAVE_XKBLIB
231   return XkbKeycodeToKeysym (dpy, kc, 0, 0);
232 #else
233   return XKeycodeToKeysym (dpy, kc, 0);
234 #endif
235 }
236
237 /* Checks whether a ConfigureNotify event is in the queue */
238 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
239 struct _ConfigureNotifyEventPendingArgs
240 {
241   Window window;
242   guint width;
243   guint height;
244   gboolean match;
245 };
246
247 static Bool
248 configure_notify_event_pending_cb (Display * dpy, XEvent * xev, XPointer arg)
249 {
250   ConfigureNotifyEventPendingArgs *const args =
251       (ConfigureNotifyEventPendingArgs *) arg;
252
253   if (xev->type == ConfigureNotify &&
254       xev->xconfigure.window == args->window &&
255       xev->xconfigure.width == args->width &&
256       xev->xconfigure.height == args->height)
257     args->match = TRUE;
258
259   /* XXX: this is a hack to traverse the whole queue because we
260      can't use XPeekIfEvent() since it could block */
261   return False;
262 }
263
264 static gboolean
265 configure_notify_event_pending (GstVaapiSink * sink, Window window,
266     guint width, guint height)
267 {
268   GstVaapiDisplayX11 *const display =
269       GST_VAAPI_DISPLAY_X11 (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
270   ConfigureNotifyEventPendingArgs args;
271   XEvent xev;
272
273   args.window = window;
274   args.width = width;
275   args.height = height;
276   args.match = FALSE;
277
278   /* XXX: don't use XPeekIfEvent() because it might block */
279   XCheckIfEvent (gst_vaapi_display_x11_get_display (display),
280       &xev, configure_notify_event_pending_cb, (XPointer) & args);
281   return args.match;
282 }
283
284 static gboolean
285 gst_vaapisink_x11_create_window (GstVaapiSink * sink, guint width, guint height)
286 {
287   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
288
289   g_return_val_if_fail (sink->window == NULL, FALSE);
290
291   sink->window = gst_vaapi_window_x11_new (display, width, height);
292   if (!sink->window)
293     return FALSE;
294
295   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (sink),
296       gst_vaapi_window_x11_get_xid (GST_VAAPI_WINDOW_X11 (sink->window)));
297   return TRUE;
298 }
299
300 static gboolean
301 gst_vaapisink_x11_create_window_from_handle (GstVaapiSink * sink,
302     guintptr window)
303 {
304   GstVaapiDisplay *display;
305   Window rootwin;
306   unsigned int width, height, border_width, depth;
307   int x, y;
308   XID xid = window;
309
310   if (!gst_vaapisink_ensure_display (sink))
311     return FALSE;
312   display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
313
314   gst_vaapi_display_lock (display);
315   XGetGeometry (gst_vaapi_display_x11_get_display (GST_VAAPI_DISPLAY_X11
316           (display)), xid, &rootwin, &x, &y, &width, &height, &border_width,
317       &depth);
318   gst_vaapi_display_unlock (display);
319
320   if ((width != sink->window_width || height != sink->window_height) &&
321       !configure_notify_event_pending (sink, xid, width, height)) {
322     if (!gst_vaapisink_ensure_render_rect (sink, width, height))
323       return FALSE;
324     sink->window_width = width;
325     sink->window_height = height;
326   }
327
328   if (!sink->window
329       || gst_vaapi_window_x11_get_xid (GST_VAAPI_WINDOW_X11 (sink->window)) !=
330       xid) {
331     gst_vaapi_window_replace (&sink->window, NULL);
332     sink->window = gst_vaapi_window_x11_new_with_xid (display, xid);
333     if (!sink->window)
334       return FALSE;
335   }
336
337   gst_vaapisink_set_event_handling (sink, sink->handle_events);
338   return TRUE;
339 }
340
341 static gboolean
342 gst_vaapisink_x11_handle_events (GstVaapiSink * sink)
343 {
344   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
345   gboolean has_events, do_expose = FALSE;
346   guint pointer_x = 0, pointer_y = 0;
347   gboolean pointer_moved = FALSE;
348   XEvent e;
349
350   if (sink->window) {
351     Display *const x11_dpy =
352         gst_vaapi_display_x11_get_display (GST_VAAPI_DISPLAY_X11 (display));
353     Window x11_win =
354         gst_vaapi_window_x11_get_xid (GST_VAAPI_WINDOW_X11 (sink->window));
355
356     /* Track MousePointer interaction */
357     for (;;) {
358       gst_vaapi_display_lock (display);
359       has_events = XCheckWindowEvent (x11_dpy, x11_win, PointerMotionMask, &e);
360       gst_vaapi_display_unlock (display);
361       if (!has_events)
362         break;
363
364       switch (e.type) {
365         case MotionNotify:
366           pointer_x = e.xmotion.x;
367           pointer_y = e.xmotion.y;
368           pointer_moved = TRUE;
369           break;
370         default:
371           break;
372       }
373     }
374     if (pointer_moved) {
375       gst_vaapi_display_lock (display);
376       gst_navigation_send_mouse_event (GST_NAVIGATION (sink),
377           "mouse-move", 0, pointer_x, pointer_y);
378       gst_vaapi_display_unlock (display);
379     }
380
381     /* Track KeyPress, KeyRelease, ButtonPress, ButtonRelease */
382     for (;;) {
383       KeySym keysym;
384       const char *key_str = NULL;
385       gst_vaapi_display_lock (display);
386       has_events = XCheckWindowEvent (x11_dpy, x11_win,
387           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
388           &e);
389       gst_vaapi_display_unlock (display);
390       if (!has_events)
391         break;
392
393       switch (e.type) {
394         case ButtonPress:
395           gst_navigation_send_mouse_event (GST_NAVIGATION (sink),
396               "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
397           break;
398         case ButtonRelease:
399           gst_navigation_send_mouse_event (GST_NAVIGATION (sink),
400               "mouse-button-release", e.xbutton.button, e.xbutton.x,
401               e.xbutton.y);
402           break;
403         case KeyPress:
404         case KeyRelease:
405           gst_vaapi_display_lock (display);
406           keysym = x11_keycode_to_keysym (x11_dpy, e.xkey.keycode);
407           if (keysym != NoSymbol) {
408             key_str = XKeysymToString (keysym);
409           } else {
410             key_str = "unknown";
411           }
412           gst_vaapi_display_unlock (display);
413           gst_navigation_send_key_event (GST_NAVIGATION (sink),
414               e.type == KeyPress ? "key-press" : "key-release", key_str);
415           break;
416         default:
417           break;
418       }
419     }
420
421     /* Handle Expose + ConfigureNotify */
422     /* Need to lock whole loop or we corrupt the XEvent queue: */
423     for (;;) {
424       gst_vaapi_display_lock (display);
425       has_events = XCheckWindowEvent (x11_dpy, x11_win,
426           StructureNotifyMask | ExposureMask, &e);
427       gst_vaapi_display_unlock (display);
428       if (!has_events)
429         break;
430
431       switch (e.type) {
432         case Expose:
433           do_expose = TRUE;
434           break;
435         case ConfigureNotify:
436           if (gst_vaapisink_reconfigure_window (sink))
437             do_expose = TRUE;
438           break;
439         default:
440           break;
441       }
442     }
443     if (do_expose)
444       gst_vaapisink_video_overlay_expose (GST_VIDEO_OVERLAY (sink));
445   }
446   return TRUE;
447 }
448
449 static gboolean
450 gst_vaapisink_x11_pre_start_event_thread (GstVaapiSink * sink)
451 {
452   GstVaapiDisplayX11 *const display =
453       GST_VAAPI_DISPLAY_X11 (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
454   static const int x11_event_mask = (KeyPressMask | KeyReleaseMask |
455       ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
456       ExposureMask | StructureNotifyMask);
457
458   if (sink->window) {
459     XSelectInput (gst_vaapi_display_x11_get_display (display),
460         gst_vaapi_window_x11_get_xid (GST_VAAPI_WINDOW_X11 (sink->window)),
461         x11_event_mask);
462   }
463   return TRUE;
464 }
465
466 static gboolean
467 gst_vaapisink_x11_pre_stop_event_thread (GstVaapiSink * sink)
468 {
469   GstVaapiDisplayX11 *const display =
470       GST_VAAPI_DISPLAY_X11 (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
471
472   if (sink->window) {
473     XSelectInput (gst_vaapi_display_x11_get_display (display),
474         gst_vaapi_window_x11_get_xid (GST_VAAPI_WINDOW_X11 (sink->window)), 0);
475   }
476   return TRUE;
477 }
478
479 static const inline GstVaapiSinkBackend *
480 gst_vaapisink_backend_x11 (void)
481 {
482   static const GstVaapiSinkBackend GstVaapiSinkBackendX11 = {
483     .create_window = gst_vaapisink_x11_create_window,
484     .create_window_from_handle = gst_vaapisink_x11_create_window_from_handle,
485     .render_surface = gst_vaapisink_render_surface,
486
487     .event_thread_needed = TRUE,
488     .handle_events = gst_vaapisink_x11_handle_events,
489     .pre_start_event_thread = gst_vaapisink_x11_pre_start_event_thread,
490     .pre_stop_event_thread = gst_vaapisink_x11_pre_stop_event_thread,
491   };
492   return &GstVaapiSinkBackendX11;
493 }
494 #endif
495
496 /* ------------------------------------------------------------------------ */
497 /* --- Wayland Backend                                                  --- */
498 /* ------------------------------------------------------------------------ */
499
500 #if USE_WAYLAND
501 #include <gst/vaapi/gstvaapidisplay_wayland.h>
502 #include <gst/vaapi/gstvaapiwindow_wayland.h>
503
504 static gboolean
505 gst_vaapisink_wayland_create_window (GstVaapiSink * sink, guint width,
506     guint height)
507 {
508   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
509
510   g_return_val_if_fail (sink->window == NULL, FALSE);
511
512   sink->window = gst_vaapi_window_wayland_new (display, width, height);
513   if (!sink->window)
514     return FALSE;
515   return TRUE;
516 }
517
518 static const inline GstVaapiSinkBackend *
519 gst_vaapisink_backend_wayland (void)
520 {
521   static const GstVaapiSinkBackend GstVaapiSinkBackendWayland = {
522     .create_window = gst_vaapisink_wayland_create_window,
523     .render_surface = gst_vaapisink_render_surface,
524   };
525   return &GstVaapiSinkBackendWayland;
526 }
527 #endif
528
529 /* ------------------------------------------------------------------------ */
530 /* --- GstVideoOverlay interface                                        --- */
531 /* ------------------------------------------------------------------------ */
532
533 static void
534 gst_vaapisink_video_overlay_set_window_handle (GstVideoOverlay * overlay,
535     guintptr window)
536 {
537   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
538   GstVaapiDisplayType display_type;
539
540   if (!gst_vaapisink_ensure_display (sink))
541     return;
542
543   display_type = GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE (sink);
544
545   /* Disable GLX rendering when vaapisink is using a foreign X
546      window. It's pretty much useless */
547   if (display_type == GST_VAAPI_DISPLAY_TYPE_GLX) {
548     display_type = GST_VAAPI_DISPLAY_TYPE_X11;
549     gst_vaapi_plugin_base_set_display_type (GST_VAAPI_PLUGIN_BASE (sink),
550         display_type);
551   }
552
553   sink->foreign_window = TRUE;
554   if (sink->backend->create_window_from_handle)
555     sink->backend->create_window_from_handle (sink, window);
556 }
557
558 static void
559 gst_vaapisink_video_overlay_set_render_rectangle (GstVideoOverlay * overlay,
560     gint x, gint y, gint width, gint height)
561 {
562   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
563   GstVaapiRectangle *const display_rect = &sink->display_rect;
564
565   display_rect->x = x;
566   display_rect->y = y;
567   display_rect->width = width;
568   display_rect->height = height;
569
570   GST_DEBUG ("render rect (%d,%d):%ux%u",
571       display_rect->x, display_rect->y,
572       display_rect->width, display_rect->height);
573 }
574
575 static void
576 gst_vaapisink_video_overlay_expose (GstVideoOverlay * overlay)
577 {
578   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
579
580   if (sink->video_buffer) {
581     gst_vaapisink_reconfigure_window (sink);
582     gst_vaapisink_show_frame (GST_BASE_SINK_CAST (sink), sink->video_buffer);
583   }
584 }
585
586 static void
587 gst_vaapisink_video_overlay_set_event_handling (GstVideoOverlay * overlay,
588     gboolean handle_events)
589 {
590   GstVaapiSink *const sink = GST_VAAPISINK (overlay);
591
592   gst_vaapisink_set_event_handling (sink, handle_events);
593 }
594
595 static void
596 gst_vaapisink_video_overlay_iface_init (GstVideoOverlayInterface * iface)
597 {
598   iface->set_window_handle = gst_vaapisink_video_overlay_set_window_handle;
599   iface->set_render_rectangle =
600       gst_vaapisink_video_overlay_set_render_rectangle;
601   iface->expose = gst_vaapisink_video_overlay_expose;
602   iface->handle_events = gst_vaapisink_video_overlay_set_event_handling;
603 }
604
605 /* ------------------------------------------------------------------------ */
606 /* --- GstColorBalance interface                                        --- */
607 /* ------------------------------------------------------------------------ */
608
609 enum
610 {
611   CB_HUE = 1,
612   CB_SATURATION,
613   CB_BRIGHTNESS,
614   CB_CONTRAST
615 };
616
617 typedef struct
618 {
619   guint cb_id;
620   const gchar *prop_name;
621   const gchar *channel_name;
622 } ColorBalanceMap;
623
624 static const ColorBalanceMap cb_map[4] = {
625   {CB_HUE, GST_VAAPI_DISPLAY_PROP_HUE, "VA_HUE"},
626   {CB_SATURATION, GST_VAAPI_DISPLAY_PROP_SATURATION, "VA_SATURATION"},
627   {CB_BRIGHTNESS, GST_VAAPI_DISPLAY_PROP_BRIGHTNESS, "VA_BRIGHTNESS"},
628   {CB_CONTRAST, GST_VAAPI_DISPLAY_PROP_CONTRAST, "VA_CONTRAST"}
629 };
630
631 static guint
632 cb_get_id_from_channel_name (GstVaapiSink * sink, const gchar * name)
633 {
634   guint i;
635
636   for (i = 0; i < G_N_ELEMENTS (sink->cb_values); i++) {
637     if (g_ascii_strcasecmp (cb_map[i].channel_name, name) == 0)
638       return cb_map[i].cb_id;
639   }
640
641   GST_WARNING ("got an unknown channel %s", name);
642   return 0;
643 }
644
645 static inline GValue *
646 cb_get_gvalue (GstVaapiSink * sink, guint id)
647 {
648   g_return_val_if_fail ((guint) (id - CB_HUE) < G_N_ELEMENTS (sink->cb_values),
649       NULL);
650
651   return &sink->cb_values[id - CB_HUE];
652 }
653
654 static gboolean
655 cb_set_gvalue (GstVaapiSink * sink, guint id, const GValue * value)
656 {
657   GValue *const v_value = cb_get_gvalue (sink, id);
658
659   if (!v_value)
660     return FALSE;
661
662   g_value_set_float (v_value, g_value_get_float (value));
663   sink->cb_changed |= (1U << id);
664   return TRUE;
665 }
666
667 static inline gfloat
668 cb_get_value (GstVaapiSink * sink, guint id)
669 {
670   const GValue *const v_value = cb_get_gvalue (sink, id);
671
672   return v_value ? g_value_get_float (v_value) : 0.0;
673 }
674
675 static gboolean
676 cb_set_value (GstVaapiSink * sink, guint id, gfloat value)
677 {
678   GValue v_value = G_VALUE_INIT;
679   gboolean success;
680
681   g_value_init (&v_value, G_TYPE_FLOAT);
682   g_value_set_float (&v_value, value);
683   success = cb_set_gvalue (sink, id, &v_value);
684   g_value_unset (&v_value);
685   return success;
686 }
687
688 static gboolean
689 cb_sync_values_from_display (GstVaapiSink * sink, GstVaapiDisplay * display)
690 {
691   GValue v_value = G_VALUE_INIT;
692   guint i, failures = 0;
693
694   for (i = 0; i < G_N_ELEMENTS (sink->cb_values); i++) {
695     const guint cb_id = CB_HUE + i;
696     if (!gst_vaapi_display_has_property (display, cb_map[i].prop_name))
697       continue;
698
699     if (G_IS_VALUE (&v_value))
700       g_value_unset (&v_value);
701     if (gst_vaapi_display_get_property (display, cb_map[i].prop_name, &v_value))
702       cb_set_gvalue (sink, cb_id, &v_value);
703     else
704       failures++;
705   }
706   sink->cb_changed = 0;
707   return failures == 0;
708 }
709
710 static gboolean
711 cb_sync_values_to_display (GstVaapiSink * sink, GstVaapiDisplay * display)
712 {
713   guint i, failures = 0;
714
715   for (i = 0; i < G_N_ELEMENTS (sink->cb_values); i++) {
716     const guint cb_id = CB_HUE + i;
717     if (!(sink->cb_changed & (1U << cb_id)))
718       continue;
719
720     if (!gst_vaapi_display_set_property (display, cb_map[i].prop_name,
721             cb_get_gvalue (sink, cb_id)))
722       failures++;
723   }
724   sink->cb_changed = 0;
725   return failures == 0;
726 }
727
728 #define CB_CHANNEL_FACTOR (1000.0)
729
730 static void
731 cb_channels_init (GstVaapiSink * sink)
732 {
733   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
734   GstColorBalanceChannel *channel;
735   GParamSpecFloat *pspec;
736   guint i;
737
738   for (i = 0; i < G_N_ELEMENTS (sink->cb_values); i++) {
739     if (!gst_vaapi_display_has_property (display, cb_map[i].prop_name))
740       continue;
741
742     pspec = G_PARAM_SPEC_FLOAT (g_properties[PROP_HUE + i]);
743     if (!pspec)
744       continue;
745
746     channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
747     channel->label = g_strdup (cb_map[i].channel_name);
748     channel->min_value = pspec->minimum * CB_CHANNEL_FACTOR;
749     channel->max_value = pspec->maximum * CB_CHANNEL_FACTOR;
750
751     sink->cb_channels = g_list_prepend (sink->cb_channels, channel);
752   }
753
754   if (sink->cb_channels)
755     sink->cb_channels = g_list_reverse (sink->cb_channels);
756 }
757
758 static void
759 cb_channels_finalize (GstVaapiSink * sink)
760 {
761   if (sink->cb_channels) {
762     g_list_free_full (sink->cb_channels, g_object_unref);
763     sink->cb_channels = NULL;
764   }
765 }
766
767 static const GList *
768 gst_vaapisink_color_balance_list_channels (GstColorBalance * cb)
769 {
770   GstVaapiSink *const sink = GST_VAAPISINK (cb);
771
772   if (!gst_vaapisink_ensure_display (sink))
773     return NULL;
774
775   if (!sink->cb_channels)
776     cb_channels_init (sink);
777   return sink->cb_channels;
778 }
779
780 static void
781 gst_vaapisink_color_balance_set_value (GstColorBalance * cb,
782     GstColorBalanceChannel * channel, gint value)
783 {
784   GstVaapiSink *const sink = GST_VAAPISINK (cb);
785   guint cb_id;
786
787   g_return_if_fail (channel->label != NULL);
788
789   if (!gst_vaapisink_ensure_display (sink))
790     return;
791
792   cb_id = cb_get_id_from_channel_name (sink, channel->label);
793   if (!cb_id)
794     return;
795
796   cb_set_value (sink, cb_id, value / CB_CHANNEL_FACTOR);
797 }
798
799 static gint
800 gst_vaapisink_color_balance_get_value (GstColorBalance * cb,
801     GstColorBalanceChannel * channel)
802 {
803   GstVaapiSink *const sink = GST_VAAPISINK (cb);
804   guint cb_id;
805
806   g_return_val_if_fail (channel->label != NULL, 0);
807
808   if (!gst_vaapisink_ensure_display (sink))
809     return 0;
810
811   cb_id = cb_get_id_from_channel_name (sink, channel->label);
812   if (!cb_id)
813     return 0;
814
815   return cb_get_value (sink, cb_id) * CB_CHANNEL_FACTOR;
816 }
817
818 static GstColorBalanceType
819 gst_vaapisink_color_balance_get_type (GstColorBalance * cb)
820 {
821   return GST_COLOR_BALANCE_HARDWARE;
822 }
823
824 static void
825 gst_vaapisink_color_balance_iface_init (GstColorBalanceInterface * iface)
826 {
827   iface->list_channels = gst_vaapisink_color_balance_list_channels;
828   iface->set_value = gst_vaapisink_color_balance_set_value;
829   iface->get_value = gst_vaapisink_color_balance_get_value;
830 #if GST_CHECK_VERSION(1,0,0)
831   iface->get_balance_type = gst_vaapisink_color_balance_get_type;
832 #else
833   iface->balance_type = gst_vaapisink_color_balance_get_type (NULL);
834 #endif
835 }
836
837 /* ------------------------------------------------------------------------ */
838 /* --- GstNavigation interface                                          --- */
839 /* ------------------------------------------------------------------------ */
840
841 static void
842 gst_vaapisink_navigation_send_event (GstNavigation * navigation,
843     GstStructure * structure)
844 {
845   GstVaapiSink *const sink = GST_VAAPISINK (navigation);
846   GstPad *peer;
847
848   if ((peer = gst_pad_get_peer (GST_VAAPI_PLUGIN_BASE_SINK_PAD (sink)))) {
849     GstEvent *event;
850     GstVaapiRectangle *disp_rect = &sink->display_rect;
851     gdouble x, y, xscale = 1.0, yscale = 1.0;
852
853     event = gst_event_new_navigation (structure);
854
855     if (!sink->window)
856       return;
857
858     /* We calculate scaling using the original video frames geometry to include
859        pixel aspect ratio scaling. */
860     xscale = (gdouble) sink->video_width / disp_rect->width;
861     yscale = (gdouble) sink->video_height / disp_rect->height;
862
863     /* Converting pointer coordinates to the non scaled geometry */
864     if (gst_structure_get_double (structure, "pointer_x", &x)) {
865       x = MIN (x, disp_rect->x + disp_rect->width);
866       x = MAX (x - disp_rect->x, 0);
867       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
868           (gdouble) x * xscale, NULL);
869     }
870     if (gst_structure_get_double (structure, "pointer_y", &y)) {
871       y = MIN (y, disp_rect->y + disp_rect->height);
872       y = MAX (y - disp_rect->y, 0);
873       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
874           (gdouble) y * yscale, NULL);
875     }
876
877     gst_pad_send_event (peer, event);
878     gst_object_unref (peer);
879   }
880 }
881
882 static void
883 gst_vaapisink_navigation_iface_init (GstNavigationInterface * iface)
884 {
885   iface->send_event = gst_vaapisink_navigation_send_event;
886 }
887
888 /* ------------------------------------------------------------------------ */
889 /* --- Common implementation                                            --- */
890 /* ------------------------------------------------------------------------ */
891
892 static gboolean
893 gst_vaapisink_reconfigure_window (GstVaapiSink * sink)
894 {
895   guint win_width, win_height;
896
897   gst_vaapi_window_reconfigure (sink->window);
898   gst_vaapi_window_get_size (sink->window, &win_width, &win_height);
899   if (win_width != sink->window_width || win_height != sink->window_height) {
900     if (!gst_vaapisink_ensure_render_rect (sink, win_width, win_height))
901       return FALSE;
902     GST_INFO ("window was resized from %ux%u to %ux%u",
903         sink->window_width, sink->window_height, win_width, win_height);
904     sink->window_width = win_width;
905     sink->window_height = win_height;
906     return TRUE;
907   }
908   return FALSE;
909 }
910
911 static gpointer
912 gst_vaapisink_event_thread (GstVaapiSink * sink)
913 {
914   GST_OBJECT_LOCK (sink);
915   while (!sink->event_thread_cancel) {
916     GST_OBJECT_UNLOCK (sink);
917     sink->backend->handle_events (sink);
918     g_usleep (G_USEC_PER_SEC / 20);
919     GST_OBJECT_LOCK (sink);
920   }
921   GST_OBJECT_UNLOCK (sink);
922   return NULL;
923 }
924
925 static void
926 gst_vaapisink_set_event_handling (GstVaapiSink * sink, gboolean handle_events)
927 {
928   GThread *thread = NULL;
929
930   if (!sink->backend || !sink->backend->event_thread_needed)
931     return;
932
933   GST_OBJECT_LOCK (sink);
934   sink->handle_events = handle_events;
935   if (handle_events && !sink->event_thread) {
936     /* Setup our event listening thread */
937     GST_DEBUG ("starting xevent thread");
938     if (sink->backend->pre_start_event_thread)
939       sink->backend->pre_start_event_thread (sink);
940
941     sink->event_thread_cancel = FALSE;
942     sink->event_thread = g_thread_try_new ("vaapisink-events",
943         (GThreadFunc) gst_vaapisink_event_thread, sink, NULL);
944   } else if (!handle_events && sink->event_thread) {
945     GST_DEBUG ("stopping xevent thread");
946     if (sink->backend->pre_stop_event_thread)
947       sink->backend->pre_stop_event_thread (sink);
948
949     /* Grab thread and mark it as NULL */
950     thread = sink->event_thread;
951     sink->event_thread = NULL;
952     sink->event_thread_cancel = TRUE;
953   }
954   GST_OBJECT_UNLOCK (sink);
955
956   /* Wait for our event thread to finish */
957   if (thread) {
958     g_thread_join (thread);
959     GST_DEBUG ("xevent thread stopped");
960   }
961 }
962
963 static void
964 gst_vaapisink_ensure_backend (GstVaapiSink * sink)
965 {
966   switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE (sink)) {
967 #if USE_DRM
968     case GST_VAAPI_DISPLAY_TYPE_DRM:
969       sink->backend = gst_vaapisink_backend_drm ();
970       break;
971 #endif
972 #if USE_X11
973     case GST_VAAPI_DISPLAY_TYPE_X11:
974       sink->backend = gst_vaapisink_backend_x11 ();
975       break;
976 #endif
977 #if USE_GLX
978     case GST_VAAPI_DISPLAY_TYPE_GLX:
979       sink->backend = gst_vaapisink_backend_x11 ();
980       break;
981 #endif
982 #if USE_WAYLAND
983     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
984       sink->backend = gst_vaapisink_backend_wayland ();
985       break;
986 #endif
987     default:
988       GST_ERROR ("failed to initialize GstVaapiSink backend");
989       g_assert_not_reached ();
990       break;
991   }
992 }
993
994 static gboolean
995 gst_vaapisink_ensure_uploader (GstVaapiSink * sink)
996 {
997   if (!gst_vaapisink_ensure_display (sink))
998     return FALSE;
999   if (!gst_vaapi_plugin_base_ensure_uploader (GST_VAAPI_PLUGIN_BASE (sink)))
1000     return FALSE;
1001   return TRUE;
1002 }
1003
1004 static gboolean
1005 gst_vaapisink_ensure_render_rect (GstVaapiSink * sink, guint width,
1006     guint height)
1007 {
1008   GstVaapiRectangle *const display_rect = &sink->display_rect;
1009   guint num, den, display_par_n, display_par_d;
1010   gboolean success;
1011
1012   /* Return success if caps are not set yet */
1013   if (!sink->caps)
1014     return TRUE;
1015
1016   if (!sink->keep_aspect) {
1017     display_rect->width = width;
1018     display_rect->height = height;
1019     display_rect->x = 0;
1020     display_rect->y = 0;
1021
1022     GST_DEBUG ("force-aspect-ratio is false; distorting while scaling video");
1023     GST_DEBUG ("render rect (%d,%d):%ux%u",
1024         display_rect->x, display_rect->y,
1025         display_rect->width, display_rect->height);
1026     return TRUE;
1027   }
1028
1029   GST_DEBUG ("ensure render rect within %ux%u bounds", width, height);
1030
1031   gst_vaapi_display_get_pixel_aspect_ratio (GST_VAAPI_PLUGIN_BASE_DISPLAY
1032       (sink), &display_par_n, &display_par_d);
1033   GST_DEBUG ("display pixel-aspect-ratio %d/%d", display_par_n, display_par_d);
1034
1035   success = gst_video_calculate_display_ratio (&num, &den,
1036       sink->video_width, sink->video_height,
1037       sink->video_par_n, sink->video_par_d, display_par_n, display_par_d);
1038   if (!success)
1039     return FALSE;
1040   GST_DEBUG ("video size %dx%d, calculated ratio %d/%d",
1041       sink->video_width, sink->video_height, num, den);
1042
1043   display_rect->width = gst_util_uint64_scale_int (height, num, den);
1044   if (display_rect->width <= width) {
1045     GST_DEBUG ("keeping window height");
1046     display_rect->height = height;
1047   } else {
1048     GST_DEBUG ("keeping window width");
1049     display_rect->width = width;
1050     display_rect->height = gst_util_uint64_scale_int (width, den, num);
1051   }
1052   GST_DEBUG ("scaling video to %ux%u", display_rect->width,
1053       display_rect->height);
1054
1055   g_assert (display_rect->width <= width);
1056   g_assert (display_rect->height <= height);
1057
1058   display_rect->x = (width - display_rect->width) / 2;
1059   display_rect->y = (height - display_rect->height) / 2;
1060
1061   GST_DEBUG ("render rect (%d,%d):%ux%u",
1062       display_rect->x, display_rect->y,
1063       display_rect->width, display_rect->height);
1064   return TRUE;
1065 }
1066
1067 static inline gboolean
1068 gst_vaapisink_ensure_window (GstVaapiSink * sink, guint width, guint height)
1069 {
1070   return sink->window || sink->backend->create_window (sink, width, height);
1071 }
1072
1073 static void
1074 gst_vaapisink_ensure_window_size (GstVaapiSink * sink, guint * width_ptr,
1075     guint * height_ptr)
1076 {
1077   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
1078   GstVideoRectangle src_rect, dst_rect, out_rect;
1079   guint num, den, display_width, display_height, display_par_n, display_par_d;
1080   gboolean success, scale;
1081
1082   if (sink->foreign_window) {
1083     *width_ptr = sink->window_width;
1084     *height_ptr = sink->window_height;
1085     return;
1086   }
1087
1088   gst_vaapi_display_get_size (display, &display_width, &display_height);
1089   if (sink->fullscreen) {
1090     *width_ptr = display_width;
1091     *height_ptr = display_height;
1092     return;
1093   }
1094
1095   gst_vaapi_display_get_pixel_aspect_ratio (display,
1096       &display_par_n, &display_par_d);
1097
1098   success = gst_video_calculate_display_ratio (&num, &den,
1099       sink->video_width, sink->video_height,
1100       sink->video_par_n, sink->video_par_d, display_par_n, display_par_d);
1101   if (!success) {
1102     num = sink->video_par_n;
1103     den = sink->video_par_d;
1104   }
1105
1106   src_rect.x = 0;
1107   src_rect.y = 0;
1108   src_rect.w = gst_util_uint64_scale_int (sink->video_height, num, den);
1109   src_rect.h = sink->video_height;
1110   dst_rect.x = 0;
1111   dst_rect.y = 0;
1112   dst_rect.w = display_width;
1113   dst_rect.h = display_height;
1114   scale = (src_rect.w > dst_rect.w || src_rect.h > dst_rect.h);
1115   gst_video_sink_center_rect (src_rect, dst_rect, &out_rect, scale);
1116   *width_ptr = out_rect.w;
1117   *height_ptr = out_rect.h;
1118 }
1119
1120 static inline gboolean
1121 gst_vaapisink_ensure_colorbalance (GstVaapiSink * sink)
1122 {
1123   return cb_sync_values_to_display (sink, GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
1124 }
1125
1126 static gboolean
1127 gst_vaapisink_ensure_rotation (GstVaapiSink * sink,
1128     gboolean recalc_display_rect)
1129 {
1130   GstVaapiDisplay *const display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
1131   gboolean success = FALSE;
1132
1133   g_return_val_if_fail (display, FALSE);
1134
1135   if (sink->rotation == sink->rotation_req)
1136     return TRUE;
1137
1138   if (!sink->use_rotation) {
1139     GST_WARNING ("VA display does not support rotation");
1140     goto end;
1141   }
1142
1143   gst_vaapi_display_lock (display);
1144   success = gst_vaapi_display_set_rotation (display, sink->rotation_req);
1145   gst_vaapi_display_unlock (display);
1146   if (!success) {
1147     GST_ERROR ("failed to change VA display rotation mode");
1148     goto end;
1149   }
1150
1151   if (((sink->rotation + sink->rotation_req) % 180) == 90) {
1152     /* Orientation changed */
1153     G_PRIMITIVE_SWAP (guint, sink->video_width, sink->video_height);
1154     G_PRIMITIVE_SWAP (gint, sink->video_par_n, sink->video_par_d);
1155   }
1156
1157   if (recalc_display_rect && !sink->foreign_window)
1158     gst_vaapisink_ensure_render_rect (sink, sink->window_width,
1159         sink->window_height);
1160   success = TRUE;
1161
1162 end:
1163   sink->rotation = sink->rotation_req;
1164   return success;
1165 }
1166
1167 static const gchar *
1168 get_display_type_name (GstVaapiDisplayType display_type)
1169 {
1170   gpointer const klass = g_type_class_peek (GST_VAAPI_TYPE_DISPLAY_TYPE);
1171   GEnumValue *const e = g_enum_get_value (klass, display_type);
1172
1173   if (e)
1174     return e->value_name;
1175   return "<unknown-type>";
1176 }
1177
1178 static void
1179 gst_vaapisink_display_changed (GstVaapiPluginBase * plugin)
1180 {
1181   GstVaapiSink *const sink = GST_VAAPISINK_CAST (plugin);
1182   GstVaapiRenderMode render_mode;
1183
1184   GST_INFO ("created %s %p", get_display_type_name (plugin->display_type),
1185       plugin->display);
1186
1187   gst_vaapisink_ensure_backend (sink);
1188
1189   sink->use_overlay =
1190       gst_vaapi_display_get_render_mode (plugin->display, &render_mode) &&
1191       render_mode == GST_VAAPI_RENDER_MODE_OVERLAY;
1192   GST_DEBUG ("use %s rendering mode",
1193       sink->use_overlay ? "overlay" : "texture");
1194
1195   /* Keep our own colorbalance values, should we have any change pending */
1196   if (!sink->cb_changed)
1197     cb_sync_values_from_display (sink, plugin->display);
1198
1199   sink->use_rotation = gst_vaapi_display_has_property (plugin->display,
1200       GST_VAAPI_DISPLAY_PROP_ROTATION);
1201 }
1202
1203 static gboolean
1204 gst_vaapisink_start (GstBaseSink * base_sink)
1205 {
1206   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1207
1208   if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (sink)))
1209     return FALSE;
1210   return gst_vaapisink_ensure_uploader (sink);
1211 }
1212
1213 static gboolean
1214 gst_vaapisink_stop (GstBaseSink * base_sink)
1215 {
1216   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1217
1218   gst_buffer_replace (&sink->video_buffer, NULL);
1219   gst_vaapi_window_replace (&sink->window, NULL);
1220
1221   gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (sink));
1222   return TRUE;
1223 }
1224
1225 static GstCaps *
1226 gst_vaapisink_get_caps_impl (GstBaseSink * base_sink)
1227 {
1228   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1229   GstCaps *out_caps, *yuv_caps;
1230
1231 #if GST_CHECK_VERSION(1,1,0)
1232   out_caps = gst_static_pad_template_get_caps (&gst_vaapisink_sink_factory);
1233 #else
1234   out_caps = gst_caps_from_string (GST_VAAPI_SURFACE_CAPS);
1235 #endif
1236   if (!out_caps)
1237     return NULL;
1238
1239   if (gst_vaapisink_ensure_uploader (sink)) {
1240     yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS (sink);
1241     if (yuv_caps) {
1242       out_caps = gst_caps_make_writable (out_caps);
1243       gst_caps_append (out_caps, gst_caps_copy (yuv_caps));
1244     }
1245   }
1246   return out_caps;
1247 }
1248
1249 #if GST_CHECK_VERSION(1,0,0)
1250 static inline GstCaps *
1251 gst_vaapisink_get_caps (GstBaseSink * base_sink, GstCaps * filter)
1252 {
1253   GstCaps *caps, *out_caps;
1254
1255   caps = gst_vaapisink_get_caps_impl (base_sink);
1256   if (caps && filter) {
1257     out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1258     gst_caps_unref (caps);
1259   } else
1260     out_caps = caps;
1261   return out_caps;
1262 }
1263 #else
1264 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
1265 #endif
1266
1267 static void
1268 update_colorimetry (GstVaapiSink * sink, GstVideoColorimetry * cinfo)
1269 {
1270 #if GST_CHECK_VERSION(1,0,0)
1271   if (gst_video_colorimetry_matches (cinfo, GST_VIDEO_COLORIMETRY_BT601))
1272     sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_601;
1273   else if (gst_video_colorimetry_matches (cinfo, GST_VIDEO_COLORIMETRY_BT709))
1274     sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_709;
1275   else if (gst_video_colorimetry_matches (cinfo,
1276           GST_VIDEO_COLORIMETRY_SMPTE240M))
1277     sink->color_standard = GST_VAAPI_COLOR_STANDARD_SMPTE_240M;
1278   else
1279     sink->color_standard = 0;
1280
1281   GST_DEBUG ("colorimetry %s", gst_video_colorimetry_to_string (cinfo));
1282 #endif
1283 }
1284
1285 static gboolean
1286 gst_vaapisink_set_caps (GstBaseSink * base_sink, GstCaps * caps)
1287 {
1288   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (base_sink);
1289   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1290   GstVideoInfo *const vip = GST_VAAPI_PLUGIN_BASE_SINK_PAD_INFO (sink);
1291   GstVaapiDisplay *display;
1292   guint win_width, win_height;
1293
1294   if (!gst_vaapisink_ensure_display (sink))
1295     return FALSE;
1296   display = GST_VAAPI_PLUGIN_BASE_DISPLAY (sink);
1297
1298   if (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE (sink) == GST_VAAPI_DISPLAY_TYPE_DRM)
1299     return TRUE;
1300
1301   if (!gst_vaapi_plugin_base_set_caps (plugin, caps, NULL))
1302     return FALSE;
1303
1304   sink->video_width = GST_VIDEO_INFO_WIDTH (vip);
1305   sink->video_height = GST_VIDEO_INFO_HEIGHT (vip);
1306   sink->video_par_n = GST_VIDEO_INFO_PAR_N (vip);
1307   sink->video_par_d = GST_VIDEO_INFO_PAR_D (vip);
1308   GST_DEBUG ("video pixel-aspect-ratio %d/%d",
1309       sink->video_par_n, sink->video_par_d);
1310
1311   update_colorimetry (sink, &vip->colorimetry);
1312   gst_caps_replace (&sink->caps, caps);
1313
1314   gst_vaapisink_ensure_colorbalance (sink);
1315   gst_vaapisink_ensure_rotation (sink, FALSE);
1316
1317   gst_vaapisink_ensure_window_size (sink, &win_width, &win_height);
1318   if (sink->window) {
1319     if (!sink->foreign_window || sink->fullscreen)
1320       gst_vaapi_window_set_size (sink->window, win_width, win_height);
1321   } else {
1322     gst_vaapi_display_lock (display);
1323     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1324     gst_vaapi_display_unlock (display);
1325     if (sink->window)
1326       return TRUE;
1327     if (!gst_vaapisink_ensure_window (sink, win_width, win_height))
1328       return FALSE;
1329     gst_vaapi_window_set_fullscreen (sink->window, sink->fullscreen);
1330     gst_vaapi_window_show (sink->window);
1331     gst_vaapi_window_get_size (sink->window, &win_width, &win_height);
1332     gst_vaapisink_set_event_handling (sink, sink->handle_events);
1333   }
1334   sink->window_width = win_width;
1335   sink->window_height = win_height;
1336   GST_DEBUG ("window size %ux%u", win_width, win_height);
1337
1338   return gst_vaapisink_ensure_render_rect (sink, win_width, win_height);
1339 }
1340
1341 static GstFlowReturn
1342 gst_vaapisink_show_frame_unlocked (GstVaapiSink * sink, GstBuffer * src_buffer)
1343 {
1344   GstVaapiVideoMeta *meta;
1345   GstVaapiSurfaceProxy *proxy;
1346   GstVaapiSurface *surface;
1347   GstBuffer *buffer;
1348   guint flags;
1349   GstVaapiRectangle *surface_rect = NULL;
1350 #if GST_CHECK_VERSION(1,0,0)
1351   GstVaapiRectangle tmp_rect;
1352 #endif
1353   GstFlowReturn ret;
1354   gint32 view_id;
1355
1356 #if GST_CHECK_VERSION(1,0,0)
1357   GstVideoCropMeta *const crop_meta =
1358       gst_buffer_get_video_crop_meta (src_buffer);
1359   if (crop_meta) {
1360     surface_rect = &tmp_rect;
1361     surface_rect->x = crop_meta->x;
1362     surface_rect->y = crop_meta->y;
1363     surface_rect->width = crop_meta->width;
1364     surface_rect->height = crop_meta->height;
1365   }
1366 #endif
1367
1368   ret = gst_vaapi_plugin_base_get_input_buffer (GST_VAAPI_PLUGIN_BASE (sink),
1369       src_buffer, &buffer);
1370   if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_SUPPORTED)
1371     return ret;
1372
1373   meta = gst_buffer_get_vaapi_video_meta (buffer);
1374   GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE (sink,
1375       gst_vaapi_video_meta_get_display (meta));
1376
1377   proxy = gst_vaapi_video_meta_get_surface_proxy (meta);
1378   if (!proxy)
1379     goto error;
1380
1381   surface = gst_vaapi_video_meta_get_surface (meta);
1382   if (!surface)
1383     goto error;
1384
1385   /* Validate view component to display */
1386   view_id = GST_VAAPI_SURFACE_PROXY_VIEW_ID (proxy);
1387   if (G_UNLIKELY (sink->view_id == -1))
1388     sink->view_id = view_id;
1389   else if (sink->view_id != view_id) {
1390     gst_buffer_unref (buffer);
1391     return GST_FLOW_OK;
1392   }
1393
1394   gst_vaapisink_ensure_colorbalance (sink);
1395   gst_vaapisink_ensure_rotation (sink, TRUE);
1396
1397   GST_DEBUG ("render surface %" GST_VAAPI_ID_FORMAT,
1398       GST_VAAPI_ID_ARGS (gst_vaapi_surface_get_id (surface)));
1399
1400   if (!surface_rect)
1401     surface_rect = (GstVaapiRectangle *)
1402         gst_vaapi_video_meta_get_render_rect (meta);
1403
1404   if (surface_rect)
1405     GST_DEBUG ("render rect (%d,%d), size %ux%u",
1406         surface_rect->x, surface_rect->y,
1407         surface_rect->width, surface_rect->height);
1408
1409   flags = gst_vaapi_video_meta_get_render_flags (meta);
1410
1411   /* Append default color standard obtained from caps if none was
1412      available on a per-buffer basis */
1413   if (!(flags & GST_VAAPI_COLOR_STANDARD_MASK))
1414     flags |= sink->color_standard;
1415
1416   if (!gst_vaapi_apply_composition (surface, src_buffer))
1417     GST_WARNING ("could not update subtitles");
1418
1419   if (!sink->backend->render_surface (sink, surface, surface_rect, flags))
1420     goto error;
1421
1422   /* Retain VA surface until the next one is displayed */
1423   gst_buffer_replace (&sink->video_buffer, buffer);
1424   gst_buffer_unref (buffer);
1425   return GST_FLOW_OK;
1426
1427 error:
1428   gst_buffer_unref (buffer);
1429   return GST_FLOW_EOS;
1430 }
1431
1432 static GstFlowReturn
1433 gst_vaapisink_show_frame (GstBaseSink * base_sink, GstBuffer * src_buffer)
1434 {
1435   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1436   GstFlowReturn ret;
1437
1438   /* We need at least to protect the gst_vaapi_aplpy_composition()
1439    * call to prevent a race during subpicture destruction.
1440    * FIXME: a less coarse grained lock could be used, though */
1441   gst_vaapi_display_lock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
1442   ret = gst_vaapisink_show_frame_unlocked (sink, src_buffer);
1443   gst_vaapi_display_unlock (GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
1444   return ret;
1445 }
1446
1447 #if GST_CHECK_VERSION(1,0,0)
1448 static gboolean
1449 gst_vaapisink_propose_allocation (GstBaseSink * base_sink, GstQuery * query)
1450 {
1451   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (base_sink);
1452
1453   if (!gst_vaapi_plugin_base_propose_allocation (plugin, query))
1454     return FALSE;
1455
1456   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1457   gst_query_add_allocation_meta (query,
1458       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1459   return TRUE;
1460 }
1461 #else
1462 static GstFlowReturn
1463 gst_vaapisink_buffer_alloc (GstBaseSink * base_sink, guint64 offset, guint size,
1464     GstCaps * caps, GstBuffer ** outbuf_ptr)
1465 {
1466   return
1467       gst_vaapi_plugin_base_allocate_input_buffer (GST_VAAPI_PLUGIN_BASE
1468       (base_sink), caps, outbuf_ptr);
1469 }
1470 #endif
1471
1472 static gboolean
1473 gst_vaapisink_query (GstBaseSink * base_sink, GstQuery * query)
1474 {
1475   GstVaapiSink *const sink = GST_VAAPISINK_CAST (base_sink);
1476
1477   GST_INFO_OBJECT (sink, "query type %s", GST_QUERY_TYPE_NAME (query));
1478
1479   if (gst_vaapi_reply_to_query (query, GST_VAAPI_PLUGIN_BASE_DISPLAY (sink))) {
1480     GST_DEBUG ("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY (sink));
1481     return TRUE;
1482   }
1483   return GST_BASE_SINK_CLASS (gst_vaapisink_parent_class)->query (base_sink,
1484       query);
1485 }
1486
1487 static void
1488 gst_vaapisink_destroy (GstVaapiSink * sink)
1489 {
1490   gst_vaapisink_set_event_handling (sink, FALSE);
1491
1492   cb_channels_finalize (sink);
1493   gst_buffer_replace (&sink->video_buffer, NULL);
1494   gst_caps_replace (&sink->caps, NULL);
1495 }
1496
1497 static void
1498 gst_vaapisink_finalize (GObject * object)
1499 {
1500   gst_vaapisink_destroy (GST_VAAPISINK_CAST (object));
1501
1502   gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object));
1503   G_OBJECT_CLASS (gst_vaapisink_parent_class)->finalize (object);
1504 }
1505
1506 static void
1507 gst_vaapisink_set_property (GObject * object,
1508     guint prop_id, const GValue * value, GParamSpec * pspec)
1509 {
1510   GstVaapiSink *const sink = GST_VAAPISINK_CAST (object);
1511
1512   switch (prop_id) {
1513     case PROP_DISPLAY_TYPE:
1514       gst_vaapi_plugin_base_set_display_type (GST_VAAPI_PLUGIN_BASE (sink),
1515           g_value_get_enum (value));
1516       break;
1517     case PROP_DISPLAY_NAME:
1518       gst_vaapi_plugin_base_set_display_name (GST_VAAPI_PLUGIN_BASE (sink),
1519           g_value_get_string (value));
1520       break;
1521     case PROP_FULLSCREEN:
1522       sink->fullscreen = g_value_get_boolean (value);
1523       break;
1524     case PROP_VIEW_ID:
1525       sink->view_id = g_value_get_int (value);
1526       break;
1527     case PROP_ROTATION:
1528       sink->rotation_req = g_value_get_enum (value);
1529       break;
1530     case PROP_FORCE_ASPECT_RATIO:
1531       sink->keep_aspect = g_value_get_boolean (value);
1532       break;
1533     case PROP_HUE:
1534     case PROP_SATURATION:
1535     case PROP_BRIGHTNESS:
1536     case PROP_CONTRAST:
1537       cb_set_gvalue (sink, (prop_id - PROP_HUE) + CB_HUE, value);
1538       break;
1539     default:
1540       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1541       break;
1542   }
1543 }
1544
1545 static void
1546 gst_vaapisink_get_property (GObject * object,
1547     guint prop_id, GValue * value, GParamSpec * pspec)
1548 {
1549   GstVaapiSink *const sink = GST_VAAPISINK_CAST (object);
1550
1551   switch (prop_id) {
1552     case PROP_DISPLAY_TYPE:
1553       g_value_set_enum (value, GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE (sink));
1554       break;
1555     case PROP_DISPLAY_NAME:
1556       g_value_set_string (value, GST_VAAPI_PLUGIN_BASE_DISPLAY_NAME (sink));
1557       break;
1558     case PROP_FULLSCREEN:
1559       g_value_set_boolean (value, sink->fullscreen);
1560       break;
1561     case PROP_VIEW_ID:
1562       g_value_set_int (value, sink->view_id);
1563       break;
1564     case PROP_ROTATION:
1565       g_value_set_enum (value, sink->rotation);
1566       break;
1567     case PROP_FORCE_ASPECT_RATIO:
1568       g_value_set_boolean (value, sink->keep_aspect);
1569       break;
1570     case PROP_HUE:
1571     case PROP_SATURATION:
1572     case PROP_BRIGHTNESS:
1573     case PROP_CONTRAST:
1574       g_value_set_float (value, cb_get_value (sink,
1575               (prop_id - PROP_HUE) + CB_HUE));
1576       break;
1577     default:
1578       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1579       break;
1580   }
1581 }
1582
1583 static void
1584 gst_vaapisink_set_bus (GstElement * element, GstBus * bus)
1585 {
1586   /* Make sure to allocate a VA display in the sink element first,
1587      so that upstream elements could query a display that was
1588      allocated here, and that exactly matches what the user
1589      requested through the "display" property */
1590   if (!GST_ELEMENT_BUS (element) && bus)
1591     gst_vaapisink_ensure_display (GST_VAAPISINK_CAST (element));
1592
1593   GST_ELEMENT_CLASS (gst_vaapisink_parent_class)->set_bus (element, bus);
1594 }
1595
1596 static void
1597 gst_vaapisink_class_init (GstVaapiSinkClass * klass)
1598 {
1599   GObjectClass *const object_class = G_OBJECT_CLASS (klass);
1600   GstElementClass *const element_class = GST_ELEMENT_CLASS (klass);
1601   GstBaseSinkClass *const basesink_class = GST_BASE_SINK_CLASS (klass);
1602   GstVaapiPluginBaseClass *const base_plugin_class =
1603       GST_VAAPI_PLUGIN_BASE_CLASS (klass);
1604   GstPadTemplate *pad_template;
1605
1606   GST_DEBUG_CATEGORY_INIT (gst_debug_vaapisink,
1607       GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1608
1609   gst_vaapi_plugin_base_class_init (base_plugin_class);
1610   base_plugin_class->has_interface = gst_vaapisink_has_interface;
1611   base_plugin_class->display_changed = gst_vaapisink_display_changed;
1612
1613   object_class->finalize = gst_vaapisink_finalize;
1614   object_class->set_property = gst_vaapisink_set_property;
1615   object_class->get_property = gst_vaapisink_get_property;
1616
1617   basesink_class->start = gst_vaapisink_start;
1618   basesink_class->stop = gst_vaapisink_stop;
1619   basesink_class->get_caps = gst_vaapisink_get_caps;
1620   basesink_class->set_caps = gst_vaapisink_set_caps;
1621   basesink_class->preroll = gst_vaapisink_show_frame;
1622   basesink_class->render = gst_vaapisink_show_frame;
1623   basesink_class->query = gst_vaapisink_query;
1624 #if GST_CHECK_VERSION(1,0,0)
1625   basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1626 #else
1627   basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1628 #endif
1629
1630   element_class->set_bus = gst_vaapisink_set_bus;
1631   gst_element_class_set_static_metadata (element_class,
1632       "VA-API sink", "Sink/Video", GST_PLUGIN_DESC,
1633       "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1634
1635   pad_template = gst_static_pad_template_get (&gst_vaapisink_sink_factory);
1636   gst_element_class_add_pad_template (element_class, pad_template);
1637
1638   /**
1639    * GstVaapiSink:display:
1640    *
1641    * The type of display to use.
1642    */
1643   g_properties[PROP_DISPLAY_TYPE] =
1644       g_param_spec_enum ("display",
1645       "display type",
1646       "display type to use",
1647       GST_VAAPI_TYPE_DISPLAY_TYPE,
1648       GST_VAAPI_DISPLAY_TYPE_ANY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1649
1650   /**
1651    * GstVaapiSink:display-name:
1652    *
1653    * The native display name.
1654    */
1655   g_properties[PROP_DISPLAY_NAME] =
1656       g_param_spec_string ("display-name",
1657       "display name",
1658       "display name to use", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1659
1660   /**
1661    * GstVaapiSink:fullscreen:
1662    *
1663    * Selects whether fullscreen mode is enabled or not.
1664    */
1665   g_properties[PROP_FULLSCREEN] =
1666       g_param_spec_boolean ("fullscreen",
1667       "Fullscreen",
1668       "Requests window in fullscreen state",
1669       FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1670
1671   /**
1672    * GstVaapiSink:rotation:
1673    *
1674    * The VA display rotation mode, expressed as a #GstVaapiRotation.
1675    */
1676   g_properties[PROP_ROTATION] =
1677       g_param_spec_enum (GST_VAAPI_DISPLAY_PROP_ROTATION,
1678       "rotation",
1679       "The display rotation mode",
1680       GST_VAAPI_TYPE_ROTATION,
1681       DEFAULT_ROTATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1682
1683   /**
1684    * GstVaapiSink:force-aspect-ratio:
1685    *
1686    * When enabled, scaling respects video aspect ratio; when disabled,
1687    * the video is distorted to fit the window.
1688    */
1689   g_properties[PROP_FORCE_ASPECT_RATIO] =
1690       g_param_spec_boolean ("force-aspect-ratio",
1691       "Force aspect ratio",
1692       "When enabled, scaling will respect original aspect ratio",
1693       TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1694
1695   /**
1696    * GstVaapiSink:view-id:
1697    *
1698    * When not set to -1, the displayed frame will always be the one
1699    * that matches the view-id of the very first displayed frame. Any
1700    * other number will indicate the desire to display the supplied
1701    * view-id only.
1702    */
1703   g_properties[PROP_VIEW_ID] =
1704       g_param_spec_int ("view-id",
1705       "View ID",
1706       "ID of the view component of interest to display",
1707       -1, G_MAXINT32, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1708
1709   /**
1710    * GstVaapiSink:hue:
1711    *
1712    * The VA display hue, expressed as a float value. Range is -180.0
1713    * to 180.0. Default value is 0.0 and represents no modification.
1714    */
1715   g_properties[PROP_HUE] =
1716       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_HUE,
1717       "hue", "The display hue value", -180.0, 180.0, 0.0,
1718       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
1719
1720   /**
1721    * GstVaapiSink:saturation:
1722    *
1723    * The VA display saturation, expressed as a float value. Range is
1724    * 0.0 to 2.0. Default value is 1.0 and represents no modification.
1725    */
1726   g_properties[PROP_SATURATION] =
1727       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_SATURATION,
1728       "saturation",
1729       "The display saturation value", 0.0, 2.0, 1.0,
1730       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
1731
1732   /**
1733    * GstVaapiSink:brightness:
1734    *
1735    * The VA display brightness, expressed as a float value. Range is
1736    * -1.0 to 1.0. Default value is 0.0 and represents no modification.
1737    */
1738   g_properties[PROP_BRIGHTNESS] =
1739       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_BRIGHTNESS,
1740       "brightness",
1741       "The display brightness value", -1.0, 1.0, 0.0,
1742       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
1743
1744   /**
1745    * GstVaapiSink:contrast:
1746    *
1747    * The VA display contrast, expressed as a float value. Range is 0.0
1748    * to 2.0. Default value is 1.0 and represents no modification.
1749    */
1750   g_properties[PROP_CONTRAST] =
1751       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_CONTRAST,
1752       "contrast",
1753       "The display contrast value", 0.0, 2.0, 1.0,
1754       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
1755
1756   g_object_class_install_properties (object_class, N_PROPERTIES, g_properties);
1757 }
1758
1759 static void
1760 gst_vaapisink_init (GstVaapiSink * sink)
1761 {
1762   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (sink);
1763   guint i;
1764
1765   gst_vaapi_plugin_base_init (plugin, GST_CAT_DEFAULT);
1766   gst_vaapi_plugin_base_set_display_type (plugin, DEFAULT_DISPLAY_TYPE);
1767
1768   sink->video_par_n = 1;
1769   sink->video_par_d = 1;
1770   sink->view_id = -1;
1771   sink->handle_events = TRUE;
1772   sink->rotation = DEFAULT_ROTATION;
1773   sink->rotation_req = DEFAULT_ROTATION;
1774   sink->keep_aspect = TRUE;
1775   gst_video_info_init (&sink->video_info);
1776
1777   for (i = 0; i < G_N_ELEMENTS (sink->cb_values); i++)
1778     g_value_init (&sink->cb_values[i], G_TYPE_FLOAT);
1779 }