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