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