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