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