d9fd70371ed0fbc9feba2b63ae2a3103e6a53672
[platform/upstream/gstreamer-vaapi.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 #if USE_DRM
40 # include <gst/vaapi/gstvaapidisplay_drm.h>
41 #endif
42 #if USE_X11
43 # include <gst/vaapi/gstvaapidisplay_x11.h>
44 # include <gst/vaapi/gstvaapiwindow_x11.h>
45 #endif
46 #if USE_GLX
47 # include <gst/vaapi/gstvaapidisplay_glx.h>
48 # include <gst/vaapi/gstvaapiwindow_glx.h>
49 #endif
50 #if USE_WAYLAND
51 # include <gst/vaapi/gstvaapidisplay_wayland.h>
52 # include <gst/vaapi/gstvaapiwindow_wayland.h>
53 #endif
54
55 /* Supported interfaces */
56 #if GST_CHECK_VERSION(1,0,0)
57 # include <gst/video/videooverlay.h>
58 #else
59 # include <gst/interfaces/xoverlay.h>
60
61 # define GST_TYPE_VIDEO_OVERLAY         GST_TYPE_X_OVERLAY
62 # define GST_VIDEO_OVERLAY              GST_X_OVERLAY
63 # define GstVideoOverlay                GstXOverlay
64 # define GstVideoOverlayInterface       GstXOverlayClass
65
66 # define gst_video_overlay_prepare_window_handle(sink) \
67     gst_x_overlay_prepare_xwindow_id(sink)
68 # define gst_video_overlay_got_window_handle(sink, window_handle) \
69     gst_x_overlay_got_window_handle(sink, window_handle)
70 #endif
71
72 #include "gstvaapisink.h"
73 #include "gstvaapipluginutil.h"
74 #include "gstvaapivideometa.h"
75 #if GST_CHECK_VERSION(1,0,0)
76 #include "gstvaapivideobufferpool.h"
77 #include "gstvaapivideomemory.h"
78 #endif
79
80 #define GST_PLUGIN_NAME "vaapisink"
81 #define GST_PLUGIN_DESC "A VA-API based videosink"
82
83 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
84 #define GST_CAT_DEFAULT gst_debug_vaapisink
85
86 /* Default template */
87 static const char gst_vaapisink_sink_caps_str[] =
88 #if GST_CHECK_VERSION(1,1,0)
89     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
90         GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ";"
91     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL);
92 #else
93 #if GST_CHECK_VERSION(1,0,0)
94     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; "
95 #else
96     "video/x-raw-yuv, "
97     "width  = (int) [ 1, MAX ], "
98     "height = (int) [ 1, MAX ]; "
99 #endif
100     GST_VAAPI_SURFACE_CAPS;
101 #endif
102
103 static GstStaticPadTemplate gst_vaapisink_sink_factory =
104     GST_STATIC_PAD_TEMPLATE(
105         "sink",
106         GST_PAD_SINK,
107         GST_PAD_ALWAYS,
108         GST_STATIC_CAPS(gst_vaapisink_sink_caps_str));
109
110 static gboolean
111 gst_vaapisink_has_interface(GstVaapiPluginBase *plugin, GType type)
112 {
113     return type == GST_TYPE_VIDEO_OVERLAY;
114 }
115
116 static void
117 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface);
118
119 G_DEFINE_TYPE_WITH_CODE(
120     GstVaapiSink,
121     gst_vaapisink,
122     GST_TYPE_VIDEO_SINK,
123     GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
124     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_OVERLAY,
125                           gst_vaapisink_video_overlay_iface_init))
126
127 enum {
128     PROP_0,
129
130     PROP_DISPLAY_TYPE,
131     PROP_FULLSCREEN,
132     PROP_SYNCHRONOUS,
133     PROP_USE_GLX,
134     PROP_USE_REFLECTION,
135     PROP_ROTATION,
136     PROP_FORCE_ASPECT_RATIO,
137 };
138
139 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
140 #define DEFAULT_ROTATION                GST_VAAPI_ROTATION_0
141
142 static inline gboolean
143 gst_vaapisink_ensure_display(GstVaapiSink *sink);
144
145 /* GstVideoOverlay interface */
146
147 #if USE_X11
148 static gboolean
149 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
150 #endif
151
152 static GstFlowReturn
153 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
154
155 static void
156 gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
157     guintptr window)
158 {
159     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
160     GstVaapiDisplayType display_type;
161
162     if (!gst_vaapisink_ensure_display(sink))
163         return;
164     display_type = GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
165
166     /* Disable GLX rendering when vaapisink is using a foreign X
167        window. It's pretty much useless */
168     if (display_type == GST_VAAPI_DISPLAY_TYPE_GLX) {
169         display_type = GST_VAAPI_DISPLAY_TYPE_X11;
170         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
171             display_type);
172     }
173
174     sink->foreign_window = TRUE;
175
176     switch (display_type) {
177 #if USE_X11
178     case GST_VAAPI_DISPLAY_TYPE_X11:
179         gst_vaapisink_ensure_window_xid(sink, window);
180         break;
181 #endif
182     default:
183         break;
184     }
185 }
186
187 static void
188 gst_vaapisink_video_overlay_set_render_rectangle(
189     GstVideoOverlay *overlay,
190     gint         x,
191     gint         y,
192     gint         width,
193     gint         height
194 )
195 {
196     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
197     GstVaapiRectangle * const display_rect = &sink->display_rect;
198
199     display_rect->x      = x;
200     display_rect->y      = y;
201     display_rect->width  = width;
202     display_rect->height = height;
203     
204     GST_DEBUG("render rect (%d,%d):%ux%u",
205               display_rect->x, display_rect->y,
206               display_rect->width, display_rect->height);
207 }
208
209 static void
210 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
211 {
212     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
213     GstBaseSink * const base_sink = GST_BASE_SINK(overlay);
214     GstBuffer *buffer;
215
216     if (sink->use_overlay)
217         buffer = sink->video_buffer ? gst_buffer_ref(sink->video_buffer) : NULL;
218     else {
219 #if GST_CHECK_VERSION(1,0,0)
220         GstSample * const sample = gst_base_sink_get_last_sample(base_sink);
221         if (!sample)
222             return;
223         buffer = gst_buffer_ref(gst_sample_get_buffer(sample));
224         gst_sample_unref(sample);
225 #else
226         buffer = gst_base_sink_get_last_buffer(base_sink);
227 #endif
228     }
229     if (buffer) {
230         gst_vaapisink_show_frame(base_sink, buffer);
231         gst_buffer_unref(buffer);
232     }
233 }
234
235 static void
236 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface)
237 {
238     iface->set_window_handle    = gst_vaapisink_video_overlay_set_window_handle;
239     iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle;
240     iface->expose               = gst_vaapisink_video_overlay_expose;
241 }
242
243 static void
244 gst_vaapisink_destroy(GstVaapiSink *sink)
245 {
246     gst_buffer_replace(&sink->video_buffer, NULL);
247 #if USE_GLX
248     gst_vaapi_texture_replace(&sink->texture, NULL);
249 #endif
250     gst_caps_replace(&sink->caps, NULL);
251 }
252
253 #if USE_X11
254 /* Checks whether a ConfigureNotify event is in the queue */
255 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
256 struct _ConfigureNotifyEventPendingArgs {
257     Window      window;
258     guint       width;
259     guint       height;
260     gboolean    match;
261 };
262
263 static Bool
264 configure_notify_event_pending_cb(Display *dpy, XEvent *xev, XPointer arg)
265 {
266     ConfigureNotifyEventPendingArgs * const args =
267         (ConfigureNotifyEventPendingArgs *)arg;
268
269     if (xev->type == ConfigureNotify &&
270         xev->xconfigure.window == args->window &&
271         xev->xconfigure.width  == args->width  &&
272         xev->xconfigure.height == args->height)
273         args->match = TRUE;
274
275     /* XXX: this is a hack to traverse the whole queue because we
276        can't use XPeekIfEvent() since it could block */
277     return False;
278 }
279
280 static gboolean
281 configure_notify_event_pending(
282     GstVaapiSink *sink,
283     Window        window,
284     guint         width,
285     guint         height
286 )
287 {
288     GstVaapiDisplayX11 * const display =
289         GST_VAAPI_DISPLAY_X11(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
290     ConfigureNotifyEventPendingArgs args;
291     XEvent xev;
292
293     args.window = window;
294     args.width  = width;
295     args.height = height;
296     args.match  = FALSE;
297
298     /* XXX: don't use XPeekIfEvent() because it might block */
299     XCheckIfEvent(
300         gst_vaapi_display_x11_get_display(display),
301         &xev,
302         configure_notify_event_pending_cb, (XPointer)&args
303     );
304     return args.match;
305 }
306 #endif
307
308 static const gchar *
309 get_display_type_name(GstVaapiDisplayType display_type)
310 {
311     gpointer const klass = g_type_class_peek(GST_VAAPI_TYPE_DISPLAY_TYPE);
312     GEnumValue * const e = g_enum_get_value(klass, display_type);
313
314     if (e)
315         return e->value_name;
316     return "<unknown-type>";
317 }
318
319 static inline gboolean
320 gst_vaapisink_ensure_display(GstVaapiSink *sink)
321 {
322     return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(sink));
323 }
324
325 static void
326 gst_vaapisink_display_changed(GstVaapiPluginBase *plugin)
327 {
328     GstVaapiSink * const sink = GST_VAAPISINK(plugin);
329     GstVaapiRenderMode render_mode;
330
331     GST_INFO("created %s %p", get_display_type_name(plugin->display_type),
332              plugin->display);
333
334     sink->use_overlay =
335         gst_vaapi_display_get_render_mode(plugin->display, &render_mode) &&
336         render_mode == GST_VAAPI_RENDER_MODE_OVERLAY;
337     GST_DEBUG("use %s rendering mode",
338               sink->use_overlay ? "overlay" : "texture");
339
340     sink->use_rotation = gst_vaapi_display_has_property(plugin->display,
341         GST_VAAPI_DISPLAY_PROP_ROTATION);
342 }
343
344 static gboolean
345 gst_vaapisink_ensure_uploader(GstVaapiSink *sink)
346 {
347     if (!gst_vaapisink_ensure_display(sink))
348         return FALSE;
349     if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(sink)))
350         return FALSE;
351     return TRUE;
352 }
353
354 static gboolean
355 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
356 {
357     GstVaapiRectangle * const display_rect = &sink->display_rect;
358     guint num, den, display_par_n, display_par_d;
359     gboolean success;
360
361     /* Return success if caps are not set yet */
362     if (!sink->caps)
363         return TRUE;
364
365     if (!sink->keep_aspect) {
366         display_rect->width = width;
367         display_rect->height = height;
368         display_rect->x = 0;
369         display_rect->y = 0;
370
371         GST_DEBUG("force-aspect-ratio is false; distorting while scaling video");
372         GST_DEBUG("render rect (%d,%d):%ux%u",
373                   display_rect->x, display_rect->y,
374                   display_rect->width, display_rect->height);
375         return TRUE;
376     }
377
378     GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
379
380     gst_vaapi_display_get_pixel_aspect_ratio(
381         GST_VAAPI_PLUGIN_BASE_DISPLAY(sink),
382         &display_par_n, &display_par_d
383     );
384     GST_DEBUG("display pixel-aspect-ratio %d/%d",
385               display_par_n, display_par_d);
386
387     success = gst_video_calculate_display_ratio(
388         &num, &den,
389         sink->video_width, sink->video_height,
390         sink->video_par_n, sink->video_par_d,
391         display_par_n, display_par_d
392     );
393     if (!success)
394         return FALSE;
395     GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
396               sink->video_width, sink->video_height, num, den);
397
398     display_rect->width = gst_util_uint64_scale_int(height, num, den);
399     if (display_rect->width <= width) {
400         GST_DEBUG("keeping window height");
401         display_rect->height = height;
402     }
403     else {
404         GST_DEBUG("keeping window width");
405         display_rect->width  = width;
406         display_rect->height =
407             gst_util_uint64_scale_int(width, den, num);
408     }
409     GST_DEBUG("scaling video to %ux%u", display_rect->width, display_rect->height);
410
411     g_assert(display_rect->width  <= width);
412     g_assert(display_rect->height <= height);
413
414     display_rect->x = (width  - display_rect->width)  / 2;
415     display_rect->y = (height - display_rect->height) / 2;
416
417     GST_DEBUG("render rect (%d,%d):%ux%u",
418               display_rect->x, display_rect->y,
419               display_rect->width, display_rect->height);
420     return TRUE;
421 }
422
423 static void
424 gst_vaapisink_ensure_window_size(GstVaapiSink *sink, guint *pwidth, guint *pheight)
425 {
426     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
427     GstVideoRectangle src_rect, dst_rect, out_rect;
428     guint num, den, display_width, display_height, display_par_n, display_par_d;
429     gboolean success, scale;
430
431     if (sink->foreign_window) {
432         *pwidth  = sink->window_width;
433         *pheight = sink->window_height;
434         return;
435     }
436
437     gst_vaapi_display_get_size(display, &display_width, &display_height);
438     if (sink->fullscreen) {
439         *pwidth  = display_width;
440         *pheight = display_height;
441         return;
442     }
443
444     gst_vaapi_display_get_pixel_aspect_ratio(
445         display,
446         &display_par_n, &display_par_d
447     );
448
449     success = gst_video_calculate_display_ratio(
450         &num, &den,
451         sink->video_width, sink->video_height,
452         sink->video_par_n, sink->video_par_d,
453         display_par_n, display_par_d
454     );
455     if (!success) {
456         num = sink->video_par_n;
457         den = sink->video_par_d;
458     }
459
460     src_rect.x = 0;
461     src_rect.y = 0;
462     src_rect.w = gst_util_uint64_scale_int(sink->video_height, num, den);
463     src_rect.h = sink->video_height;
464     dst_rect.x = 0;
465     dst_rect.y = 0;
466     dst_rect.w = display_width;
467     dst_rect.h = display_height;
468     scale      = (src_rect.w > dst_rect.w || src_rect.h > dst_rect.h);
469     gst_video_sink_center_rect(src_rect, dst_rect, &out_rect, scale);
470     *pwidth    = out_rect.w;
471     *pheight   = out_rect.h;
472 }
473
474 static inline gboolean
475 gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
476 {
477     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
478
479     if (!sink->window) {
480         const GstVaapiDisplayType display_type =
481             GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
482         switch (display_type) {
483 #if USE_GLX
484         case GST_VAAPI_DISPLAY_TYPE_GLX:
485             sink->window = gst_vaapi_window_glx_new(display, width, height);
486             goto notify_video_overlay_interface;
487 #endif
488 #if USE_X11
489         case GST_VAAPI_DISPLAY_TYPE_X11:
490             sink->window = gst_vaapi_window_x11_new(display, width, height);
491         notify_video_overlay_interface:
492             if (!sink->window)
493                 break;
494             gst_video_overlay_got_window_handle(
495                 GST_VIDEO_OVERLAY(sink),
496                 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window))
497             );
498             break;
499 #endif
500 #if USE_WAYLAND
501         case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
502             sink->window = gst_vaapi_window_wayland_new(display, width, height);
503             break;
504 #endif
505         default:
506             GST_ERROR("unsupported display type %d", display_type);
507             return FALSE;
508         }
509     }
510     return sink->window != NULL;
511 }
512
513 #if USE_X11
514 static gboolean
515 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
516 {
517     GstVaapiDisplay *display;
518     Window rootwin;
519     unsigned int width, height, border_width, depth;
520     int x, y;
521     XID xid = window_id;
522
523     if (!gst_vaapisink_ensure_display(sink))
524         return FALSE;
525     display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
526
527     gst_vaapi_display_lock(display);
528     XGetGeometry(
529         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(display)),
530         xid,
531         &rootwin,
532         &x, &y, &width, &height, &border_width, &depth
533     );
534     gst_vaapi_display_unlock(display);
535
536     if ((width != sink->window_width || height != sink->window_height) &&
537         !configure_notify_event_pending(sink, xid, width, height)) {
538         if (!gst_vaapisink_ensure_render_rect(sink, width, height))
539             return FALSE;
540         sink->window_width  = width;
541         sink->window_height = height;
542     }
543
544     if (sink->window &&
545         gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
546         return TRUE;
547
548     gst_vaapi_window_replace(&sink->window, NULL);
549
550     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
551 #if USE_GLX
552     case GST_VAAPI_DISPLAY_TYPE_GLX:
553         sink->window = gst_vaapi_window_glx_new_with_xid(display, xid);
554         break;
555 #endif
556     case GST_VAAPI_DISPLAY_TYPE_X11:
557         sink->window = gst_vaapi_window_x11_new_with_xid(display, xid);
558         break;
559     default:
560         GST_ERROR("unsupported display type %d",
561                   GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
562         return FALSE;
563     }
564     return sink->window != NULL;
565 }
566 #endif
567
568 static gboolean
569 gst_vaapisink_ensure_rotation(GstVaapiSink *sink, gboolean recalc_display_rect)
570 {
571     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
572     gboolean success = FALSE;
573
574     g_return_val_if_fail(display, FALSE);
575
576     if (sink->rotation == sink->rotation_req)
577         return TRUE;
578
579     if (!sink->use_rotation) {
580         GST_WARNING("VA display does not support rotation");
581         goto end;
582     }
583
584     gst_vaapi_display_lock(display);
585     success = gst_vaapi_display_set_rotation(display, sink->rotation_req);
586     gst_vaapi_display_unlock(display);
587     if (!success) {
588         GST_ERROR("failed to change VA display rotation mode");
589         goto end;
590     }
591
592     if (((sink->rotation + sink->rotation_req) % 180) == 90) {
593         /* Orientation changed */
594         G_PRIMITIVE_SWAP(guint, sink->video_width, sink->video_height);
595         G_PRIMITIVE_SWAP(gint, sink->video_par_n, sink->video_par_d);
596     }
597
598     if (recalc_display_rect && !sink->foreign_window)
599         gst_vaapisink_ensure_render_rect(sink, sink->window_width,
600             sink->window_height);
601     success = TRUE;
602
603 end:
604     sink->rotation = sink->rotation_req;
605     return success;
606 }
607
608 static gboolean
609 gst_vaapisink_start(GstBaseSink *base_sink)
610 {
611     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
612
613     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(sink)))
614         return FALSE;
615     return gst_vaapisink_ensure_uploader(sink);
616 }
617
618 static gboolean
619 gst_vaapisink_stop(GstBaseSink *base_sink)
620 {
621     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
622
623     gst_buffer_replace(&sink->video_buffer, NULL);
624 #if GST_CHECK_VERSION(1,0,0)
625     g_clear_object(&sink->video_buffer_pool);
626 #endif
627     gst_vaapi_window_replace(&sink->window, NULL);
628
629     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(sink));
630     return TRUE;
631 }
632
633 static GstCaps *
634 gst_vaapisink_get_caps_impl(GstBaseSink *base_sink)
635 {
636     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
637     GstCaps *out_caps, *yuv_caps;
638
639 #if GST_CHECK_VERSION(1,1,0)
640     out_caps = gst_static_pad_template_get_caps(&gst_vaapisink_sink_factory);
641 #else
642     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS);
643 #endif
644     if (!out_caps)
645         return NULL;
646
647     if (gst_vaapisink_ensure_uploader(sink)) {
648         yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(sink);
649         if (yuv_caps) {
650             out_caps = gst_caps_make_writable(out_caps);
651             gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
652         }
653     }
654     return out_caps;
655 }
656
657 #if GST_CHECK_VERSION(1,0,0)
658 static inline GstCaps *
659 gst_vaapisink_get_caps(GstBaseSink *base_sink, GstCaps *filter)
660 {
661     GstCaps *caps, *out_caps;
662
663     caps = gst_vaapisink_get_caps_impl(base_sink);
664     if (caps && filter) {
665         out_caps = gst_caps_intersect_full(caps, filter,
666             GST_CAPS_INTERSECT_FIRST);
667         gst_caps_unref(caps);
668     }
669     else
670         out_caps = caps;
671     return out_caps;
672 }
673 #else
674 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
675 #endif
676
677 static void
678 update_colorimetry(GstVaapiSink *sink, GstVideoColorimetry *cinfo)
679 {
680 #if GST_CHECK_VERSION(1,0,0)
681     if (gst_video_colorimetry_matches(cinfo,
682             GST_VIDEO_COLORIMETRY_BT601))
683         sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_601;
684     else if (gst_video_colorimetry_matches(cinfo,
685             GST_VIDEO_COLORIMETRY_BT709))
686         sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_709;
687     else if (gst_video_colorimetry_matches(cinfo,
688             GST_VIDEO_COLORIMETRY_SMPTE240M))
689         sink->color_standard = GST_VAAPI_COLOR_STANDARD_SMPTE_240M;
690     else
691         sink->color_standard = 0;
692
693     GST_DEBUG("colorimetry %s", gst_video_colorimetry_to_string(cinfo));
694 #endif
695 }
696
697 static gboolean
698 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
699 {
700     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
701     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
702     GstVideoInfo * const vip = GST_VAAPI_PLUGIN_BASE_SINK_PAD_INFO(sink);
703     GstVaapiDisplay *display;
704     guint win_width, win_height;
705
706     if (!gst_vaapisink_ensure_display(sink))
707         return FALSE;
708     display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
709
710 #if USE_DRM
711     if (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink) == GST_VAAPI_DISPLAY_TYPE_DRM)
712         return TRUE;
713 #endif
714
715     if (!gst_vaapi_plugin_base_set_caps(plugin, caps, NULL))
716         return FALSE;
717
718     sink->video_width   = GST_VIDEO_INFO_WIDTH(vip);
719     sink->video_height  = GST_VIDEO_INFO_HEIGHT(vip);
720     sink->video_par_n   = GST_VIDEO_INFO_PAR_N(vip);
721     sink->video_par_d   = GST_VIDEO_INFO_PAR_D(vip);
722     GST_DEBUG("video pixel-aspect-ratio %d/%d",
723               sink->video_par_n, sink->video_par_d);
724
725     update_colorimetry(sink, &vip->colorimetry);
726     gst_caps_replace(&sink->caps, caps);
727
728     gst_vaapisink_ensure_rotation(sink, FALSE);
729
730     gst_vaapisink_ensure_window_size(sink, &win_width, &win_height);
731     if (sink->window) {
732         if (!sink->foreign_window || sink->fullscreen)
733             gst_vaapi_window_set_size(sink->window, win_width, win_height);
734     }
735     else {
736         gst_vaapi_display_lock(display);
737         gst_video_overlay_prepare_window_handle(GST_VIDEO_OVERLAY(sink));
738         gst_vaapi_display_unlock(display);
739         if (sink->window)
740             return TRUE;
741         if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
742             return FALSE;
743         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
744         gst_vaapi_window_show(sink->window);
745         gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
746     }
747     sink->window_width  = win_width;
748     sink->window_height = win_height;
749     GST_DEBUG("window size %ux%u", win_width, win_height);
750
751     return gst_vaapisink_ensure_render_rect(sink, win_width, win_height);
752 }
753
754 #if USE_GLX
755 static void
756 render_background(GstVaapiSink *sink)
757 {
758     /* Original code from Mirco Muller (MacSlow):
759        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
760     GLfloat fStartX = 0.0f;
761     GLfloat fStartY = 0.0f;
762     GLfloat fWidth  = (GLfloat)sink->window_width;
763     GLfloat fHeight = (GLfloat)sink->window_height;
764
765     glClear(GL_COLOR_BUFFER_BIT);
766     glBegin(GL_QUADS);
767     {
768         /* top third, darker grey to white */
769         glColor3f(0.85f, 0.85f, 0.85f);
770         glVertex3f(fStartX, fStartY, 0.0f);
771         glColor3f(0.85f, 0.85f, 0.85f);
772         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
773         glColor3f(1.0f, 1.0f, 1.0f);
774         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
775         glColor3f(1.0f, 1.0f, 1.0f);
776         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
777
778         /* middle third, just plain white */
779         glColor3f(1.0f, 1.0f, 1.0f);
780         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
781         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
782         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
783         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
784
785         /* bottom third, white to lighter grey */
786         glColor3f(1.0f, 1.0f, 1.0f);
787         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
788         glColor3f(1.0f, 1.0f, 1.0f);
789         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
790         glColor3f(0.62f, 0.66f, 0.69f);
791         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
792         glColor3f(0.62f, 0.66f, 0.69f);
793         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
794     }
795     glEnd();
796 }
797
798 static void
799 render_frame(GstVaapiSink *sink, GstVaapiSurface *surface,
800     const GstVaapiRectangle *surface_rect)
801 {
802     const guint x1 = sink->display_rect.x;
803     const guint x2 = sink->display_rect.x + sink->display_rect.width;
804     const guint y1 = sink->display_rect.y;
805     const guint y2 = sink->display_rect.y + sink->display_rect.height;
806     gfloat tx1, tx2, ty1, ty2;
807     guint width, height;
808
809     if (surface_rect) {
810         gst_vaapi_surface_get_size(surface, &width, &height);
811         tx1 = (gfloat)surface_rect->x / width;
812         ty1 = (gfloat)surface_rect->y / height;
813         tx2 = (gfloat)(surface_rect->x + surface_rect->width) / width;
814         ty2 = (gfloat)(surface_rect->y + surface_rect->height) / height;
815     }
816     else {
817         tx1 = 0.0f;
818         ty1 = 0.0f;
819         tx2 = 1.0f;
820         ty2 = 1.0f;
821     }
822
823     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
824     glBegin(GL_QUADS);
825     {
826         glTexCoord2f(tx1, ty1); glVertex2i(x1, y1);
827         glTexCoord2f(tx1, ty2); glVertex2i(x1, y2);
828         glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
829         glTexCoord2f(tx2, ty1); glVertex2i(x2, y1);
830     }
831     glEnd();
832 }
833
834 static void
835 render_reflection(GstVaapiSink *sink)
836 {
837     const guint x1 = sink->display_rect.x;
838     const guint x2 = sink->display_rect.x + sink->display_rect.width;
839     const guint y1 = sink->display_rect.y;
840     const guint rh = sink->display_rect.height / 5;
841     GLfloat     ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
842
843     glBegin(GL_QUADS);
844     {
845         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
846         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
847         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
848
849         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
850         glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
851         glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
852     }
853     glEnd();
854 }
855
856 static gboolean
857 gst_vaapisink_ensure_texture(GstVaapiSink *sink, GstVaapiSurface *surface)
858 {
859     GstVaapiDisplay * display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
860     GstVideoRectangle tex_rect, dis_rect, out_rect;
861     guint width, height;
862
863     if (sink->texture)
864         return TRUE;
865
866     gst_vaapi_surface_get_size(surface, &width, &height);
867     tex_rect.x = 0;
868     tex_rect.y = 0;
869     tex_rect.w = width;
870     tex_rect.h = height;
871
872     gst_vaapi_display_get_size(display, &width, &height);
873     dis_rect.x = 0;
874     dis_rect.y = 0;
875     dis_rect.w = width;
876     dis_rect.h = height;
877
878     gst_video_sink_center_rect(tex_rect, dis_rect, &out_rect, TRUE);
879
880     /* XXX: use surface size for now since some VA drivers have issues
881        with downscaling to the provided texture size. i.e. we should be
882        using the resulting out_rect size, which preserves the aspect
883        ratio of the surface */
884     width = tex_rect.w;
885     height = tex_rect.h;
886     GST_INFO("texture size %ux%u", width, height);
887
888     sink->texture = gst_vaapi_texture_new(display,
889         GL_TEXTURE_2D, GL_BGRA, width, height);
890     return sink->texture != NULL;
891 }
892
893 static gboolean
894 gst_vaapisink_show_frame_glx(
895     GstVaapiSink               *sink,
896     GstVaapiSurface            *surface,
897     const GstVaapiRectangle    *surface_rect,
898     guint                       flags
899 )
900 {
901     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(sink->window);
902     GLenum target;
903     GLuint texture;
904
905     gst_vaapi_window_glx_make_current(window);
906     if (!gst_vaapisink_ensure_texture(sink, surface))
907         goto error_create_texture;
908     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
909         goto error_transfer_surface;
910
911     target  = gst_vaapi_texture_get_target(sink->texture);
912     texture = gst_vaapi_texture_get_id(sink->texture);
913     if (target != GL_TEXTURE_2D || !texture)
914         return FALSE;
915
916     if (sink->use_reflection)
917         render_background(sink);
918
919     glEnable(target);
920     glBindTexture(target, texture);
921     {
922         if (sink->use_reflection) {
923             glPushMatrix();
924             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
925             glTranslatef(50.0f, 0.0f, 0.0f);
926         }
927         render_frame(sink, surface, surface_rect);
928         if (sink->use_reflection) {
929             glPushMatrix();
930             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
931             render_reflection(sink);
932             glPopMatrix();
933             glPopMatrix();
934         }
935     }
936     glBindTexture(target, 0);
937     glDisable(target);
938     gst_vaapi_window_glx_swap_buffers(window);
939     return TRUE;
940
941     /* ERRORS */
942 error_create_texture:
943     {
944         GST_DEBUG("could not create VA/GLX texture");
945         return FALSE;
946     }
947 error_transfer_surface:
948     {
949         GST_DEBUG("could not transfer VA surface to texture");
950         return FALSE;
951     }
952 }
953 #endif
954
955 static inline gboolean
956 gst_vaapisink_put_surface(
957     GstVaapiSink               *sink,
958     GstVaapiSurface            *surface,
959     const GstVaapiRectangle    *surface_rect,
960     guint                       flags
961 )
962 {
963     if (!gst_vaapi_window_put_surface(sink->window, surface,
964                 surface_rect, &sink->display_rect, flags)) {
965         GST_DEBUG("could not render VA surface");
966         return FALSE;
967     }
968     return TRUE;
969 }
970
971 static GstFlowReturn
972 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
973 {
974     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
975     GstVaapiVideoMeta *meta;
976     GstVaapiSurface *surface;
977     GstBuffer *buffer;
978     guint flags;
979     gboolean success;
980     GstVaapiRectangle *surface_rect = NULL;
981 #if GST_CHECK_VERSION(1,0,0)
982     GstVaapiRectangle tmp_rect;
983 #endif
984     GstFlowReturn ret;
985
986 #if GST_CHECK_VERSION(1,0,0)
987     GstVideoCropMeta * const crop_meta =
988         gst_buffer_get_video_crop_meta(src_buffer);
989     if (crop_meta) {
990         surface_rect = &tmp_rect;
991         surface_rect->x = crop_meta->x;
992         surface_rect->y = crop_meta->y;
993         surface_rect->width = crop_meta->width;
994         surface_rect->height = crop_meta->height;
995     }
996 #endif
997
998     ret = gst_vaapi_plugin_base_get_input_buffer(GST_VAAPI_PLUGIN_BASE(sink),
999         src_buffer, &buffer);
1000     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_SUPPORTED)
1001         return ret;
1002
1003     meta = gst_buffer_get_vaapi_video_meta(buffer);
1004     GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE(sink,
1005         gst_vaapi_video_meta_get_display(meta));
1006
1007     if (!sink->window)
1008         goto error;
1009
1010     gst_vaapisink_ensure_rotation(sink, TRUE);
1011
1012     surface = gst_vaapi_video_meta_get_surface(meta);
1013     if (!surface)
1014         goto error;
1015
1016     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
1017               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
1018
1019     if (!surface_rect)
1020         surface_rect = (GstVaapiRectangle *)
1021             gst_vaapi_video_meta_get_render_rect(meta);
1022
1023     if (surface_rect)
1024         GST_DEBUG("render rect (%d,%d), size %ux%u",
1025                   surface_rect->x, surface_rect->y,
1026                   surface_rect->width, surface_rect->height);
1027
1028     flags = gst_vaapi_video_meta_get_render_flags(meta);
1029
1030     /* Append default color standard obtained from caps if none was
1031        available on a per-buffer basis */
1032     if (!(flags & GST_VAAPI_COLOR_STANDARD_MASK))
1033         flags |= sink->color_standard;
1034
1035     if (!gst_vaapi_apply_composition(surface, src_buffer))
1036         GST_WARNING("could not update subtitles");
1037
1038     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
1039 #if USE_DRM
1040     case GST_VAAPI_DISPLAY_TYPE_DRM:
1041         success = TRUE;
1042         break;
1043 #endif
1044 #if USE_GLX
1045     case GST_VAAPI_DISPLAY_TYPE_GLX:
1046         if (!sink->use_glx)
1047             goto put_surface_x11;
1048         success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
1049             flags);
1050         break;
1051 #endif
1052 #if USE_X11
1053     case GST_VAAPI_DISPLAY_TYPE_X11:
1054     put_surface_x11:
1055         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1056         break;
1057 #endif
1058 #if USE_WAYLAND
1059     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1060         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1061         break;
1062 #endif
1063     default:
1064         GST_ERROR("unsupported display type %d",
1065                   GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1066         success = FALSE;
1067         break;
1068     }
1069     if (!success)
1070         goto error;
1071
1072     /* Retain VA surface until the next one is displayed */
1073     if (sink->use_overlay)
1074         gst_buffer_replace(&sink->video_buffer, buffer);
1075     gst_buffer_unref(buffer);
1076     return GST_FLOW_OK;
1077
1078 error:
1079     gst_buffer_unref(buffer);
1080     return GST_FLOW_EOS;
1081 }
1082
1083 #if GST_CHECK_VERSION(1,0,0)
1084 static gboolean
1085 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
1086 {
1087     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
1088
1089     if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1090         return FALSE;
1091
1092     gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1093     gst_query_add_allocation_meta(query,
1094         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1095     return TRUE;
1096 }
1097 #else
1098 static GstFlowReturn
1099 gst_vaapisink_buffer_alloc(GstBaseSink *base_sink, guint64 offset, guint size,
1100     GstCaps *caps, GstBuffer **outbuf_ptr)
1101 {
1102     return gst_vaapi_plugin_base_allocate_input_buffer(
1103         GST_VAAPI_PLUGIN_BASE(base_sink), caps, outbuf_ptr);
1104 }
1105 #endif
1106
1107 static gboolean
1108 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
1109 {
1110     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1111
1112     GST_INFO_OBJECT(sink, "query type %s", GST_QUERY_TYPE_NAME(query));
1113
1114     if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(sink))) {
1115         GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1116         return TRUE;
1117     }
1118
1119     return GST_BASE_SINK_CLASS(gst_vaapisink_parent_class)->query(base_sink,
1120         query);
1121 }
1122
1123 static void
1124 gst_vaapisink_finalize(GObject *object)
1125 {
1126     gst_vaapisink_destroy(GST_VAAPISINK(object));
1127
1128     gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(object));
1129     G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
1130 }
1131
1132 static void
1133 gst_vaapisink_set_property(
1134     GObject      *object,
1135     guint         prop_id,
1136     const GValue *value,
1137     GParamSpec   *pspec
1138 )
1139 {
1140     GstVaapiSink * const sink = GST_VAAPISINK(object);
1141
1142     switch (prop_id) {
1143     case PROP_DISPLAY_TYPE:
1144         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
1145             g_value_get_enum(value));
1146         break;
1147     case PROP_FULLSCREEN:
1148         sink->fullscreen = g_value_get_boolean(value);
1149         break;
1150     case PROP_SYNCHRONOUS:
1151         sink->synchronous = g_value_get_boolean(value);
1152         break;
1153     case PROP_USE_GLX:
1154         sink->use_glx = g_value_get_boolean(value);
1155         break;
1156     case PROP_USE_REFLECTION:
1157         sink->use_reflection = g_value_get_boolean(value);
1158         break;
1159     case PROP_ROTATION:
1160         sink->rotation_req = g_value_get_enum(value);
1161         break;
1162     case PROP_FORCE_ASPECT_RATIO:
1163         sink->keep_aspect = g_value_get_boolean(value);
1164         break;
1165     default:
1166         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1167         break;
1168     }
1169 }
1170
1171 static void
1172 gst_vaapisink_get_property(
1173     GObject    *object,
1174     guint       prop_id,
1175     GValue     *value,
1176     GParamSpec *pspec
1177 )
1178 {
1179     GstVaapiSink * const sink = GST_VAAPISINK(object);
1180
1181     switch (prop_id) {
1182     case PROP_DISPLAY_TYPE:
1183         g_value_set_enum(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1184         break;
1185     case PROP_FULLSCREEN:
1186         g_value_set_boolean(value, sink->fullscreen);
1187         break;
1188     case PROP_SYNCHRONOUS:
1189         g_value_set_boolean(value, sink->synchronous);
1190         break;
1191     case PROP_USE_GLX:
1192         g_value_set_boolean(value, sink->use_glx);
1193         break;
1194     case PROP_USE_REFLECTION:
1195         g_value_set_boolean(value, sink->use_reflection);
1196         break;
1197     case PROP_ROTATION:
1198         g_value_set_enum(value, sink->rotation);
1199         break;
1200     case PROP_FORCE_ASPECT_RATIO:
1201         g_value_set_boolean(value, sink->keep_aspect);
1202         break;
1203     default:
1204         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1205         break;
1206     }
1207 }
1208
1209 static void
1210 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
1211 {
1212     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
1213     GstElementClass * const  element_class  = GST_ELEMENT_CLASS(klass);
1214     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
1215     GstVaapiPluginBaseClass * const base_plugin_class =
1216         GST_VAAPI_PLUGIN_BASE_CLASS(klass);
1217     GstPadTemplate *pad_template;
1218
1219     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
1220                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1221
1222     gst_vaapi_plugin_base_class_init(base_plugin_class);
1223     base_plugin_class->has_interface    = gst_vaapisink_has_interface;
1224     base_plugin_class->display_changed  = gst_vaapisink_display_changed;
1225
1226     object_class->finalize       = gst_vaapisink_finalize;
1227     object_class->set_property   = gst_vaapisink_set_property;
1228     object_class->get_property   = gst_vaapisink_get_property;
1229
1230     basesink_class->start        = gst_vaapisink_start;
1231     basesink_class->stop         = gst_vaapisink_stop;
1232     basesink_class->get_caps     = gst_vaapisink_get_caps;
1233     basesink_class->set_caps     = gst_vaapisink_set_caps;
1234     basesink_class->preroll      = gst_vaapisink_show_frame;
1235     basesink_class->render       = gst_vaapisink_show_frame;
1236     basesink_class->query        = gst_vaapisink_query;
1237 #if GST_CHECK_VERSION(1,0,0)
1238     basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1239 #else
1240     basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1241 #endif
1242
1243     gst_element_class_set_static_metadata(element_class,
1244         "VA-API sink",
1245         "Sink/Video",
1246         GST_PLUGIN_DESC,
1247         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1248
1249     pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
1250     gst_element_class_add_pad_template(element_class, pad_template);
1251
1252     g_object_class_install_property
1253         (object_class,
1254          PROP_DISPLAY_TYPE,
1255          g_param_spec_enum("display",
1256                            "display type",
1257                            "display type to use",
1258                            GST_VAAPI_TYPE_DISPLAY_TYPE,
1259                            GST_VAAPI_DISPLAY_TYPE_ANY,
1260                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1261
1262 #if USE_GLX
1263     g_object_class_install_property
1264         (object_class,
1265          PROP_USE_GLX,
1266          g_param_spec_boolean("use-glx",
1267                               "OpenGL rendering",
1268                               "Enables OpenGL rendering",
1269                               FALSE,
1270                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1271
1272     g_object_class_install_property
1273         (object_class,
1274          PROP_USE_REFLECTION,
1275          g_param_spec_boolean("use-reflection",
1276                               "Reflection effect",
1277                               "Enables OpenGL reflection effect",
1278                               FALSE,
1279                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1280 #endif
1281
1282     g_object_class_install_property
1283         (object_class,
1284          PROP_FULLSCREEN,
1285          g_param_spec_boolean("fullscreen",
1286                               "Fullscreen",
1287                               "Requests window in fullscreen state",
1288                               FALSE,
1289                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1290
1291     /**
1292      * GstVaapiSink:synchronous:
1293      *
1294      * When enabled, runs the X display in synchronous mode. Note that
1295      * this is used only for debugging.
1296      */
1297     g_object_class_install_property
1298         (object_class,
1299          PROP_SYNCHRONOUS,
1300          g_param_spec_boolean("synchronous",
1301                               "Synchronous mode",
1302                               "Toggles X display synchronous mode",
1303                               FALSE,
1304                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1305
1306     /**
1307      * GstVaapiSink:rotation:
1308      *
1309      * The VA display rotation mode, expressed as a #GstVaapiRotation.
1310      */
1311     g_object_class_install_property
1312         (object_class,
1313          PROP_ROTATION,
1314          g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
1315                            "rotation",
1316                            "The display rotation mode",
1317                            GST_VAAPI_TYPE_ROTATION,
1318                            DEFAULT_ROTATION,
1319                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1320
1321     /**
1322      * GstVaapiSink:force-aspect-ratio:
1323      *
1324      * When enabled, scaling respects video aspect ratio; when disabled, the
1325      * video is distorted to fit the window.
1326      */
1327     g_object_class_install_property
1328         (object_class,
1329          PROP_FORCE_ASPECT_RATIO,
1330          g_param_spec_boolean("force-aspect-ratio",
1331                               "Force aspect ratio",
1332                               "When enabled, scaling will respect original aspect ratio",
1333                               TRUE,
1334                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1335 }
1336
1337 static void
1338 gst_vaapisink_init(GstVaapiSink *sink)
1339 {
1340     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(sink);
1341
1342     gst_vaapi_plugin_base_init(plugin, GST_CAT_DEFAULT);
1343     gst_vaapi_plugin_base_set_display_type(plugin, DEFAULT_DISPLAY_TYPE);
1344
1345     sink->caps           = NULL;
1346     sink->window         = NULL;
1347     sink->window_width   = 0;
1348     sink->window_height  = 0;
1349     sink->texture        = NULL;
1350     sink->video_buffer   = NULL;
1351     sink->video_width    = 0;
1352     sink->video_height   = 0;
1353     sink->video_par_n    = 1;
1354     sink->video_par_d    = 1;
1355     sink->foreign_window = FALSE;
1356     sink->fullscreen     = FALSE;
1357     sink->synchronous    = FALSE;
1358     sink->rotation       = DEFAULT_ROTATION;
1359     sink->rotation_req   = DEFAULT_ROTATION;
1360     sink->use_reflection = FALSE;
1361     sink->use_overlay    = FALSE;
1362     sink->use_rotation   = FALSE;
1363     sink->keep_aspect    = TRUE;
1364     gst_video_info_init(&sink->video_info);
1365 }