vaapisink: automatically detect overlay rendering mode.
[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  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapisink
25  * @short_description: A VA-API based videosink
26  *
27  * vaapisink renders video frames to a drawable (X #Window) on a local
28  * display using the Video Acceleration (VA) API. The element will
29  * create its own internal window and render into it.
30  */
31
32 #include "config.h"
33 #include <gst/gst.h>
34 #include <gst/video/video.h>
35 #include <gst/video/videocontext.h>
36 #include <gst/vaapi/gstvaapivideobuffer.h>
37 #include <gst/vaapi/gstvaapivideosink.h>
38 #if USE_DRM
39 # include <gst/vaapi/gstvaapidisplay_drm.h>
40 #endif
41 #if USE_X11
42 # include <gst/vaapi/gstvaapidisplay_x11.h>
43 # include <gst/vaapi/gstvaapiwindow_x11.h>
44 #endif
45 #if USE_GLX
46 # include <gst/vaapi/gstvaapidisplay_glx.h>
47 # include <gst/vaapi/gstvaapiwindow_glx.h>
48 #endif
49 #if USE_WAYLAND
50 # include <gst/vaapi/gstvaapidisplay_wayland.h>
51 # include <gst/vaapi/gstvaapiwindow_wayland.h>
52 #endif
53
54 /* Supported interfaces */
55 #include <gst/interfaces/xoverlay.h>
56
57 #include "gstvaapisink.h"
58 #include "gstvaapipluginutil.h"
59
60 #define GST_PLUGIN_NAME "vaapisink"
61 #define GST_PLUGIN_DESC "A VA-API based videosink"
62
63 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
64 #define GST_CAT_DEFAULT gst_debug_vaapisink
65
66 /* ElementFactory information */
67 static const GstElementDetails gst_vaapisink_details =
68     GST_ELEMENT_DETAILS(
69         "VA-API sink",
70         "Sink/Video",
71         GST_PLUGIN_DESC,
72         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
73
74 /* Default template */
75 static GstStaticPadTemplate gst_vaapisink_sink_factory =
76     GST_STATIC_PAD_TEMPLATE(
77         "sink",
78         GST_PAD_SINK,
79         GST_PAD_ALWAYS,
80         GST_STATIC_CAPS(GST_VAAPI_SURFACE_CAPS));
81
82 static void
83 gst_vaapisink_implements_iface_init(GstImplementsInterfaceClass *iface);
84
85 static void
86 gst_vaapisink_video_context_iface_init(GstVideoContextInterface *iface);
87
88 static void
89 gst_vaapisink_xoverlay_iface_init(GstXOverlayClass *iface);
90
91 G_DEFINE_TYPE_WITH_CODE(
92     GstVaapiSink,
93     gst_vaapisink,
94     GST_TYPE_VIDEO_SINK,
95     G_IMPLEMENT_INTERFACE(GST_TYPE_IMPLEMENTS_INTERFACE,
96                           gst_vaapisink_implements_iface_init);
97     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_CONTEXT,
98                           gst_vaapisink_video_context_iface_init);
99     G_IMPLEMENT_INTERFACE(GST_TYPE_X_OVERLAY,
100                           gst_vaapisink_xoverlay_iface_init));
101
102 enum {
103     PROP_0,
104
105     PROP_DISPLAY_TYPE,
106     PROP_FULLSCREEN,
107     PROP_SYNCHRONOUS,
108     PROP_USE_REFLECTION
109 };
110
111 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
112
113 /* GstImplementsInterface interface */
114
115 static gboolean
116 gst_vaapisink_implements_interface_supported(
117     GstImplementsInterface *iface,
118     GType                   type
119 )
120 {
121     return (type == GST_TYPE_VIDEO_CONTEXT ||
122             type == GST_TYPE_X_OVERLAY);
123 }
124
125 static void
126 gst_vaapisink_implements_iface_init(GstImplementsInterfaceClass *iface)
127 {
128     iface->supported = gst_vaapisink_implements_interface_supported;
129 }
130
131 /* GstVaapiVideoSink interface */
132
133 static void
134 gst_vaapisink_set_video_context(GstVideoContext *context, const gchar *type,
135     const GValue *value)
136 {
137   GstVaapiSink *sink = GST_VAAPISINK (context);
138   gst_vaapi_set_display (type, value, &sink->display);
139 }
140
141 static void
142 gst_vaapisink_video_context_iface_init(GstVideoContextInterface *iface)
143 {
144     iface->set_context = gst_vaapisink_set_video_context;
145 }
146
147 /* GstXOverlay 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_xoverlay_set_window_handle(GstXOverlay *overlay, guintptr window)
159 {
160     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
161
162     /* Disable GLX rendering when vaapisink is using a foreign X
163        window. It's pretty much useless */
164     if (sink->display_type == GST_VAAPI_DISPLAY_TYPE_GLX)
165         sink->display_type = GST_VAAPI_DISPLAY_TYPE_X11;
166
167     sink->foreign_window = TRUE;
168
169     switch (sink->display_type) {
170 #if USE_X11
171     case GST_VAAPI_DISPLAY_TYPE_X11:
172         gst_vaapisink_ensure_window_xid(sink, window);
173         break;
174 #endif
175     default:
176         break;
177     }
178 }
179
180 static void
181 gst_vaapisink_xoverlay_set_render_rectangle(
182     GstXOverlay *overlay,
183     gint         x,
184     gint         y,
185     gint         width,
186     gint         height
187 )
188 {
189     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
190     GstVaapiRectangle * const display_rect = &sink->display_rect;
191
192     display_rect->x      = x;
193     display_rect->y      = y;
194     display_rect->width  = width;
195     display_rect->height = height;
196     
197     GST_DEBUG("render rect (%d,%d):%ux%u",
198               display_rect->x, display_rect->y,
199               display_rect->width, display_rect->height);
200 }
201
202 static void
203 gst_vaapisink_xoverlay_expose(GstXOverlay *overlay)
204 {
205     GstBaseSink * const base_sink = GST_BASE_SINK(overlay);
206     GstBuffer *buffer;
207
208     buffer = gst_base_sink_get_last_buffer(base_sink);
209     if (buffer) {
210         gst_vaapisink_show_frame(base_sink, buffer);
211         gst_buffer_unref(buffer);
212     }
213 }
214
215 static void
216 gst_vaapisink_xoverlay_iface_init(GstXOverlayClass *iface)
217 {
218     iface->set_window_handle    = gst_vaapisink_xoverlay_set_window_handle;
219     iface->set_render_rectangle = gst_vaapisink_xoverlay_set_render_rectangle;
220     iface->expose               = gst_vaapisink_xoverlay_expose;
221 }
222
223 static void
224 gst_vaapisink_destroy(GstVaapiSink *sink)
225 {
226     gst_buffer_replace(&sink->video_buffer, NULL);
227     g_clear_object(&sink->texture);
228     g_clear_object(&sink->display);
229
230     gst_caps_replace(&sink->caps, NULL);
231 }
232
233 #if USE_X11
234 /* Checks whether a ConfigureNotify event is in the queue */
235 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
236 struct _ConfigureNotifyEventPendingArgs {
237     Window      window;
238     guint       width;
239     guint       height;
240     gboolean    match;
241 };
242
243 static Bool
244 configure_notify_event_pending_cb(Display *dpy, XEvent *xev, XPointer arg)
245 {
246     ConfigureNotifyEventPendingArgs * const args =
247         (ConfigureNotifyEventPendingArgs *)arg;
248
249     if (xev->type == ConfigureNotify &&
250         xev->xconfigure.window == args->window &&
251         xev->xconfigure.width  == args->width  &&
252         xev->xconfigure.height == args->height)
253         args->match = TRUE;
254
255     /* XXX: this is a hack to traverse the whole queue because we
256        can't use XPeekIfEvent() since it could block */
257     return False;
258 }
259
260 static gboolean
261 configure_notify_event_pending(
262     GstVaapiSink *sink,
263     Window        window,
264     guint         width,
265     guint         height
266 )
267 {
268     ConfigureNotifyEventPendingArgs args;
269     XEvent xev;
270
271     args.window = window;
272     args.width  = width;
273     args.height = height;
274     args.match  = FALSE;
275
276     /* XXX: don't use XPeekIfEvent() because it might block */
277     XCheckIfEvent(
278         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(sink->display)),
279         &xev,
280         configure_notify_event_pending_cb, (XPointer)&args
281     );
282     return args.match;
283 }
284 #endif
285
286 static const gchar *
287 get_display_type_name(GstVaapiDisplayType display_type)
288 {
289     gpointer const klass = g_type_class_peek(GST_VAAPI_TYPE_DISPLAY_TYPE);
290     GEnumValue * const e = g_enum_get_value(klass, display_type);
291
292     if (e)
293         return e->value_name;
294     return "<unknown-type>";
295 }
296
297 static inline gboolean
298 gst_vaapisink_ensure_display(GstVaapiSink *sink)
299 {
300     GstVaapiDisplayType display_type;
301     GstVaapiRenderMode render_mode;
302
303     if (!gst_vaapi_ensure_display(sink, sink->display_type, &sink->display))
304         return FALSE;
305
306     display_type = gst_vaapi_display_get_display_type(sink->display);
307     if (display_type != sink->display_type) {
308         GST_INFO("created %s %p", get_display_type_name(display_type),
309             sink->display);
310         sink->display_type = display_type;
311
312         sink->use_overlay =
313             gst_vaapi_display_get_render_mode(sink->display, &render_mode) &&
314             render_mode == GST_VAAPI_RENDER_MODE_OVERLAY;
315         GST_DEBUG("use %s rendering mode", sink->use_overlay ? "overlay" : "texture");
316     }
317     return TRUE;
318 }
319
320 static gboolean
321 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
322 {
323     GstVaapiRectangle * const display_rect = &sink->display_rect;
324     guint num, den, display_par_n, display_par_d;
325     gboolean success;
326
327     /* Return success if caps are not set yet */
328     if (!sink->caps)
329         return TRUE;
330
331     GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
332
333     gst_vaapi_display_get_pixel_aspect_ratio(
334         sink->display,
335         &display_par_n, &display_par_d
336     );
337     GST_DEBUG("display pixel-aspect-ratio %d/%d",
338               display_par_n, display_par_d);
339
340     success = gst_video_calculate_display_ratio(
341         &num, &den,
342         sink->video_width, sink->video_height,
343         sink->video_par_n, sink->video_par_d,
344         display_par_n, display_par_d
345     );
346     if (!success)
347         return FALSE;
348     GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
349               sink->video_width, sink->video_height, num, den);
350
351     display_rect->width = gst_util_uint64_scale_int(height, num, den);
352     if (display_rect->width <= width) {
353         GST_DEBUG("keeping window height");
354         display_rect->height = height;
355     }
356     else {
357         GST_DEBUG("keeping window width");
358         display_rect->width  = width;
359         display_rect->height =
360             gst_util_uint64_scale_int(width, den, num);
361     }
362     GST_DEBUG("scaling video to %ux%u", display_rect->width, display_rect->height);
363
364     g_assert(display_rect->width  <= width);
365     g_assert(display_rect->height <= height);
366
367     display_rect->x = (width  - display_rect->width)  / 2;
368     display_rect->y = (height - display_rect->height) / 2;
369
370     GST_DEBUG("render rect (%d,%d):%ux%u",
371               display_rect->x, display_rect->y,
372               display_rect->width, display_rect->height);
373     return TRUE;
374 }
375
376 static inline gboolean
377 gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
378 {
379     GstVaapiDisplay * const display = sink->display;
380
381     if (!sink->window) {
382         switch (sink->display_type) {
383 #if USE_GLX
384         case GST_VAAPI_DISPLAY_TYPE_GLX:
385             sink->window = gst_vaapi_window_glx_new(display, width, height);
386             goto notify_xoverlay_interface;
387 #endif
388 #if USE_X11
389         case GST_VAAPI_DISPLAY_TYPE_X11:
390             sink->window = gst_vaapi_window_x11_new(display, width, height);
391         notify_xoverlay_interface:
392             if (!sink->window)
393                 break;
394             gst_x_overlay_got_window_handle(
395                 GST_X_OVERLAY(sink),
396                 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window))
397             );
398             break;
399 #endif
400 #if USE_WAYLAND
401         case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
402             sink->window = gst_vaapi_window_wayland_new(display, width, height);
403             break;
404 #endif
405         default:
406             GST_ERROR("unsupported display type %d", sink->display_type);
407             return FALSE;
408         }
409     }
410     return sink->window != NULL;
411 }
412
413 #if USE_X11
414 static gboolean
415 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
416 {
417     Window rootwin;
418     unsigned int width, height, border_width, depth;
419     int x, y;
420     XID xid = window_id;
421
422     if (!gst_vaapisink_ensure_display(sink))
423         return FALSE;
424
425     gst_vaapi_display_lock(sink->display);
426     XGetGeometry(
427         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(sink->display)),
428         xid,
429         &rootwin,
430         &x, &y, &width, &height, &border_width, &depth
431     );
432     gst_vaapi_display_unlock(sink->display);
433
434     if ((width != sink->window_width || height != sink->window_height) &&
435         !configure_notify_event_pending(sink, xid, width, height)) {
436         if (!gst_vaapisink_ensure_render_rect(sink, width, height))
437             return FALSE;
438         sink->window_width  = width;
439         sink->window_height = height;
440     }
441
442     if (sink->window &&
443         gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
444         return TRUE;
445
446     g_clear_object(&sink->window);
447
448     switch (sink->display_type) {
449 #if USE_GLX
450     case GST_VAAPI_DISPLAY_TYPE_GLX:
451         sink->window = gst_vaapi_window_glx_new_with_xid(sink->display, xid);
452         break;
453 #endif
454     case GST_VAAPI_DISPLAY_TYPE_X11:
455         sink->window = gst_vaapi_window_x11_new_with_xid(sink->display, xid);
456         break;
457     default:
458         GST_ERROR("unsupported display type %d", sink->display_type);
459         return FALSE;
460     }
461     return sink->window != NULL;
462 }
463 #endif
464
465 static gboolean
466 gst_vaapisink_start(GstBaseSink *base_sink)
467 {
468     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
469
470     return gst_vaapisink_ensure_display(sink);
471 }
472
473 static gboolean
474 gst_vaapisink_stop(GstBaseSink *base_sink)
475 {
476     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
477
478     gst_buffer_replace(&sink->video_buffer, NULL);
479     g_clear_object(&sink->window);
480     g_clear_object(&sink->display);
481
482     return TRUE;
483 }
484
485 static gboolean
486 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
487 {
488     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
489     GstStructure * const structure = gst_caps_get_structure(caps, 0);
490     guint win_width, win_height, display_width, display_height;
491     gint video_width, video_height, video_par_n = 1, video_par_d = 1;
492
493 #if USE_DRM
494     if (sink->display_type == GST_VAAPI_DISPLAY_TYPE_DRM)
495         return TRUE;
496 #endif
497
498     if (!structure)
499         return FALSE;
500     if (!gst_structure_get_int(structure, "width",  &video_width))
501         return FALSE;
502     if (!gst_structure_get_int(structure, "height", &video_height))
503         return FALSE;
504     sink->video_width  = video_width;
505     sink->video_height = video_height;
506
507     gst_video_parse_caps_pixel_aspect_ratio(caps, &video_par_n, &video_par_d);
508     sink->video_par_n  = video_par_n;
509     sink->video_par_d  = video_par_d;
510     GST_DEBUG("video pixel-aspect-ratio %d/%d", video_par_n, video_par_d);
511
512     gst_caps_replace(&sink->caps, caps);
513
514     if (!gst_vaapisink_ensure_display(sink))
515         return FALSE;
516
517     gst_vaapi_display_get_size(sink->display, &display_width, &display_height);
518     if (sink->foreign_window) {
519         win_width  = sink->window_width;
520         win_height = sink->window_height;
521     }
522     else if (sink->fullscreen ||
523              video_width > display_width || video_height > display_height) {
524         win_width  = display_width;
525         win_height = display_height;
526     }
527     else {
528         win_width  = video_width;
529         win_height = video_height;
530     }
531
532     if (sink->window) {
533         if (!sink->foreign_window || sink->fullscreen)
534             gst_vaapi_window_set_size(sink->window, win_width, win_height);
535     }
536     else {
537         gst_vaapi_display_lock(sink->display);
538         gst_x_overlay_prepare_xwindow_id(GST_X_OVERLAY(sink));
539         gst_vaapi_display_unlock(sink->display);
540         if (sink->window)
541             return TRUE;
542         if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
543             return FALSE;
544         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
545         gst_vaapi_window_show(sink->window);
546         gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
547     }
548     sink->window_width  = win_width;
549     sink->window_height = win_height;
550     GST_DEBUG("window size %ux%u", win_width, win_height);
551
552     return gst_vaapisink_ensure_render_rect(sink, win_width, win_height);
553 }
554
555 #if USE_GLX
556 static void
557 render_background(GstVaapiSink *sink)
558 {
559     /* Original code from Mirco Muller (MacSlow):
560        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
561     GLfloat fStartX = 0.0f;
562     GLfloat fStartY = 0.0f;
563     GLfloat fWidth  = (GLfloat)sink->window_width;
564     GLfloat fHeight = (GLfloat)sink->window_height;
565
566     glClear(GL_COLOR_BUFFER_BIT);
567     glBegin(GL_QUADS);
568     {
569         /* top third, darker grey to white */
570         glColor3f(0.85f, 0.85f, 0.85f);
571         glVertex3f(fStartX, fStartY, 0.0f);
572         glColor3f(0.85f, 0.85f, 0.85f);
573         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
574         glColor3f(1.0f, 1.0f, 1.0f);
575         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
576         glColor3f(1.0f, 1.0f, 1.0f);
577         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
578
579         /* middle third, just plain white */
580         glColor3f(1.0f, 1.0f, 1.0f);
581         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
582         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
583         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
584         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
585
586         /* bottom third, white to lighter grey */
587         glColor3f(1.0f, 1.0f, 1.0f);
588         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
589         glColor3f(1.0f, 1.0f, 1.0f);
590         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
591         glColor3f(0.62f, 0.66f, 0.69f);
592         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
593         glColor3f(0.62f, 0.66f, 0.69f);
594         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
595     }
596     glEnd();
597 }
598
599 static void
600 render_frame(GstVaapiSink *sink)
601 {
602     const guint x1 = sink->display_rect.x;
603     const guint x2 = sink->display_rect.x + sink->display_rect.width;
604     const guint y1 = sink->display_rect.y;
605     const guint y2 = sink->display_rect.y + sink->display_rect.height;
606
607     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
608     glBegin(GL_QUADS);
609     {
610         glTexCoord2f(0.0f, 0.0f); glVertex2i(x1, y1);
611         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y2);
612         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y2);
613         glTexCoord2f(1.0f, 0.0f); glVertex2i(x2, y1);
614     }
615     glEnd();
616 }
617
618 static void
619 render_reflection(GstVaapiSink *sink)
620 {
621     const guint x1 = sink->display_rect.x;
622     const guint x2 = sink->display_rect.x + sink->display_rect.width;
623     const guint y1 = sink->display_rect.y;
624     const guint rh = sink->display_rect.height / 5;
625     GLfloat     ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
626
627     glBegin(GL_QUADS);
628     {
629         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
630         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
631         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
632
633         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
634         glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
635         glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
636     }
637     glEnd();
638 }
639
640 static gboolean
641 gst_vaapisink_show_frame_glx(
642     GstVaapiSink    *sink,
643     GstVaapiSurface *surface,
644     guint            flags
645 )
646 {
647     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(sink->window);
648     GLenum target;
649     GLuint texture;
650
651     gst_vaapi_window_glx_make_current(window);
652     if (!sink->texture) {
653         sink->texture = gst_vaapi_texture_new(
654             sink->display,
655             GL_TEXTURE_2D,
656             GL_BGRA,
657             sink->video_width,
658             sink->video_height
659         );
660         if (!sink->texture)
661             goto error_create_texture;
662     }
663     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
664         goto error_transfer_surface;
665
666     target  = gst_vaapi_texture_get_target(sink->texture);
667     texture = gst_vaapi_texture_get_id(sink->texture);
668     if (target != GL_TEXTURE_2D || !texture)
669         return FALSE;
670
671     if (sink->use_reflection)
672         render_background(sink);
673
674     glEnable(target);
675     glBindTexture(target, texture);
676     {
677         if (sink->use_reflection) {
678             glPushMatrix();
679             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
680             glTranslatef(50.0f, 0.0f, 0.0f);
681         }
682         render_frame(sink);
683         if (sink->use_reflection) {
684             glPushMatrix();
685             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
686             render_reflection(sink);
687             glPopMatrix();
688             glPopMatrix();
689         }
690     }
691     glBindTexture(target, 0);
692     glDisable(target);
693     gst_vaapi_window_glx_swap_buffers(window);
694     return TRUE;
695
696     /* ERRORS */
697 error_create_texture:
698     {
699         GST_DEBUG("could not create VA/GLX texture");
700         return FALSE;
701     }
702 error_transfer_surface:
703     {
704         GST_DEBUG("could not transfer VA surface to texture");
705         return FALSE;
706     }
707 }
708 #endif
709
710 static inline gboolean
711 gst_vaapisink_put_surface(
712     GstVaapiSink    *sink,
713     GstVaapiSurface *surface,
714     guint            flags
715 )
716 {
717     if (!gst_vaapi_window_put_surface(sink->window, surface,
718                 NULL, &sink->display_rect, flags)) {
719         GST_DEBUG("could not render VA surface");
720         return FALSE;
721     }
722     return TRUE;
723 }
724
725 static GstFlowReturn
726 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer)
727 {
728     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
729     GstVaapiVideoBuffer * const vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
730     GstVaapiSurface *surface;
731     guint flags;
732     gboolean success;
733     GstVideoOverlayComposition * const composition =
734         gst_video_buffer_get_overlay_composition(buffer);
735
736     if (sink->display != gst_vaapi_video_buffer_get_display (vbuffer)) {
737       g_clear_object(&sink->display);
738       sink->display = g_object_ref (gst_vaapi_video_buffer_get_display (vbuffer));
739     }
740
741     if (!sink->window)
742         return GST_FLOW_UNEXPECTED;
743
744     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
745     if (!surface)
746         return GST_FLOW_UNEXPECTED;
747
748     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
749               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
750
751     flags = gst_vaapi_video_buffer_get_render_flags(vbuffer);
752
753     if (!gst_vaapi_surface_set_subpictures_from_composition(surface,
754              composition, TRUE))
755         GST_WARNING("could not update subtitles");
756
757     switch (sink->display_type) {
758 #if USE_GLX
759     case GST_VAAPI_DISPLAY_TYPE_GLX:
760         success = gst_vaapisink_show_frame_glx(sink, surface, flags);
761         break;
762 #endif
763 #if USE_DRM
764     case GST_VAAPI_DISPLAY_TYPE_DRM:
765         success = TRUE;
766         break;
767 #endif
768 #if USE_X11
769     case GST_VAAPI_DISPLAY_TYPE_X11:
770         success = gst_vaapisink_put_surface(sink, surface, flags);
771         break;
772 #endif
773 #if USE_WAYLAND
774     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
775         success = gst_vaapisink_put_surface(sink, surface, flags);
776         break;
777 #endif
778     default:
779         GST_ERROR("unsupported display type %d", sink->display_type);
780         success = FALSE;
781         break;
782     }
783     if (!success)
784         return GST_FLOW_UNEXPECTED;
785
786     /* Retain VA surface until the next one is displayed */
787     if (sink->use_overlay)
788         gst_buffer_replace(&sink->video_buffer, buffer);
789     return GST_FLOW_OK;
790 }
791
792 static gboolean
793 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
794 {
795     GstVaapiSink *sink = GST_VAAPISINK(base_sink);
796     GST_DEBUG ("sharing display %p", sink->display);
797     return gst_vaapi_reply_to_query (query, sink->display);
798 }
799
800 static void
801 gst_vaapisink_finalize(GObject *object)
802 {
803     gst_vaapisink_destroy(GST_VAAPISINK(object));
804
805     G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
806 }
807
808 static void
809 gst_vaapisink_set_property(
810     GObject      *object,
811     guint         prop_id,
812     const GValue *value,
813     GParamSpec   *pspec
814 )
815 {
816     GstVaapiSink * const sink = GST_VAAPISINK(object);
817
818     switch (prop_id) {
819     case PROP_DISPLAY_TYPE:
820         sink->display_type = g_value_get_enum(value);
821         break;
822     case PROP_FULLSCREEN:
823         sink->fullscreen = g_value_get_boolean(value);
824         break;
825     case PROP_SYNCHRONOUS:
826         sink->synchronous = g_value_get_boolean(value);
827         break;
828     case PROP_USE_REFLECTION:
829         sink->use_reflection = g_value_get_boolean(value);
830         break;
831     default:
832         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
833         break;
834     }
835 }
836
837 static void
838 gst_vaapisink_get_property(
839     GObject    *object,
840     guint       prop_id,
841     GValue     *value,
842     GParamSpec *pspec
843 )
844 {
845     GstVaapiSink * const sink = GST_VAAPISINK(object);
846
847     switch (prop_id) {
848     case PROP_DISPLAY_TYPE:
849         g_value_set_enum(value, sink->display_type);
850         break;
851     case PROP_FULLSCREEN:
852         g_value_set_boolean(value, sink->fullscreen);
853         break;
854     case PROP_SYNCHRONOUS:
855         g_value_set_boolean(value, sink->synchronous);
856         break;
857     case PROP_USE_REFLECTION:
858         g_value_set_boolean(value, sink->use_reflection);
859         break;
860     default:
861         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
862         break;
863     }
864 }
865
866 static void
867 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
868 {
869     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
870     GstElementClass * const  element_class  = GST_ELEMENT_CLASS(klass);
871     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
872     GstPadTemplate *pad_template;
873
874     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
875                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
876
877     object_class->finalize       = gst_vaapisink_finalize;
878     object_class->set_property   = gst_vaapisink_set_property;
879     object_class->get_property   = gst_vaapisink_get_property;
880
881     basesink_class->start        = gst_vaapisink_start;
882     basesink_class->stop         = gst_vaapisink_stop;
883     basesink_class->set_caps     = gst_vaapisink_set_caps;
884     basesink_class->preroll      = gst_vaapisink_show_frame;
885     basesink_class->render       = gst_vaapisink_show_frame;
886     basesink_class->query        = gst_vaapisink_query;
887
888     gst_element_class_set_details_simple(
889         element_class,
890         gst_vaapisink_details.longname,
891         gst_vaapisink_details.klass,
892         gst_vaapisink_details.description,
893         gst_vaapisink_details.author
894     );
895
896     pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
897     gst_element_class_add_pad_template(element_class, pad_template);
898     gst_object_unref(pad_template);
899
900     g_object_class_install_property
901         (object_class,
902          PROP_DISPLAY_TYPE,
903          g_param_spec_enum("display",
904                            "display type",
905                            "display type to use",
906                            GST_VAAPI_TYPE_DISPLAY_TYPE,
907                            GST_VAAPI_DISPLAY_TYPE_ANY,
908                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
909
910 #if USE_GLX
911     g_object_class_install_property
912         (object_class,
913          PROP_USE_REFLECTION,
914          g_param_spec_boolean("use-reflection",
915                               "Reflection effect",
916                               "Enables OpenGL reflection effect",
917                               FALSE,
918                               G_PARAM_READWRITE));
919 #endif
920
921     g_object_class_install_property
922         (object_class,
923          PROP_FULLSCREEN,
924          g_param_spec_boolean("fullscreen",
925                               "Fullscreen",
926                               "Requests window in fullscreen state",
927                               FALSE,
928                               G_PARAM_READWRITE));
929
930     /**
931      * GstVaapiSink:synchronous:
932      *
933      * When enabled, runs the X display in synchronous mode. Note that
934      * this is used only for debugging.
935      */
936     g_object_class_install_property
937         (object_class,
938          PROP_SYNCHRONOUS,
939          g_param_spec_boolean("synchronous",
940                               "Synchronous mode",
941                               "Toggles X display synchronous mode",
942                               FALSE,
943                               G_PARAM_READWRITE));
944 }
945
946 static void
947 gst_vaapisink_init(GstVaapiSink *sink)
948 {
949     sink->caps           = NULL;
950     sink->display        = NULL;
951     sink->window         = NULL;
952     sink->window_width   = 0;
953     sink->window_height  = 0;
954     sink->texture        = NULL;
955     sink->video_buffer   = NULL;
956     sink->video_width    = 0;
957     sink->video_height   = 0;
958     sink->video_par_n    = 1;
959     sink->video_par_d    = 1;
960     sink->foreign_window = FALSE;
961     sink->fullscreen     = FALSE;
962     sink->synchronous    = FALSE;
963     sink->display_type   = DEFAULT_DISPLAY_TYPE;
964     sink->use_reflection = FALSE;
965     sink->use_overlay    = FALSE;
966 }