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