vaapisink: fix GstVideoOverlay::expose() implementation.
[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     PROP_VIEW_ID,
139 };
140
141 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
142 #define DEFAULT_ROTATION                GST_VAAPI_ROTATION_0
143
144 static inline gboolean
145 gst_vaapisink_ensure_display(GstVaapiSink *sink);
146
147 /* GstVideoOverlay interface */
148
149 #if USE_X11
150 static gboolean
151 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
152 #endif
153
154 static GstFlowReturn
155 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
156
157 static void
158 gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
159     guintptr window)
160 {
161     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
162     GstVaapiDisplayType display_type;
163
164     if (!gst_vaapisink_ensure_display(sink))
165         return;
166     display_type = GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
167
168     /* Disable GLX rendering when vaapisink is using a foreign X
169        window. It's pretty much useless */
170     if (display_type == GST_VAAPI_DISPLAY_TYPE_GLX) {
171         display_type = GST_VAAPI_DISPLAY_TYPE_X11;
172         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
173             display_type);
174     }
175
176     sink->foreign_window = TRUE;
177
178     switch (display_type) {
179 #if USE_X11
180     case GST_VAAPI_DISPLAY_TYPE_X11:
181         gst_vaapisink_ensure_window_xid(sink, window);
182         break;
183 #endif
184     default:
185         break;
186     }
187 }
188
189 static void
190 gst_vaapisink_video_overlay_set_render_rectangle(
191     GstVideoOverlay *overlay,
192     gint         x,
193     gint         y,
194     gint         width,
195     gint         height
196 )
197 {
198     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
199     GstVaapiRectangle * const display_rect = &sink->display_rect;
200
201     display_rect->x      = x;
202     display_rect->y      = y;
203     display_rect->width  = width;
204     display_rect->height = height;
205     
206     GST_DEBUG("render rect (%d,%d):%ux%u",
207               display_rect->x, display_rect->y,
208               display_rect->width, display_rect->height);
209 }
210
211 static void
212 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
213 {
214     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
215
216     if (sink->video_buffer)
217         gst_vaapisink_show_frame(GST_BASE_SINK_CAST(sink), 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     GstVaapiSurfaceProxy *proxy;
969     GstVaapiSurface *surface;
970     GstBuffer *buffer;
971     guint flags;
972     gboolean success;
973     GstVaapiRectangle *surface_rect = NULL;
974 #if GST_CHECK_VERSION(1,0,0)
975     GstVaapiRectangle tmp_rect;
976 #endif
977     GstFlowReturn ret;
978     gint32 view_id;
979
980 #if GST_CHECK_VERSION(1,0,0)
981     GstVideoCropMeta * const crop_meta =
982         gst_buffer_get_video_crop_meta(src_buffer);
983     if (crop_meta) {
984         surface_rect = &tmp_rect;
985         surface_rect->x = crop_meta->x;
986         surface_rect->y = crop_meta->y;
987         surface_rect->width = crop_meta->width;
988         surface_rect->height = crop_meta->height;
989     }
990 #endif
991
992     ret = gst_vaapi_plugin_base_get_input_buffer(GST_VAAPI_PLUGIN_BASE(sink),
993         src_buffer, &buffer);
994     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_SUPPORTED)
995         return ret;
996
997     meta = gst_buffer_get_vaapi_video_meta(buffer);
998     GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE(sink,
999         gst_vaapi_video_meta_get_display(meta));
1000
1001     gst_vaapisink_ensure_rotation(sink, TRUE);
1002
1003     proxy = gst_vaapi_video_meta_get_surface_proxy(meta);
1004     if (!proxy)
1005         goto error;
1006
1007     /* Valide view component to display */
1008     view_id = GST_VAAPI_SURFACE_PROXY_VIEW_ID(proxy);
1009     if (G_UNLIKELY(sink->view_id == -1))
1010         sink->view_id = view_id;
1011     else if (sink->view_id != view_id) {
1012         gst_buffer_unref(buffer);
1013         return GST_FLOW_OK;
1014     }
1015
1016     surface = gst_vaapi_video_meta_get_surface(meta);
1017     if (!surface)
1018         goto error;
1019
1020     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
1021               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
1022
1023     if (!surface_rect)
1024         surface_rect = (GstVaapiRectangle *)
1025             gst_vaapi_video_meta_get_render_rect(meta);
1026
1027     if (surface_rect)
1028         GST_DEBUG("render rect (%d,%d), size %ux%u",
1029                   surface_rect->x, surface_rect->y,
1030                   surface_rect->width, surface_rect->height);
1031
1032     flags = gst_vaapi_video_meta_get_render_flags(meta);
1033
1034     /* Append default color standard obtained from caps if none was
1035        available on a per-buffer basis */
1036     if (!(flags & GST_VAAPI_COLOR_STANDARD_MASK))
1037         flags |= sink->color_standard;
1038
1039     if (!gst_vaapi_apply_composition(surface, src_buffer))
1040         GST_WARNING("could not update subtitles");
1041
1042     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
1043 #if USE_DRM
1044     case GST_VAAPI_DISPLAY_TYPE_DRM:
1045         success = TRUE;
1046         break;
1047 #endif
1048 #if USE_GLX
1049     case GST_VAAPI_DISPLAY_TYPE_GLX:
1050         if (!sink->use_glx)
1051             goto put_surface_x11;
1052         success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
1053             flags);
1054         break;
1055 #endif
1056 #if USE_X11
1057     case GST_VAAPI_DISPLAY_TYPE_X11:
1058     put_surface_x11:
1059         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1060         break;
1061 #endif
1062 #if USE_WAYLAND
1063     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1064         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1065         break;
1066 #endif
1067     default:
1068         GST_ERROR("unsupported display type %d",
1069                   GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1070         success = FALSE;
1071         break;
1072     }
1073     if (!success)
1074         goto error;
1075
1076     /* Retain VA surface until the next one is displayed */
1077     gst_buffer_replace(&sink->video_buffer, buffer);
1078     gst_buffer_unref(buffer);
1079     return GST_FLOW_OK;
1080
1081 error:
1082     gst_buffer_unref(buffer);
1083     return GST_FLOW_EOS;
1084 }
1085
1086 #if GST_CHECK_VERSION(1,0,0)
1087 static gboolean
1088 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
1089 {
1090     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
1091
1092     if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1093         return FALSE;
1094
1095     gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1096     gst_query_add_allocation_meta(query,
1097         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1098     return TRUE;
1099 }
1100 #else
1101 static GstFlowReturn
1102 gst_vaapisink_buffer_alloc(GstBaseSink *base_sink, guint64 offset, guint size,
1103     GstCaps *caps, GstBuffer **outbuf_ptr)
1104 {
1105     return gst_vaapi_plugin_base_allocate_input_buffer(
1106         GST_VAAPI_PLUGIN_BASE(base_sink), caps, outbuf_ptr);
1107 }
1108 #endif
1109
1110 static gboolean
1111 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
1112 {
1113     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1114
1115     GST_INFO_OBJECT(sink, "query type %s", GST_QUERY_TYPE_NAME(query));
1116
1117     if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(sink))) {
1118         GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1119         return TRUE;
1120     }
1121
1122     return GST_BASE_SINK_CLASS(gst_vaapisink_parent_class)->query(base_sink,
1123         query);
1124 }
1125
1126 static void
1127 gst_vaapisink_finalize(GObject *object)
1128 {
1129     gst_vaapisink_destroy(GST_VAAPISINK(object));
1130
1131     gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(object));
1132     G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
1133 }
1134
1135 static void
1136 gst_vaapisink_set_property(
1137     GObject      *object,
1138     guint         prop_id,
1139     const GValue *value,
1140     GParamSpec   *pspec
1141 )
1142 {
1143     GstVaapiSink * const sink = GST_VAAPISINK(object);
1144
1145     switch (prop_id) {
1146     case PROP_DISPLAY_TYPE:
1147         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
1148             g_value_get_enum(value));
1149         break;
1150     case PROP_DISPLAY_NAME:
1151         gst_vaapi_plugin_base_set_display_name(GST_VAAPI_PLUGIN_BASE(sink),
1152             g_value_get_string(value));
1153         break;
1154     case PROP_FULLSCREEN:
1155         sink->fullscreen = g_value_get_boolean(value);
1156         break;
1157     case PROP_SYNCHRONOUS:
1158         sink->synchronous = g_value_get_boolean(value);
1159         break;
1160     case PROP_VIEW_ID:
1161         sink->view_id = g_value_get_int(value);
1162         break;
1163     case PROP_USE_GLX:
1164         sink->use_glx = g_value_get_boolean(value);
1165         break;
1166     case PROP_USE_REFLECTION:
1167         sink->use_reflection = g_value_get_boolean(value);
1168         break;
1169     case PROP_ROTATION:
1170         sink->rotation_req = g_value_get_enum(value);
1171         break;
1172     case PROP_FORCE_ASPECT_RATIO:
1173         sink->keep_aspect = g_value_get_boolean(value);
1174         break;
1175     default:
1176         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1177         break;
1178     }
1179 }
1180
1181 static void
1182 gst_vaapisink_get_property(
1183     GObject    *object,
1184     guint       prop_id,
1185     GValue     *value,
1186     GParamSpec *pspec
1187 )
1188 {
1189     GstVaapiSink * const sink = GST_VAAPISINK(object);
1190
1191     switch (prop_id) {
1192     case PROP_DISPLAY_TYPE:
1193         g_value_set_enum(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1194         break;
1195     case PROP_DISPLAY_NAME:
1196         g_value_set_string(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_NAME(sink));
1197         break;
1198     case PROP_FULLSCREEN:
1199         g_value_set_boolean(value, sink->fullscreen);
1200         break;
1201     case PROP_SYNCHRONOUS:
1202         g_value_set_boolean(value, sink->synchronous);
1203         break;
1204     case PROP_VIEW_ID:
1205         g_value_set_int(value, sink->view_id);
1206         break;
1207     case PROP_USE_GLX:
1208         g_value_set_boolean(value, sink->use_glx);
1209         break;
1210     case PROP_USE_REFLECTION:
1211         g_value_set_boolean(value, sink->use_reflection);
1212         break;
1213     case PROP_ROTATION:
1214         g_value_set_enum(value, sink->rotation);
1215         break;
1216     case PROP_FORCE_ASPECT_RATIO:
1217         g_value_set_boolean(value, sink->keep_aspect);
1218         break;
1219     default:
1220         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1221         break;
1222     }
1223 }
1224
1225 static void
1226 gst_vaapisink_set_bus(GstElement *element, GstBus *bus)
1227 {
1228     /* Make sure to allocate a VA display in the sink element first,
1229        so that upstream elements could query a display that was
1230        allocated here, and that exactly matches what the user
1231        requested through the "display" property */
1232     if (!GST_ELEMENT_BUS(element) && bus)
1233         gst_vaapisink_ensure_display(GST_VAAPISINK(element));
1234
1235     GST_ELEMENT_CLASS(gst_vaapisink_parent_class)->set_bus(element, bus);
1236 }
1237
1238 static void
1239 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
1240 {
1241     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
1242     GstElementClass * const  element_class  = GST_ELEMENT_CLASS(klass);
1243     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
1244     GstVaapiPluginBaseClass * const base_plugin_class =
1245         GST_VAAPI_PLUGIN_BASE_CLASS(klass);
1246     GstPadTemplate *pad_template;
1247
1248     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
1249                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1250
1251     gst_vaapi_plugin_base_class_init(base_plugin_class);
1252     base_plugin_class->has_interface    = gst_vaapisink_has_interface;
1253     base_plugin_class->display_changed  = gst_vaapisink_display_changed;
1254
1255     object_class->finalize       = gst_vaapisink_finalize;
1256     object_class->set_property   = gst_vaapisink_set_property;
1257     object_class->get_property   = gst_vaapisink_get_property;
1258
1259     basesink_class->start        = gst_vaapisink_start;
1260     basesink_class->stop         = gst_vaapisink_stop;
1261     basesink_class->get_caps     = gst_vaapisink_get_caps;
1262     basesink_class->set_caps     = gst_vaapisink_set_caps;
1263     basesink_class->preroll      = gst_vaapisink_show_frame;
1264     basesink_class->render       = gst_vaapisink_show_frame;
1265     basesink_class->query        = gst_vaapisink_query;
1266 #if GST_CHECK_VERSION(1,0,0)
1267     basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1268 #else
1269     basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1270 #endif
1271
1272     element_class->set_bus = gst_vaapisink_set_bus;
1273     gst_element_class_set_static_metadata(element_class,
1274         "VA-API sink",
1275         "Sink/Video",
1276         GST_PLUGIN_DESC,
1277         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1278
1279     pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
1280     gst_element_class_add_pad_template(element_class, pad_template);
1281
1282     g_object_class_install_property
1283         (object_class,
1284          PROP_DISPLAY_TYPE,
1285          g_param_spec_enum("display",
1286                            "display type",
1287                            "display type to use",
1288                            GST_VAAPI_TYPE_DISPLAY_TYPE,
1289                            GST_VAAPI_DISPLAY_TYPE_ANY,
1290                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1291
1292     g_object_class_install_property
1293         (object_class,
1294          PROP_DISPLAY_NAME,
1295          g_param_spec_string("display-name",
1296                              "display name",
1297                              "display name to use",
1298                              NULL,
1299                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1300
1301 #if USE_GLX
1302     g_object_class_install_property
1303         (object_class,
1304          PROP_USE_GLX,
1305          g_param_spec_boolean("use-glx",
1306                               "OpenGL rendering",
1307                               "Enables OpenGL rendering",
1308                               FALSE,
1309                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1310
1311     g_object_class_install_property
1312         (object_class,
1313          PROP_USE_REFLECTION,
1314          g_param_spec_boolean("use-reflection",
1315                               "Reflection effect",
1316                               "Enables OpenGL reflection effect",
1317                               FALSE,
1318                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1319 #endif
1320
1321     g_object_class_install_property
1322         (object_class,
1323          PROP_FULLSCREEN,
1324          g_param_spec_boolean("fullscreen",
1325                               "Fullscreen",
1326                               "Requests window in fullscreen state",
1327                               FALSE,
1328                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1329
1330     /**
1331      * GstVaapiSink:synchronous:
1332      *
1333      * When enabled, runs the X display in synchronous mode. Note that
1334      * this is used only for debugging.
1335      */
1336     g_object_class_install_property
1337         (object_class,
1338          PROP_SYNCHRONOUS,
1339          g_param_spec_boolean("synchronous",
1340                               "Synchronous mode",
1341                               "Toggles X display synchronous mode",
1342                               FALSE,
1343                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1344
1345     /**
1346      * GstVaapiSink:rotation:
1347      *
1348      * The VA display rotation mode, expressed as a #GstVaapiRotation.
1349      */
1350     g_object_class_install_property
1351         (object_class,
1352          PROP_ROTATION,
1353          g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
1354                            "rotation",
1355                            "The display rotation mode",
1356                            GST_VAAPI_TYPE_ROTATION,
1357                            DEFAULT_ROTATION,
1358                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1359
1360     /**
1361      * GstVaapiSink:force-aspect-ratio:
1362      *
1363      * When enabled, scaling respects video aspect ratio; when disabled, the
1364      * video is distorted to fit the window.
1365      */
1366     g_object_class_install_property
1367         (object_class,
1368          PROP_FORCE_ASPECT_RATIO,
1369          g_param_spec_boolean("force-aspect-ratio",
1370                               "Force aspect ratio",
1371                               "When enabled, scaling will respect original aspect ratio",
1372                               TRUE,
1373                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1374
1375     /**
1376      * GstVaapiSink:view-id:
1377      *
1378      * When not set to -1, the displayed frame will always be the one
1379      * that matches the view-id of the very first displayed frame. Any
1380      * other number will indicate the desire to display the supplied
1381      * view-id only.
1382      */
1383     g_object_class_install_property
1384         (object_class,
1385          PROP_VIEW_ID,
1386          g_param_spec_int("view-id",
1387                           "View ID",
1388                           "ID of the view component of interest to display",
1389                           -1, G_MAXINT32, -1,
1390                           G_PARAM_READWRITE));
1391 }
1392
1393 static void
1394 gst_vaapisink_init(GstVaapiSink *sink)
1395 {
1396     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(sink);
1397
1398     gst_vaapi_plugin_base_init(plugin, GST_CAT_DEFAULT);
1399     gst_vaapi_plugin_base_set_display_type(plugin, DEFAULT_DISPLAY_TYPE);
1400
1401     sink->caps           = NULL;
1402     sink->window         = NULL;
1403     sink->window_width   = 0;
1404     sink->window_height  = 0;
1405     sink->texture        = NULL;
1406     sink->video_buffer   = NULL;
1407     sink->video_width    = 0;
1408     sink->video_height   = 0;
1409     sink->video_par_n    = 1;
1410     sink->video_par_d    = 1;
1411     sink->view_id        = -1;
1412     sink->foreign_window = FALSE;
1413     sink->fullscreen     = FALSE;
1414     sink->synchronous    = FALSE;
1415     sink->rotation       = DEFAULT_ROTATION;
1416     sink->rotation_req   = DEFAULT_ROTATION;
1417     sink->use_reflection = FALSE;
1418     sink->use_overlay    = FALSE;
1419     sink->use_rotation   = FALSE;
1420     sink->keep_aspect    = TRUE;
1421     gst_video_info_init(&sink->video_info);
1422 }