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