vaapisink: listen to window size changes on X11.
[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  *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6  *  Copyright (C) 2011-2014 Intel Corporation
7  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public License
11  *  as published by the Free Software Foundation; either version 2.1
12  *  of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free
21  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301 USA
23  */
24
25 /**
26  * SECTION:gstvaapisink
27  * @short_description: A VA-API based videosink
28  *
29  * vaapisink renders video frames to a drawable (X #Window) on a local
30  * display using the Video Acceleration (VA) API. The element will
31  * create its own internal window and render into it.
32  */
33
34 #include "gst/vaapi/sysdeps.h"
35 #include <gst/gst.h>
36 #include <gst/video/video.h>
37
38 #include <gst/vaapi/gstvaapivalue.h>
39 #if USE_DRM
40 # include <gst/vaapi/gstvaapidisplay_drm.h>
41 #endif
42 #if USE_X11
43 # include <gst/vaapi/gstvaapidisplay_x11.h>
44 # include <gst/vaapi/gstvaapiwindow_x11.h>
45 #endif
46 #if USE_GLX
47 # include <gst/vaapi/gstvaapidisplay_glx.h>
48 # include <gst/vaapi/gstvaapiwindow_glx.h>
49 #endif
50 #if USE_WAYLAND
51 # include <gst/vaapi/gstvaapidisplay_wayland.h>
52 # include <gst/vaapi/gstvaapiwindow_wayland.h>
53 #endif
54
55 /* Supported interfaces */
56 #if GST_CHECK_VERSION(1,0,0)
57 # include <gst/video/videooverlay.h>
58 #else
59 # include <gst/interfaces/xoverlay.h>
60
61 # define GST_TYPE_VIDEO_OVERLAY         GST_TYPE_X_OVERLAY
62 # define GST_VIDEO_OVERLAY              GST_X_OVERLAY
63 # define GstVideoOverlay                GstXOverlay
64 # define GstVideoOverlayInterface       GstXOverlayClass
65
66 # define gst_video_overlay_prepare_window_handle(sink) \
67     gst_x_overlay_prepare_xwindow_id(sink)
68 # define gst_video_overlay_got_window_handle(sink, window_handle) \
69     gst_x_overlay_got_window_handle(sink, window_handle)
70 #endif
71
72 #include "gstvaapisink.h"
73 #include "gstvaapipluginutil.h"
74 #include "gstvaapivideometa.h"
75 #if GST_CHECK_VERSION(1,0,0)
76 #include "gstvaapivideobufferpool.h"
77 #include "gstvaapivideomemory.h"
78 #endif
79
80 #define GST_PLUGIN_NAME "vaapisink"
81 #define GST_PLUGIN_DESC "A VA-API based videosink"
82
83 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
84 #define GST_CAT_DEFAULT gst_debug_vaapisink
85
86 /* Default template */
87 static const char gst_vaapisink_sink_caps_str[] =
88 #if GST_CHECK_VERSION(1,1,0)
89     GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
90         GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ";"
91     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL);
92 #else
93 #if GST_CHECK_VERSION(1,0,0)
94     GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; "
95 #else
96     "video/x-raw-yuv, "
97     "width  = (int) [ 1, MAX ], "
98     "height = (int) [ 1, MAX ]; "
99 #endif
100     GST_VAAPI_SURFACE_CAPS;
101 #endif
102
103 static GstStaticPadTemplate gst_vaapisink_sink_factory =
104     GST_STATIC_PAD_TEMPLATE(
105         "sink",
106         GST_PAD_SINK,
107         GST_PAD_ALWAYS,
108         GST_STATIC_CAPS(gst_vaapisink_sink_caps_str));
109
110 static gboolean
111 gst_vaapisink_has_interface(GstVaapiPluginBase *plugin, GType type)
112 {
113     return type == GST_TYPE_VIDEO_OVERLAY;
114 }
115
116 static void
117 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface);
118
119 G_DEFINE_TYPE_WITH_CODE(
120     GstVaapiSink,
121     gst_vaapisink,
122     GST_TYPE_VIDEO_SINK,
123     GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
124     G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_OVERLAY,
125                           gst_vaapisink_video_overlay_iface_init))
126
127 enum {
128     PROP_0,
129
130     PROP_DISPLAY_TYPE,
131     PROP_DISPLAY_NAME,
132     PROP_FULLSCREEN,
133     PROP_SYNCHRONOUS,
134     PROP_USE_GLX,
135     PROP_USE_REFLECTION,
136     PROP_ROTATION,
137     PROP_FORCE_ASPECT_RATIO,
138     PROP_VIEW_ID,
139 };
140
141 #define DEFAULT_DISPLAY_TYPE            GST_VAAPI_DISPLAY_TYPE_ANY
142 #define DEFAULT_ROTATION                GST_VAAPI_ROTATION_0
143
144 static inline gboolean
145 gst_vaapisink_ensure_display(GstVaapiSink *sink);
146
147 /* GstVideoOverlay interface */
148
149 static void
150 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay);
151
152 #if USE_X11
153 static gboolean
154 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
155 #endif
156
157 static void
158 gst_vaapisink_set_event_handling(GstVideoOverlay *overlay, gboolean handle_events);
159
160 static GstFlowReturn
161 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
162
163 static gboolean
164 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height);
165
166 static void
167 gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
168     guintptr window)
169 {
170     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
171     GstVaapiDisplayType display_type;
172
173     if (!gst_vaapisink_ensure_display(sink))
174         return;
175     display_type = GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
176
177     /* Disable GLX rendering when vaapisink is using a foreign X
178        window. It's pretty much useless */
179     if (display_type == GST_VAAPI_DISPLAY_TYPE_GLX) {
180         display_type = GST_VAAPI_DISPLAY_TYPE_X11;
181         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
182             display_type);
183     }
184
185     sink->foreign_window = TRUE;
186
187     switch (display_type) {
188 #if USE_X11
189     case GST_VAAPI_DISPLAY_TYPE_X11:
190         gst_vaapisink_ensure_window_xid(sink, window);
191         gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), sink->handle_events);
192         break;
193 #endif
194     default:
195         break;
196     }
197 }
198
199 static void
200 gst_vaapisink_video_overlay_set_render_rectangle(
201     GstVideoOverlay *overlay,
202     gint         x,
203     gint         y,
204     gint         width,
205     gint         height
206 )
207 {
208     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
209     GstVaapiRectangle * const display_rect = &sink->display_rect;
210
211     display_rect->x      = x;
212     display_rect->y      = y;
213     display_rect->width  = width;
214     display_rect->height = height;
215     
216     GST_DEBUG("render rect (%d,%d):%ux%u",
217               display_rect->x, display_rect->y,
218               display_rect->width, display_rect->height);
219 }
220
221 static gboolean
222 gst_vaapisink_reconfigure_window(GstVaapiSink * sink)
223 {
224     guint win_width, win_height;
225
226     gst_vaapi_window_reconfigure(sink->window);
227     gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
228     if (win_width != sink->window_width || win_height != sink->window_height) {
229         if (!gst_vaapisink_ensure_render_rect(sink, win_width, win_height))
230             return FALSE;
231         GST_INFO("window was resized from %ux%u to %ux%u",
232             sink->window_width, sink->window_height, win_width, win_height);
233         sink->window_width = win_width;
234         sink->window_height = win_height;
235         return TRUE;
236     }
237     return FALSE;
238 }
239
240 #if USE_X11
241 static void
242 gst_vaapisink_event_thread_loop_x11(GstVaapiSink *sink)
243 {
244     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
245     gboolean has_events, do_expose = FALSE;
246     XEvent e;
247
248     if (sink->window) {
249         Display * const x11_dpy =
250             gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(display));
251         Window x11_win =
252             gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window));
253
254         /* Handle Expose + ConfigureNotify */
255         /* Need to lock whole loop or we corrupt the XEvent queue: */
256         for (;;) {
257             gst_vaapi_display_lock(display);
258             has_events = XCheckWindowEvent(x11_dpy, x11_win,
259                 StructureNotifyMask | ExposureMask, &e);
260             gst_vaapi_display_unlock(display);
261             if (!has_events)
262                 break;
263
264             switch (e.type) {
265             case Expose:
266                 do_expose = TRUE;
267                 break;
268             case ConfigureNotify:
269                 if (gst_vaapisink_reconfigure_window(sink))
270                     do_expose = TRUE;
271                 break;
272             default:
273                 break;
274             }
275         }
276         if (do_expose)
277             gst_vaapisink_video_overlay_expose(GST_VIDEO_OVERLAY(sink));
278         /* FIXME: handle mouse and key events */
279     }
280 }
281 #endif
282
283 static void
284 gst_vaapisink_event_thread_loop_default(GstVaapiSink *sink)
285 {
286 }
287
288 static gpointer
289 gst_vaapisink_event_thread (GstVaapiSink *sink)
290 {
291     void (*thread_loop)(GstVaapiSink *sink);
292
293     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
294 #if USE_X11
295     case GST_VAAPI_DISPLAY_TYPE_X11:
296     case GST_VAAPI_DISPLAY_TYPE_GLX:
297         thread_loop = gst_vaapisink_event_thread_loop_x11;
298         break;
299 #endif
300     default:
301         thread_loop = gst_vaapisink_event_thread_loop_default;
302         break;
303     }
304
305     GST_OBJECT_LOCK(sink);
306     while (!sink->event_thread_cancel) {
307         GST_OBJECT_UNLOCK(sink);
308         thread_loop(sink);
309         g_usleep(G_USEC_PER_SEC / 20);
310         GST_OBJECT_LOCK(sink);
311     }
312     GST_OBJECT_UNLOCK(sink);
313
314   return NULL;
315 }
316
317 static void
318 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
319 {
320     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
321
322     if (sink->video_buffer) {
323         gst_vaapisink_reconfigure_window(sink);
324         gst_vaapisink_show_frame(GST_BASE_SINK_CAST(sink), sink->video_buffer);
325     }
326 }
327
328 static void
329 gst_vaapisink_set_event_handling(GstVideoOverlay *overlay,
330     gboolean handle_events)
331 {
332     GThread *thread = NULL;
333     GstVaapiSink * const sink = GST_VAAPISINK(overlay);
334 #if USE_X11
335     GstVaapiDisplayX11 * const display =
336         GST_VAAPI_DISPLAY_X11(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
337 #endif
338
339     GST_OBJECT_LOCK(sink);
340     sink->handle_events = handle_events;
341     if (handle_events && !sink->event_thread) {
342         /* Setup our event listening thread */
343         GST_DEBUG("starting xevent thread");
344         switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
345 #if USE_X11
346         case GST_VAAPI_DISPLAY_TYPE_X11:
347         case GST_VAAPI_DISPLAY_TYPE_GLX:
348             XSelectInput(gst_vaapi_display_x11_get_display(display),
349                 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)),
350                 StructureNotifyMask | ExposureMask);
351             break;
352 #endif
353         default:
354             break;
355         }
356
357         sink->event_thread_cancel = FALSE;
358         sink->event_thread = g_thread_try_new("vaapisink-events",
359                 (GThreadFunc) gst_vaapisink_event_thread, sink, NULL);
360     }
361     else if (!handle_events && sink->event_thread) {
362         GST_DEBUG("stopping xevent thread");
363         if (sink->window) {
364             switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
365 #if USE_X11
366             case GST_VAAPI_DISPLAY_TYPE_X11:
367             case GST_VAAPI_DISPLAY_TYPE_GLX:
368                 XSelectInput(gst_vaapi_display_x11_get_display(display),
369                     gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)),
370                     0);
371                 break;
372 #endif
373             default:
374                 break;
375             }
376         }
377
378         /* grab thread and mark it as NULL */
379         thread = sink->event_thread;
380         sink->event_thread = NULL;
381         sink->event_thread_cancel = TRUE;
382     }
383     GST_OBJECT_UNLOCK(sink);
384
385     /* Wait for our event thread to finish */
386     if (thread) {
387         g_thread_join(thread);
388         GST_DEBUG("xevent thread stopped");
389     }
390 }
391
392 static void
393 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface)
394 {
395     iface->set_window_handle    = gst_vaapisink_video_overlay_set_window_handle;
396     iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle;
397     iface->expose               = gst_vaapisink_video_overlay_expose;
398     iface->handle_events        = gst_vaapisink_set_event_handling;
399 }
400
401 static void
402 gst_vaapisink_destroy(GstVaapiSink *sink)
403 {
404     gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), FALSE);
405
406     gst_buffer_replace(&sink->video_buffer, NULL);
407 #if USE_GLX
408     gst_vaapi_texture_replace(&sink->texture, NULL);
409 #endif
410     gst_caps_replace(&sink->caps, NULL);
411 }
412
413 #if USE_X11
414 /* Checks whether a ConfigureNotify event is in the queue */
415 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
416 struct _ConfigureNotifyEventPendingArgs {
417     Window      window;
418     guint       width;
419     guint       height;
420     gboolean    match;
421 };
422
423 static Bool
424 configure_notify_event_pending_cb(Display *dpy, XEvent *xev, XPointer arg)
425 {
426     ConfigureNotifyEventPendingArgs * const args =
427         (ConfigureNotifyEventPendingArgs *)arg;
428
429     if (xev->type == ConfigureNotify &&
430         xev->xconfigure.window == args->window &&
431         xev->xconfigure.width  == args->width  &&
432         xev->xconfigure.height == args->height)
433         args->match = TRUE;
434
435     /* XXX: this is a hack to traverse the whole queue because we
436        can't use XPeekIfEvent() since it could block */
437     return False;
438 }
439
440 static gboolean
441 configure_notify_event_pending(
442     GstVaapiSink *sink,
443     Window        window,
444     guint         width,
445     guint         height
446 )
447 {
448     GstVaapiDisplayX11 * const display =
449         GST_VAAPI_DISPLAY_X11(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
450     ConfigureNotifyEventPendingArgs args;
451     XEvent xev;
452
453     args.window = window;
454     args.width  = width;
455     args.height = height;
456     args.match  = FALSE;
457
458     /* XXX: don't use XPeekIfEvent() because it might block */
459     XCheckIfEvent(
460         gst_vaapi_display_x11_get_display(display),
461         &xev,
462         configure_notify_event_pending_cb, (XPointer)&args
463     );
464     return args.match;
465 }
466 #endif
467
468 static const gchar *
469 get_display_type_name(GstVaapiDisplayType display_type)
470 {
471     gpointer const klass = g_type_class_peek(GST_VAAPI_TYPE_DISPLAY_TYPE);
472     GEnumValue * const e = g_enum_get_value(klass, display_type);
473
474     if (e)
475         return e->value_name;
476     return "<unknown-type>";
477 }
478
479 static inline gboolean
480 gst_vaapisink_ensure_display(GstVaapiSink *sink)
481 {
482     return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(sink));
483 }
484
485 static void
486 gst_vaapisink_display_changed(GstVaapiPluginBase *plugin)
487 {
488     GstVaapiSink * const sink = GST_VAAPISINK(plugin);
489     GstVaapiRenderMode render_mode;
490
491     GST_INFO("created %s %p", get_display_type_name(plugin->display_type),
492              plugin->display);
493
494     sink->use_overlay =
495         gst_vaapi_display_get_render_mode(plugin->display, &render_mode) &&
496         render_mode == GST_VAAPI_RENDER_MODE_OVERLAY;
497     GST_DEBUG("use %s rendering mode",
498               sink->use_overlay ? "overlay" : "texture");
499
500     sink->use_rotation = gst_vaapi_display_has_property(plugin->display,
501         GST_VAAPI_DISPLAY_PROP_ROTATION);
502 }
503
504 static gboolean
505 gst_vaapisink_ensure_uploader(GstVaapiSink *sink)
506 {
507     if (!gst_vaapisink_ensure_display(sink))
508         return FALSE;
509     if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(sink)))
510         return FALSE;
511     return TRUE;
512 }
513
514 static gboolean
515 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
516 {
517     GstVaapiRectangle * const display_rect = &sink->display_rect;
518     guint num, den, display_par_n, display_par_d;
519     gboolean success;
520
521     /* Return success if caps are not set yet */
522     if (!sink->caps)
523         return TRUE;
524
525     if (!sink->keep_aspect) {
526         display_rect->width = width;
527         display_rect->height = height;
528         display_rect->x = 0;
529         display_rect->y = 0;
530
531         GST_DEBUG("force-aspect-ratio is false; distorting while scaling video");
532         GST_DEBUG("render rect (%d,%d):%ux%u",
533                   display_rect->x, display_rect->y,
534                   display_rect->width, display_rect->height);
535         return TRUE;
536     }
537
538     GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
539
540     gst_vaapi_display_get_pixel_aspect_ratio(
541         GST_VAAPI_PLUGIN_BASE_DISPLAY(sink),
542         &display_par_n, &display_par_d
543     );
544     GST_DEBUG("display pixel-aspect-ratio %d/%d",
545               display_par_n, display_par_d);
546
547     success = gst_video_calculate_display_ratio(
548         &num, &den,
549         sink->video_width, sink->video_height,
550         sink->video_par_n, sink->video_par_d,
551         display_par_n, display_par_d
552     );
553     if (!success)
554         return FALSE;
555     GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
556               sink->video_width, sink->video_height, num, den);
557
558     display_rect->width = gst_util_uint64_scale_int(height, num, den);
559     if (display_rect->width <= width) {
560         GST_DEBUG("keeping window height");
561         display_rect->height = height;
562     }
563     else {
564         GST_DEBUG("keeping window width");
565         display_rect->width  = width;
566         display_rect->height =
567             gst_util_uint64_scale_int(width, den, num);
568     }
569     GST_DEBUG("scaling video to %ux%u", display_rect->width, display_rect->height);
570
571     g_assert(display_rect->width  <= width);
572     g_assert(display_rect->height <= height);
573
574     display_rect->x = (width  - display_rect->width)  / 2;
575     display_rect->y = (height - display_rect->height) / 2;
576
577     GST_DEBUG("render rect (%d,%d):%ux%u",
578               display_rect->x, display_rect->y,
579               display_rect->width, display_rect->height);
580     return TRUE;
581 }
582
583 static void
584 gst_vaapisink_ensure_window_size(GstVaapiSink *sink, guint *pwidth, guint *pheight)
585 {
586     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
587     GstVideoRectangle src_rect, dst_rect, out_rect;
588     guint num, den, display_width, display_height, display_par_n, display_par_d;
589     gboolean success, scale;
590
591     if (sink->foreign_window) {
592         *pwidth  = sink->window_width;
593         *pheight = sink->window_height;
594         return;
595     }
596
597     gst_vaapi_display_get_size(display, &display_width, &display_height);
598     if (sink->fullscreen) {
599         *pwidth  = display_width;
600         *pheight = display_height;
601         return;
602     }
603
604     gst_vaapi_display_get_pixel_aspect_ratio(
605         display,
606         &display_par_n, &display_par_d
607     );
608
609     success = gst_video_calculate_display_ratio(
610         &num, &den,
611         sink->video_width, sink->video_height,
612         sink->video_par_n, sink->video_par_d,
613         display_par_n, display_par_d
614     );
615     if (!success) {
616         num = sink->video_par_n;
617         den = sink->video_par_d;
618     }
619
620     src_rect.x = 0;
621     src_rect.y = 0;
622     src_rect.w = gst_util_uint64_scale_int(sink->video_height, num, den);
623     src_rect.h = sink->video_height;
624     dst_rect.x = 0;
625     dst_rect.y = 0;
626     dst_rect.w = display_width;
627     dst_rect.h = display_height;
628     scale      = (src_rect.w > dst_rect.w || src_rect.h > dst_rect.h);
629     gst_video_sink_center_rect(src_rect, dst_rect, &out_rect, scale);
630     *pwidth    = out_rect.w;
631     *pheight   = out_rect.h;
632 }
633
634 static inline gboolean
635 gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
636 {
637     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
638
639     if (!sink->window) {
640         const GstVaapiDisplayType display_type =
641             GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
642         switch (display_type) {
643 #if USE_GLX
644         case GST_VAAPI_DISPLAY_TYPE_GLX:
645             sink->window = gst_vaapi_window_glx_new(display, width, height);
646             goto notify_video_overlay_interface;
647 #endif
648 #if USE_X11
649         case GST_VAAPI_DISPLAY_TYPE_X11:
650             sink->window = gst_vaapi_window_x11_new(display, width, height);
651         notify_video_overlay_interface:
652             if (!sink->window)
653                 break;
654             gst_video_overlay_got_window_handle(
655                 GST_VIDEO_OVERLAY(sink),
656                 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window))
657             );
658             break;
659 #endif
660 #if USE_WAYLAND
661         case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
662             sink->window = gst_vaapi_window_wayland_new(display, width, height);
663             break;
664 #endif
665         default:
666             GST_ERROR("unsupported display type %d", display_type);
667             return FALSE;
668         }
669     }
670     return sink->window != NULL;
671 }
672
673 #if USE_X11
674 static gboolean
675 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
676 {
677     GstVaapiDisplay *display;
678     Window rootwin;
679     unsigned int width, height, border_width, depth;
680     int x, y;
681     XID xid = window_id;
682
683     if (!gst_vaapisink_ensure_display(sink))
684         return FALSE;
685     display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
686
687     gst_vaapi_display_lock(display);
688     XGetGeometry(
689         gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(display)),
690         xid,
691         &rootwin,
692         &x, &y, &width, &height, &border_width, &depth
693     );
694     gst_vaapi_display_unlock(display);
695
696     if ((width != sink->window_width || height != sink->window_height) &&
697         !configure_notify_event_pending(sink, xid, width, height)) {
698         if (!gst_vaapisink_ensure_render_rect(sink, width, height))
699             return FALSE;
700         sink->window_width  = width;
701         sink->window_height = height;
702     }
703
704     if (sink->window &&
705         gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
706         return TRUE;
707
708     gst_vaapi_window_replace(&sink->window, NULL);
709
710     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
711 #if USE_GLX
712     case GST_VAAPI_DISPLAY_TYPE_GLX:
713         sink->window = gst_vaapi_window_glx_new_with_xid(display, xid);
714         break;
715 #endif
716     case GST_VAAPI_DISPLAY_TYPE_X11:
717         sink->window = gst_vaapi_window_x11_new_with_xid(display, xid);
718         break;
719     default:
720         GST_ERROR("unsupported display type %d",
721                   GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
722         return FALSE;
723     }
724     return sink->window != NULL;
725 }
726 #endif
727
728 static gboolean
729 gst_vaapisink_ensure_rotation(GstVaapiSink *sink, gboolean recalc_display_rect)
730 {
731     GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
732     gboolean success = FALSE;
733
734     g_return_val_if_fail(display, FALSE);
735
736     if (sink->rotation == sink->rotation_req)
737         return TRUE;
738
739     if (!sink->use_rotation) {
740         GST_WARNING("VA display does not support rotation");
741         goto end;
742     }
743
744     gst_vaapi_display_lock(display);
745     success = gst_vaapi_display_set_rotation(display, sink->rotation_req);
746     gst_vaapi_display_unlock(display);
747     if (!success) {
748         GST_ERROR("failed to change VA display rotation mode");
749         goto end;
750     }
751
752     if (((sink->rotation + sink->rotation_req) % 180) == 90) {
753         /* Orientation changed */
754         G_PRIMITIVE_SWAP(guint, sink->video_width, sink->video_height);
755         G_PRIMITIVE_SWAP(gint, sink->video_par_n, sink->video_par_d);
756     }
757
758     if (recalc_display_rect && !sink->foreign_window)
759         gst_vaapisink_ensure_render_rect(sink, sink->window_width,
760             sink->window_height);
761     success = TRUE;
762
763 end:
764     sink->rotation = sink->rotation_req;
765     return success;
766 }
767
768 static gboolean
769 gst_vaapisink_start(GstBaseSink *base_sink)
770 {
771     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
772
773     if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(sink)))
774         return FALSE;
775     return gst_vaapisink_ensure_uploader(sink);
776 }
777
778 static gboolean
779 gst_vaapisink_stop(GstBaseSink *base_sink)
780 {
781     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
782
783     gst_buffer_replace(&sink->video_buffer, NULL);
784 #if GST_CHECK_VERSION(1,0,0)
785     g_clear_object(&sink->video_buffer_pool);
786 #endif
787     gst_vaapi_window_replace(&sink->window, NULL);
788
789     gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(sink));
790     return TRUE;
791 }
792
793 static GstCaps *
794 gst_vaapisink_get_caps_impl(GstBaseSink *base_sink)
795 {
796     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
797     GstCaps *out_caps, *yuv_caps;
798
799 #if GST_CHECK_VERSION(1,1,0)
800     out_caps = gst_static_pad_template_get_caps(&gst_vaapisink_sink_factory);
801 #else
802     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS);
803 #endif
804     if (!out_caps)
805         return NULL;
806
807     if (gst_vaapisink_ensure_uploader(sink)) {
808         yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(sink);
809         if (yuv_caps) {
810             out_caps = gst_caps_make_writable(out_caps);
811             gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
812         }
813     }
814     return out_caps;
815 }
816
817 #if GST_CHECK_VERSION(1,0,0)
818 static inline GstCaps *
819 gst_vaapisink_get_caps(GstBaseSink *base_sink, GstCaps *filter)
820 {
821     GstCaps *caps, *out_caps;
822
823     caps = gst_vaapisink_get_caps_impl(base_sink);
824     if (caps && filter) {
825         out_caps = gst_caps_intersect_full(caps, filter,
826             GST_CAPS_INTERSECT_FIRST);
827         gst_caps_unref(caps);
828     }
829     else
830         out_caps = caps;
831     return out_caps;
832 }
833 #else
834 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
835 #endif
836
837 static void
838 update_colorimetry(GstVaapiSink *sink, GstVideoColorimetry *cinfo)
839 {
840 #if GST_CHECK_VERSION(1,0,0)
841     if (gst_video_colorimetry_matches(cinfo,
842             GST_VIDEO_COLORIMETRY_BT601))
843         sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_601;
844     else if (gst_video_colorimetry_matches(cinfo,
845             GST_VIDEO_COLORIMETRY_BT709))
846         sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_709;
847     else if (gst_video_colorimetry_matches(cinfo,
848             GST_VIDEO_COLORIMETRY_SMPTE240M))
849         sink->color_standard = GST_VAAPI_COLOR_STANDARD_SMPTE_240M;
850     else
851         sink->color_standard = 0;
852
853     GST_DEBUG("colorimetry %s", gst_video_colorimetry_to_string(cinfo));
854 #endif
855 }
856
857 static gboolean
858 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
859 {
860     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
861     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
862     GstVideoInfo * const vip = GST_VAAPI_PLUGIN_BASE_SINK_PAD_INFO(sink);
863     GstVaapiDisplay *display;
864     guint win_width, win_height;
865
866     if (!gst_vaapisink_ensure_display(sink))
867         return FALSE;
868     display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
869
870 #if USE_DRM
871     if (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink) == GST_VAAPI_DISPLAY_TYPE_DRM)
872         return TRUE;
873 #endif
874
875     if (!gst_vaapi_plugin_base_set_caps(plugin, caps, NULL))
876         return FALSE;
877
878     sink->video_width   = GST_VIDEO_INFO_WIDTH(vip);
879     sink->video_height  = GST_VIDEO_INFO_HEIGHT(vip);
880     sink->video_par_n   = GST_VIDEO_INFO_PAR_N(vip);
881     sink->video_par_d   = GST_VIDEO_INFO_PAR_D(vip);
882     GST_DEBUG("video pixel-aspect-ratio %d/%d",
883               sink->video_par_n, sink->video_par_d);
884
885     update_colorimetry(sink, &vip->colorimetry);
886     gst_caps_replace(&sink->caps, caps);
887
888     gst_vaapisink_ensure_rotation(sink, FALSE);
889
890     gst_vaapisink_ensure_window_size(sink, &win_width, &win_height);
891     if (sink->window) {
892         if (!sink->foreign_window || sink->fullscreen)
893             gst_vaapi_window_set_size(sink->window, win_width, win_height);
894     }
895     else {
896         gst_vaapi_display_lock(display);
897         gst_video_overlay_prepare_window_handle(GST_VIDEO_OVERLAY(sink));
898         gst_vaapi_display_unlock(display);
899         if (sink->window)
900             return TRUE;
901         if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
902             return FALSE;
903         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
904         gst_vaapi_window_show(sink->window);
905         gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
906         gst_vaapisink_set_event_handling(GST_VIDEO_OVERLAY(sink), sink->handle_events);
907     }
908     sink->window_width  = win_width;
909     sink->window_height = win_height;
910     GST_DEBUG("window size %ux%u", win_width, win_height);
911
912     return gst_vaapisink_ensure_render_rect(sink, win_width, win_height);
913 }
914
915 #if USE_GLX
916 static void
917 render_background(GstVaapiSink *sink)
918 {
919     /* Original code from Mirco Muller (MacSlow):
920        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
921     GLfloat fStartX = 0.0f;
922     GLfloat fStartY = 0.0f;
923     GLfloat fWidth  = (GLfloat)sink->window_width;
924     GLfloat fHeight = (GLfloat)sink->window_height;
925
926     glClear(GL_COLOR_BUFFER_BIT);
927     glBegin(GL_QUADS);
928     {
929         /* top third, darker grey to white */
930         glColor3f(0.85f, 0.85f, 0.85f);
931         glVertex3f(fStartX, fStartY, 0.0f);
932         glColor3f(0.85f, 0.85f, 0.85f);
933         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
934         glColor3f(1.0f, 1.0f, 1.0f);
935         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
936         glColor3f(1.0f, 1.0f, 1.0f);
937         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
938
939         /* middle third, just plain white */
940         glColor3f(1.0f, 1.0f, 1.0f);
941         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
942         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
943         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
944         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
945
946         /* bottom third, white to lighter grey */
947         glColor3f(1.0f, 1.0f, 1.0f);
948         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
949         glColor3f(1.0f, 1.0f, 1.0f);
950         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
951         glColor3f(0.62f, 0.66f, 0.69f);
952         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
953         glColor3f(0.62f, 0.66f, 0.69f);
954         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
955     }
956     glEnd();
957 }
958
959 static void
960 render_frame(GstVaapiSink *sink, GstVaapiSurface *surface,
961     const GstVaapiRectangle *surface_rect)
962 {
963     const guint x1 = sink->display_rect.x;
964     const guint x2 = sink->display_rect.x + sink->display_rect.width;
965     const guint y1 = sink->display_rect.y;
966     const guint y2 = sink->display_rect.y + sink->display_rect.height;
967     gfloat tx1, tx2, ty1, ty2;
968     guint width, height;
969
970     if (surface_rect) {
971         gst_vaapi_surface_get_size(surface, &width, &height);
972         tx1 = (gfloat)surface_rect->x / width;
973         ty1 = (gfloat)surface_rect->y / height;
974         tx2 = (gfloat)(surface_rect->x + surface_rect->width) / width;
975         ty2 = (gfloat)(surface_rect->y + surface_rect->height) / height;
976     }
977     else {
978         tx1 = 0.0f;
979         ty1 = 0.0f;
980         tx2 = 1.0f;
981         ty2 = 1.0f;
982     }
983
984     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
985     glBegin(GL_QUADS);
986     {
987         glTexCoord2f(tx1, ty1); glVertex2i(x1, y1);
988         glTexCoord2f(tx1, ty2); glVertex2i(x1, y2);
989         glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
990         glTexCoord2f(tx2, ty1); glVertex2i(x2, y1);
991     }
992     glEnd();
993 }
994
995 static void
996 render_reflection(GstVaapiSink *sink)
997 {
998     const guint x1 = sink->display_rect.x;
999     const guint x2 = sink->display_rect.x + sink->display_rect.width;
1000     const guint y1 = sink->display_rect.y;
1001     const guint rh = sink->display_rect.height / 5;
1002     GLfloat     ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
1003
1004     glBegin(GL_QUADS);
1005     {
1006         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
1007         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
1008         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
1009
1010         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
1011         glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
1012         glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
1013     }
1014     glEnd();
1015 }
1016
1017 static gboolean
1018 gst_vaapisink_ensure_texture(GstVaapiSink *sink, GstVaapiSurface *surface)
1019 {
1020     GstVaapiDisplay * display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
1021     GstVideoRectangle tex_rect, dis_rect, out_rect;
1022     guint width, height;
1023
1024     if (sink->texture)
1025         return TRUE;
1026
1027     gst_vaapi_surface_get_size(surface, &width, &height);
1028     tex_rect.x = 0;
1029     tex_rect.y = 0;
1030     tex_rect.w = width;
1031     tex_rect.h = height;
1032
1033     gst_vaapi_display_get_size(display, &width, &height);
1034     dis_rect.x = 0;
1035     dis_rect.y = 0;
1036     dis_rect.w = width;
1037     dis_rect.h = height;
1038
1039     gst_video_sink_center_rect(tex_rect, dis_rect, &out_rect, TRUE);
1040
1041     /* XXX: use surface size for now since some VA drivers have issues
1042        with downscaling to the provided texture size. i.e. we should be
1043        using the resulting out_rect size, which preserves the aspect
1044        ratio of the surface */
1045     width = tex_rect.w;
1046     height = tex_rect.h;
1047     GST_INFO("texture size %ux%u", width, height);
1048
1049     sink->texture = gst_vaapi_texture_new(display,
1050         GL_TEXTURE_2D, GL_BGRA, width, height);
1051     return sink->texture != NULL;
1052 }
1053
1054 static gboolean
1055 gst_vaapisink_show_frame_glx(
1056     GstVaapiSink               *sink,
1057     GstVaapiSurface            *surface,
1058     const GstVaapiRectangle    *surface_rect,
1059     guint                       flags
1060 )
1061 {
1062     GstVaapiWindowGLX *window;
1063     GLenum target;
1064     GLuint texture;
1065
1066     if (!sink->window)
1067         return FALSE;
1068     window = GST_VAAPI_WINDOW_GLX(sink->window);
1069
1070     gst_vaapi_window_glx_make_current(window);
1071     if (!gst_vaapisink_ensure_texture(sink, surface))
1072         goto error_create_texture;
1073     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
1074         goto error_transfer_surface;
1075
1076     target  = gst_vaapi_texture_get_target(sink->texture);
1077     texture = gst_vaapi_texture_get_id(sink->texture);
1078     if (target != GL_TEXTURE_2D || !texture)
1079         return FALSE;
1080
1081     if (sink->use_reflection)
1082         render_background(sink);
1083
1084     glEnable(target);
1085     glBindTexture(target, texture);
1086     {
1087         if (sink->use_reflection) {
1088             glPushMatrix();
1089             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
1090             glTranslatef(50.0f, 0.0f, 0.0f);
1091         }
1092         render_frame(sink, surface, surface_rect);
1093         if (sink->use_reflection) {
1094             glPushMatrix();
1095             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
1096             render_reflection(sink);
1097             glPopMatrix();
1098             glPopMatrix();
1099         }
1100     }
1101     glBindTexture(target, 0);
1102     glDisable(target);
1103     gst_vaapi_window_glx_swap_buffers(window);
1104     return TRUE;
1105
1106     /* ERRORS */
1107 error_create_texture:
1108     {
1109         GST_DEBUG("could not create VA/GLX texture");
1110         return FALSE;
1111     }
1112 error_transfer_surface:
1113     {
1114         GST_DEBUG("could not transfer VA surface to texture");
1115         return FALSE;
1116     }
1117 }
1118 #endif
1119
1120 static inline gboolean
1121 gst_vaapisink_put_surface(
1122     GstVaapiSink               *sink,
1123     GstVaapiSurface            *surface,
1124     const GstVaapiRectangle    *surface_rect,
1125     guint                       flags
1126 )
1127 {
1128     if (!sink->window)
1129         return FALSE;
1130
1131     if (!gst_vaapi_window_put_surface(sink->window, surface,
1132                 surface_rect, &sink->display_rect, flags)) {
1133         GST_DEBUG("could not render VA surface");
1134         return FALSE;
1135     }
1136     return TRUE;
1137 }
1138
1139 static GstFlowReturn
1140 gst_vaapisink_show_frame_unlocked(GstVaapiSink *sink, GstBuffer *src_buffer)
1141 {
1142     GstVaapiVideoMeta *meta;
1143     GstVaapiSurfaceProxy *proxy;
1144     GstVaapiSurface *surface;
1145     GstBuffer *buffer;
1146     guint flags;
1147     gboolean success;
1148     GstVaapiRectangle *surface_rect = NULL;
1149 #if GST_CHECK_VERSION(1,0,0)
1150     GstVaapiRectangle tmp_rect;
1151 #endif
1152     GstFlowReturn ret;
1153     gint32 view_id;
1154
1155 #if GST_CHECK_VERSION(1,0,0)
1156     GstVideoCropMeta * const crop_meta =
1157         gst_buffer_get_video_crop_meta(src_buffer);
1158     if (crop_meta) {
1159         surface_rect = &tmp_rect;
1160         surface_rect->x = crop_meta->x;
1161         surface_rect->y = crop_meta->y;
1162         surface_rect->width = crop_meta->width;
1163         surface_rect->height = crop_meta->height;
1164     }
1165 #endif
1166
1167     ret = gst_vaapi_plugin_base_get_input_buffer(GST_VAAPI_PLUGIN_BASE(sink),
1168         src_buffer, &buffer);
1169     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_SUPPORTED)
1170         return ret;
1171
1172     meta = gst_buffer_get_vaapi_video_meta(buffer);
1173     GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE(sink,
1174         gst_vaapi_video_meta_get_display(meta));
1175
1176     gst_vaapisink_ensure_rotation(sink, TRUE);
1177
1178     proxy = gst_vaapi_video_meta_get_surface_proxy(meta);
1179     if (!proxy)
1180         goto error;
1181
1182     /* Valide view component to display */
1183     view_id = GST_VAAPI_SURFACE_PROXY_VIEW_ID(proxy);
1184     if (G_UNLIKELY(sink->view_id == -1))
1185         sink->view_id = view_id;
1186     else if (sink->view_id != view_id) {
1187         gst_buffer_unref(buffer);
1188         return GST_FLOW_OK;
1189     }
1190
1191     surface = gst_vaapi_video_meta_get_surface(meta);
1192     if (!surface)
1193         goto error;
1194
1195     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
1196               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
1197
1198     if (!surface_rect)
1199         surface_rect = (GstVaapiRectangle *)
1200             gst_vaapi_video_meta_get_render_rect(meta);
1201
1202     if (surface_rect)
1203         GST_DEBUG("render rect (%d,%d), size %ux%u",
1204                   surface_rect->x, surface_rect->y,
1205                   surface_rect->width, surface_rect->height);
1206
1207     flags = gst_vaapi_video_meta_get_render_flags(meta);
1208
1209     /* Append default color standard obtained from caps if none was
1210        available on a per-buffer basis */
1211     if (!(flags & GST_VAAPI_COLOR_STANDARD_MASK))
1212         flags |= sink->color_standard;
1213
1214     if (!gst_vaapi_apply_composition(surface, src_buffer))
1215         GST_WARNING("could not update subtitles");
1216
1217     switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
1218 #if USE_DRM
1219     case GST_VAAPI_DISPLAY_TYPE_DRM:
1220         success = TRUE;
1221         break;
1222 #endif
1223 #if USE_GLX
1224     case GST_VAAPI_DISPLAY_TYPE_GLX:
1225         if (!sink->use_glx)
1226             goto put_surface_x11;
1227         success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
1228             flags);
1229         break;
1230 #endif
1231 #if USE_X11
1232     case GST_VAAPI_DISPLAY_TYPE_X11:
1233     put_surface_x11:
1234         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1235         break;
1236 #endif
1237 #if USE_WAYLAND
1238     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1239         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1240         break;
1241 #endif
1242     default:
1243         GST_ERROR("unsupported display type %d",
1244                   GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1245         success = FALSE;
1246         break;
1247     }
1248     if (!success)
1249         goto error;
1250
1251     /* Retain VA surface until the next one is displayed */
1252     gst_buffer_replace(&sink->video_buffer, buffer);
1253     gst_buffer_unref(buffer);
1254     return GST_FLOW_OK;
1255
1256 error:
1257     gst_buffer_unref(buffer);
1258     return GST_FLOW_EOS;
1259 }
1260
1261 static GstFlowReturn
1262 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
1263 {
1264     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1265     GstFlowReturn ret;
1266
1267    /* At least we need at least to protect the _set_subpictures_()
1268     * call to prevent a race during subpicture desctruction.
1269     * FIXME: Could use a less coarse grained lock, though: */
1270     gst_vaapi_display_lock(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1271     ret = gst_vaapisink_show_frame_unlocked(sink, src_buffer);
1272     gst_vaapi_display_unlock(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1273     return ret;
1274 }
1275
1276 #if GST_CHECK_VERSION(1,0,0)
1277 static gboolean
1278 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
1279 {
1280     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
1281
1282     if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1283         return FALSE;
1284
1285     gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1286     gst_query_add_allocation_meta(query,
1287         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1288     return TRUE;
1289 }
1290 #else
1291 static GstFlowReturn
1292 gst_vaapisink_buffer_alloc(GstBaseSink *base_sink, guint64 offset, guint size,
1293     GstCaps *caps, GstBuffer **outbuf_ptr)
1294 {
1295     return gst_vaapi_plugin_base_allocate_input_buffer(
1296         GST_VAAPI_PLUGIN_BASE(base_sink), caps, outbuf_ptr);
1297 }
1298 #endif
1299
1300 static gboolean
1301 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
1302 {
1303     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1304
1305     GST_INFO_OBJECT(sink, "query type %s", GST_QUERY_TYPE_NAME(query));
1306
1307     if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(sink))) {
1308         GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1309         return TRUE;
1310     }
1311
1312     return GST_BASE_SINK_CLASS(gst_vaapisink_parent_class)->query(base_sink,
1313         query);
1314 }
1315
1316 static void
1317 gst_vaapisink_finalize(GObject *object)
1318 {
1319     gst_vaapisink_destroy(GST_VAAPISINK(object));
1320
1321     gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(object));
1322     G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
1323 }
1324
1325 static void
1326 gst_vaapisink_set_property(
1327     GObject      *object,
1328     guint         prop_id,
1329     const GValue *value,
1330     GParamSpec   *pspec
1331 )
1332 {
1333     GstVaapiSink * const sink = GST_VAAPISINK(object);
1334
1335     switch (prop_id) {
1336     case PROP_DISPLAY_TYPE:
1337         gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
1338             g_value_get_enum(value));
1339         break;
1340     case PROP_DISPLAY_NAME:
1341         gst_vaapi_plugin_base_set_display_name(GST_VAAPI_PLUGIN_BASE(sink),
1342             g_value_get_string(value));
1343         break;
1344     case PROP_FULLSCREEN:
1345         sink->fullscreen = g_value_get_boolean(value);
1346         break;
1347     case PROP_SYNCHRONOUS:
1348         sink->synchronous = g_value_get_boolean(value);
1349         break;
1350     case PROP_VIEW_ID:
1351         sink->view_id = g_value_get_int(value);
1352         break;
1353     case PROP_USE_GLX:
1354         sink->use_glx = g_value_get_boolean(value);
1355         break;
1356     case PROP_USE_REFLECTION:
1357         sink->use_reflection = g_value_get_boolean(value);
1358         break;
1359     case PROP_ROTATION:
1360         sink->rotation_req = g_value_get_enum(value);
1361         break;
1362     case PROP_FORCE_ASPECT_RATIO:
1363         sink->keep_aspect = g_value_get_boolean(value);
1364         break;
1365     default:
1366         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1367         break;
1368     }
1369 }
1370
1371 static void
1372 gst_vaapisink_get_property(
1373     GObject    *object,
1374     guint       prop_id,
1375     GValue     *value,
1376     GParamSpec *pspec
1377 )
1378 {
1379     GstVaapiSink * const sink = GST_VAAPISINK(object);
1380
1381     switch (prop_id) {
1382     case PROP_DISPLAY_TYPE:
1383         g_value_set_enum(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1384         break;
1385     case PROP_DISPLAY_NAME:
1386         g_value_set_string(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_NAME(sink));
1387         break;
1388     case PROP_FULLSCREEN:
1389         g_value_set_boolean(value, sink->fullscreen);
1390         break;
1391     case PROP_SYNCHRONOUS:
1392         g_value_set_boolean(value, sink->synchronous);
1393         break;
1394     case PROP_VIEW_ID:
1395         g_value_set_int(value, sink->view_id);
1396         break;
1397     case PROP_USE_GLX:
1398         g_value_set_boolean(value, sink->use_glx);
1399         break;
1400     case PROP_USE_REFLECTION:
1401         g_value_set_boolean(value, sink->use_reflection);
1402         break;
1403     case PROP_ROTATION:
1404         g_value_set_enum(value, sink->rotation);
1405         break;
1406     case PROP_FORCE_ASPECT_RATIO:
1407         g_value_set_boolean(value, sink->keep_aspect);
1408         break;
1409     default:
1410         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1411         break;
1412     }
1413 }
1414
1415 static void
1416 gst_vaapisink_set_bus(GstElement *element, GstBus *bus)
1417 {
1418     /* Make sure to allocate a VA display in the sink element first,
1419        so that upstream elements could query a display that was
1420        allocated here, and that exactly matches what the user
1421        requested through the "display" property */
1422     if (!GST_ELEMENT_BUS(element) && bus)
1423         gst_vaapisink_ensure_display(GST_VAAPISINK(element));
1424
1425     GST_ELEMENT_CLASS(gst_vaapisink_parent_class)->set_bus(element, bus);
1426 }
1427
1428 static void
1429 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
1430 {
1431     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
1432     GstElementClass * const  element_class  = GST_ELEMENT_CLASS(klass);
1433     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
1434     GstVaapiPluginBaseClass * const base_plugin_class =
1435         GST_VAAPI_PLUGIN_BASE_CLASS(klass);
1436     GstPadTemplate *pad_template;
1437
1438     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
1439                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1440
1441     gst_vaapi_plugin_base_class_init(base_plugin_class);
1442     base_plugin_class->has_interface    = gst_vaapisink_has_interface;
1443     base_plugin_class->display_changed  = gst_vaapisink_display_changed;
1444
1445     object_class->finalize       = gst_vaapisink_finalize;
1446     object_class->set_property   = gst_vaapisink_set_property;
1447     object_class->get_property   = gst_vaapisink_get_property;
1448
1449     basesink_class->start        = gst_vaapisink_start;
1450     basesink_class->stop         = gst_vaapisink_stop;
1451     basesink_class->get_caps     = gst_vaapisink_get_caps;
1452     basesink_class->set_caps     = gst_vaapisink_set_caps;
1453     basesink_class->preroll      = gst_vaapisink_show_frame;
1454     basesink_class->render       = gst_vaapisink_show_frame;
1455     basesink_class->query        = gst_vaapisink_query;
1456 #if GST_CHECK_VERSION(1,0,0)
1457     basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1458 #else
1459     basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1460 #endif
1461
1462     element_class->set_bus = gst_vaapisink_set_bus;
1463     gst_element_class_set_static_metadata(element_class,
1464         "VA-API sink",
1465         "Sink/Video",
1466         GST_PLUGIN_DESC,
1467         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1468
1469     pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
1470     gst_element_class_add_pad_template(element_class, pad_template);
1471
1472     g_object_class_install_property
1473         (object_class,
1474          PROP_DISPLAY_TYPE,
1475          g_param_spec_enum("display",
1476                            "display type",
1477                            "display type to use",
1478                            GST_VAAPI_TYPE_DISPLAY_TYPE,
1479                            GST_VAAPI_DISPLAY_TYPE_ANY,
1480                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1481
1482     g_object_class_install_property
1483         (object_class,
1484          PROP_DISPLAY_NAME,
1485          g_param_spec_string("display-name",
1486                              "display name",
1487                              "display name to use",
1488                              NULL,
1489                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1490
1491 #if USE_GLX
1492     g_object_class_install_property
1493         (object_class,
1494          PROP_USE_GLX,
1495          g_param_spec_boolean("use-glx",
1496                               "OpenGL rendering",
1497                               "Enables OpenGL rendering",
1498                               FALSE,
1499                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1500
1501     g_object_class_install_property
1502         (object_class,
1503          PROP_USE_REFLECTION,
1504          g_param_spec_boolean("use-reflection",
1505                               "Reflection effect",
1506                               "Enables OpenGL reflection effect",
1507                               FALSE,
1508                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1509 #endif
1510
1511     g_object_class_install_property
1512         (object_class,
1513          PROP_FULLSCREEN,
1514          g_param_spec_boolean("fullscreen",
1515                               "Fullscreen",
1516                               "Requests window in fullscreen state",
1517                               FALSE,
1518                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1519
1520     /**
1521      * GstVaapiSink:synchronous:
1522      *
1523      * When enabled, runs the X display in synchronous mode. Note that
1524      * this is used only for debugging.
1525      */
1526     g_object_class_install_property
1527         (object_class,
1528          PROP_SYNCHRONOUS,
1529          g_param_spec_boolean("synchronous",
1530                               "Synchronous mode",
1531                               "Toggles X display synchronous mode",
1532                               FALSE,
1533                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1534
1535     /**
1536      * GstVaapiSink:rotation:
1537      *
1538      * The VA display rotation mode, expressed as a #GstVaapiRotation.
1539      */
1540     g_object_class_install_property
1541         (object_class,
1542          PROP_ROTATION,
1543          g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
1544                            "rotation",
1545                            "The display rotation mode",
1546                            GST_VAAPI_TYPE_ROTATION,
1547                            DEFAULT_ROTATION,
1548                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1549
1550     /**
1551      * GstVaapiSink:force-aspect-ratio:
1552      *
1553      * When enabled, scaling respects video aspect ratio; when disabled, the
1554      * video is distorted to fit the window.
1555      */
1556     g_object_class_install_property
1557         (object_class,
1558          PROP_FORCE_ASPECT_RATIO,
1559          g_param_spec_boolean("force-aspect-ratio",
1560                               "Force aspect ratio",
1561                               "When enabled, scaling will respect original aspect ratio",
1562                               TRUE,
1563                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1564
1565     /**
1566      * GstVaapiSink:view-id:
1567      *
1568      * When not set to -1, the displayed frame will always be the one
1569      * that matches the view-id of the very first displayed frame. Any
1570      * other number will indicate the desire to display the supplied
1571      * view-id only.
1572      */
1573     g_object_class_install_property
1574         (object_class,
1575          PROP_VIEW_ID,
1576          g_param_spec_int("view-id",
1577                           "View ID",
1578                           "ID of the view component of interest to display",
1579                           -1, G_MAXINT32, -1,
1580                           G_PARAM_READWRITE));
1581 }
1582
1583 static void
1584 gst_vaapisink_init(GstVaapiSink *sink)
1585 {
1586     GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(sink);
1587
1588     gst_vaapi_plugin_base_init(plugin, GST_CAT_DEFAULT);
1589     gst_vaapi_plugin_base_set_display_type(plugin, DEFAULT_DISPLAY_TYPE);
1590
1591     sink->caps           = NULL;
1592     sink->window         = NULL;
1593     sink->window_width   = 0;
1594     sink->window_height  = 0;
1595     sink->texture        = NULL;
1596     sink->video_buffer   = NULL;
1597     sink->video_width    = 0;
1598     sink->video_height   = 0;
1599     sink->video_par_n    = 1;
1600     sink->video_par_d    = 1;
1601     sink->view_id        = -1;
1602     sink->handle_events  = TRUE;
1603     sink->foreign_window = FALSE;
1604     sink->fullscreen     = FALSE;
1605     sink->synchronous    = FALSE;
1606     sink->rotation       = DEFAULT_ROTATION;
1607     sink->rotation_req   = DEFAULT_ROTATION;
1608     sink->use_reflection = FALSE;
1609     sink->use_overlay    = FALSE;
1610     sink->use_rotation   = FALSE;
1611     sink->keep_aspect    = TRUE;
1612     gst_video_info_init(&sink->video_info);
1613 }