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