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