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