vaapisink: allow scaling to ignore aspect ratio.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapisink.c
1 /*
2  *  gstvaapisink.c - VA-API video sink
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-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     if (!gst_buffer_pool_set_config(pool, config))
664         goto error_pool_config;
665     sink->video_buffer_pool = pool;
666     return TRUE;
667
668     /* ERRORS */
669 error_create_pool:
670     {
671         GST_ERROR("failed to create buffer pool");
672         return FALSE;
673     }
674 error_pool_config:
675     {
676         GST_ERROR("failed to reset buffer pool config");
677         gst_object_unref(pool);
678         return FALSE;
679     }
680 #else
681     return TRUE;
682 #endif
683 }
684
685 static gboolean
686 gst_vaapisink_start(GstBaseSink *base_sink)
687 {
688     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
689
690     return gst_vaapisink_ensure_uploader(sink);
691 }
692
693 static gboolean
694 gst_vaapisink_stop(GstBaseSink *base_sink)
695 {
696     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
697
698     gst_buffer_replace(&sink->video_buffer, NULL);
699 #if GST_CHECK_VERSION(1,0,0)
700     g_clear_object(&sink->video_buffer_pool);
701 #endif
702     gst_vaapi_window_replace(&sink->window, NULL);
703     gst_vaapi_display_replace(&sink->display, NULL);
704     g_clear_object(&sink->uploader);
705
706     return TRUE;
707 }
708
709 static GstCaps *
710 gst_vaapisink_get_caps_impl(GstBaseSink *base_sink)
711 {
712     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
713     GstCaps *out_caps, *yuv_caps;
714
715     out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS);
716     if (!out_caps)
717         return NULL;
718
719     if (gst_vaapisink_ensure_uploader(sink)) {
720         yuv_caps = gst_vaapi_uploader_get_caps(sink->uploader);
721         if (yuv_caps)
722             gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
723     }
724     return out_caps;
725 }
726
727 #if GST_CHECK_VERSION(1,0,0)
728 static inline GstCaps *
729 gst_vaapisink_get_caps(GstBaseSink *base_sink, GstCaps *filter)
730 {
731     GstCaps *caps, *out_caps;
732
733     caps = gst_vaapisink_get_caps_impl(base_sink);
734     if (caps && filter) {
735         out_caps = gst_caps_intersect_full(caps, filter,
736             GST_CAPS_INTERSECT_FIRST);
737         gst_caps_unref(caps);
738     }
739     else
740         out_caps = caps;
741     return out_caps;
742 }
743 #else
744 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
745 #endif
746
747 static gboolean
748 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
749 {
750     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
751     GstVideoInfo vi;
752     guint win_width, win_height;
753
754 #if USE_DRM
755     if (sink->display_type == GST_VAAPI_DISPLAY_TYPE_DRM)
756         return TRUE;
757 #endif
758
759     if (!gst_vaapisink_ensure_video_buffer_pool(sink, caps))
760         return FALSE;
761
762     if (!gst_video_info_from_caps(&vi, caps))
763         return FALSE;
764     sink->use_video_raw = GST_VIDEO_INFO_IS_YUV(&vi);
765     sink->video_width   = GST_VIDEO_INFO_WIDTH(&vi);
766     sink->video_height  = GST_VIDEO_INFO_HEIGHT(&vi);
767     sink->video_par_n   = GST_VIDEO_INFO_PAR_N(&vi);
768     sink->video_par_d   = GST_VIDEO_INFO_PAR_D(&vi);
769     GST_DEBUG("video pixel-aspect-ratio %d/%d",
770               sink->video_par_n, sink->video_par_d);
771
772     gst_caps_replace(&sink->caps, caps);
773
774     if (!gst_vaapisink_ensure_display(sink))
775         return FALSE;
776
777     gst_vaapisink_ensure_rotation(sink, FALSE);
778
779     gst_vaapisink_ensure_window_size(sink, &win_width, &win_height);
780     if (sink->window) {
781         if (!sink->foreign_window || sink->fullscreen)
782             gst_vaapi_window_set_size(sink->window, win_width, win_height);
783     }
784     else {
785         gst_vaapi_display_lock(sink->display);
786         gst_video_overlay_prepare_window_handle(GST_VIDEO_OVERLAY(sink));
787         gst_vaapi_display_unlock(sink->display);
788         if (sink->window)
789             return TRUE;
790         if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
791             return FALSE;
792         gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
793         gst_vaapi_window_show(sink->window);
794         gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
795     }
796     sink->window_width  = win_width;
797     sink->window_height = win_height;
798     GST_DEBUG("window size %ux%u", win_width, win_height);
799
800     return gst_vaapisink_ensure_render_rect(sink, win_width, win_height);
801 }
802
803 #if USE_GLX
804 static void
805 render_background(GstVaapiSink *sink)
806 {
807     /* Original code from Mirco Muller (MacSlow):
808        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
809     GLfloat fStartX = 0.0f;
810     GLfloat fStartY = 0.0f;
811     GLfloat fWidth  = (GLfloat)sink->window_width;
812     GLfloat fHeight = (GLfloat)sink->window_height;
813
814     glClear(GL_COLOR_BUFFER_BIT);
815     glBegin(GL_QUADS);
816     {
817         /* top third, darker grey to white */
818         glColor3f(0.85f, 0.85f, 0.85f);
819         glVertex3f(fStartX, fStartY, 0.0f);
820         glColor3f(0.85f, 0.85f, 0.85f);
821         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
822         glColor3f(1.0f, 1.0f, 1.0f);
823         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
824         glColor3f(1.0f, 1.0f, 1.0f);
825         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
826
827         /* middle third, just plain white */
828         glColor3f(1.0f, 1.0f, 1.0f);
829         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
830         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
831         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
832         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
833
834         /* bottom third, white to lighter grey */
835         glColor3f(1.0f, 1.0f, 1.0f);
836         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
837         glColor3f(1.0f, 1.0f, 1.0f);
838         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
839         glColor3f(0.62f, 0.66f, 0.69f);
840         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
841         glColor3f(0.62f, 0.66f, 0.69f);
842         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
843     }
844     glEnd();
845 }
846
847 static void
848 render_frame(GstVaapiSink *sink, GstVaapiSurface *surface,
849     const GstVaapiRectangle *surface_rect)
850 {
851     const guint x1 = sink->display_rect.x;
852     const guint x2 = sink->display_rect.x + sink->display_rect.width;
853     const guint y1 = sink->display_rect.y;
854     const guint y2 = sink->display_rect.y + sink->display_rect.height;
855     gfloat tx1, tx2, ty1, ty2;
856     guint width, height;
857
858     if (surface_rect) {
859         gst_vaapi_surface_get_size(surface, &width, &height);
860         tx1 = (gfloat)surface_rect->x / width;
861         ty1 = (gfloat)surface_rect->y / height;
862         tx2 = (gfloat)(surface_rect->x + surface_rect->width) / width;
863         ty2 = (gfloat)(surface_rect->y + surface_rect->height) / height;
864     }
865     else {
866         tx1 = 0.0f;
867         ty1 = 0.0f;
868         tx2 = 1.0f;
869         ty2 = 1.0f;
870     }
871
872     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
873     glBegin(GL_QUADS);
874     {
875         glTexCoord2f(tx1, ty1); glVertex2i(x1, y1);
876         glTexCoord2f(tx1, ty2); glVertex2i(x1, y2);
877         glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
878         glTexCoord2f(tx2, ty1); glVertex2i(x2, y1);
879     }
880     glEnd();
881 }
882
883 static void
884 render_reflection(GstVaapiSink *sink)
885 {
886     const guint x1 = sink->display_rect.x;
887     const guint x2 = sink->display_rect.x + sink->display_rect.width;
888     const guint y1 = sink->display_rect.y;
889     const guint rh = sink->display_rect.height / 5;
890     GLfloat     ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
891
892     glBegin(GL_QUADS);
893     {
894         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
895         glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
896         glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
897
898         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
899         glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
900         glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
901     }
902     glEnd();
903 }
904
905 static gboolean
906 gst_vaapisink_ensure_texture(GstVaapiSink *sink, GstVaapiSurface *surface)
907 {
908     GstVideoRectangle tex_rect, dis_rect, out_rect;
909     guint width, height;
910
911     if (sink->texture)
912         return TRUE;
913
914     gst_vaapi_surface_get_size(surface, &width, &height);
915     tex_rect.x = 0;
916     tex_rect.y = 0;
917     tex_rect.w = width;
918     tex_rect.h = height;
919
920     gst_vaapi_display_get_size(sink->display, &width, &height);
921     dis_rect.x = 0;
922     dis_rect.y = 0;
923     dis_rect.w = width;
924     dis_rect.h = height;
925
926     gst_video_sink_center_rect(tex_rect, dis_rect, &out_rect, TRUE);
927
928     /* XXX: use surface size for now since some VA drivers have issues
929        with downscaling to the provided texture size. i.e. we should be
930        using the resulting out_rect size, which preserves the aspect
931        ratio of the surface */
932     width = tex_rect.w;
933     height = tex_rect.h;
934     GST_INFO("texture size %ux%u", width, height);
935
936     sink->texture = gst_vaapi_texture_new(sink->display,
937         GL_TEXTURE_2D, GL_BGRA, width, height);
938     return sink->texture != NULL;
939 }
940
941 static gboolean
942 gst_vaapisink_show_frame_glx(
943     GstVaapiSink               *sink,
944     GstVaapiSurface            *surface,
945     const GstVaapiRectangle    *surface_rect,
946     guint                       flags
947 )
948 {
949     GstVaapiWindowGLX * const window = GST_VAAPI_WINDOW_GLX(sink->window);
950     GLenum target;
951     GLuint texture;
952
953     gst_vaapi_window_glx_make_current(window);
954     if (!gst_vaapisink_ensure_texture(sink, surface))
955         goto error_create_texture;
956     if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
957         goto error_transfer_surface;
958
959     target  = gst_vaapi_texture_get_target(sink->texture);
960     texture = gst_vaapi_texture_get_id(sink->texture);
961     if (target != GL_TEXTURE_2D || !texture)
962         return FALSE;
963
964     if (sink->use_reflection)
965         render_background(sink);
966
967     glEnable(target);
968     glBindTexture(target, texture);
969     {
970         if (sink->use_reflection) {
971             glPushMatrix();
972             glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
973             glTranslatef(50.0f, 0.0f, 0.0f);
974         }
975         render_frame(sink, surface, surface_rect);
976         if (sink->use_reflection) {
977             glPushMatrix();
978             glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
979             render_reflection(sink);
980             glPopMatrix();
981             glPopMatrix();
982         }
983     }
984     glBindTexture(target, 0);
985     glDisable(target);
986     gst_vaapi_window_glx_swap_buffers(window);
987     return TRUE;
988
989     /* ERRORS */
990 error_create_texture:
991     {
992         GST_DEBUG("could not create VA/GLX texture");
993         return FALSE;
994     }
995 error_transfer_surface:
996     {
997         GST_DEBUG("could not transfer VA surface to texture");
998         return FALSE;
999     }
1000 }
1001 #endif
1002
1003 static inline gboolean
1004 gst_vaapisink_put_surface(
1005     GstVaapiSink               *sink,
1006     GstVaapiSurface            *surface,
1007     const GstVaapiRectangle    *surface_rect,
1008     guint                       flags
1009 )
1010 {
1011     if (!gst_vaapi_window_put_surface(sink->window, surface,
1012                 surface_rect, &sink->display_rect, flags)) {
1013         GST_DEBUG("could not render VA surface");
1014         return FALSE;
1015     }
1016     return TRUE;
1017 }
1018
1019 static GstFlowReturn
1020 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
1021 {
1022     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1023     GstVaapiVideoMeta *meta;
1024     GstVaapiSurface *surface;
1025     GstBuffer *buffer;
1026     guint flags;
1027     gboolean success;
1028     GstVaapiRectangle *surface_rect = NULL;
1029 #if GST_CHECK_VERSION(1,0,0)
1030     GstVaapiRectangle tmp_rect;
1031 #endif
1032
1033     meta = gst_buffer_get_vaapi_video_meta(src_buffer);
1034 #if GST_CHECK_VERSION(1,0,0)
1035     if (!meta)
1036         return GST_FLOW_EOS;
1037     buffer = gst_buffer_ref(src_buffer);
1038
1039     GstVideoCropMeta * const crop_meta =
1040         gst_buffer_get_video_crop_meta(buffer);
1041     if (crop_meta) {
1042         surface_rect = &tmp_rect;
1043         surface_rect->x = crop_meta->x;
1044         surface_rect->y = crop_meta->y;
1045         surface_rect->width = crop_meta->width;
1046         surface_rect->height = crop_meta->height;
1047     }
1048 #else
1049     if (meta)
1050         buffer = gst_buffer_ref(src_buffer);
1051     else if (sink->use_video_raw) {
1052         buffer = gst_vaapi_uploader_get_buffer(sink->uploader);
1053         if (!buffer)
1054             return GST_FLOW_EOS;
1055         meta = gst_buffer_get_vaapi_video_meta(buffer);
1056         if (!meta)
1057             goto error;
1058     }
1059     else
1060         return GST_FLOW_EOS;
1061
1062     if (sink->use_video_raw &&
1063         !gst_vaapi_uploader_process(sink->uploader, src_buffer, buffer)) {
1064         GST_WARNING("failed to process raw YUV buffer");
1065         goto error;
1066     }
1067 #endif
1068
1069     if (sink->display != gst_vaapi_video_meta_get_display(meta))
1070         gst_vaapi_display_replace(&sink->display,
1071             gst_vaapi_video_meta_get_display(meta));
1072
1073     if (!sink->window)
1074         goto error;
1075
1076     gst_vaapisink_ensure_rotation(sink, TRUE);
1077
1078     surface = gst_vaapi_video_meta_get_surface(meta);
1079     if (!surface)
1080         goto error;
1081
1082     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
1083               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
1084
1085     if (!surface_rect)
1086         surface_rect = (GstVaapiRectangle *)
1087             gst_vaapi_video_meta_get_render_rect(meta);
1088
1089     if (surface_rect)
1090         GST_DEBUG("render rect (%d,%d), size %ux%u",
1091                   surface_rect->x, surface_rect->y,
1092                   surface_rect->width, surface_rect->height);
1093
1094     flags = gst_vaapi_video_meta_get_render_flags(meta);
1095
1096     if (!gst_vaapi_apply_composition(surface, src_buffer))
1097         GST_WARNING("could not update subtitles");
1098
1099     switch (sink->display_type) {
1100 #if USE_DRM
1101     case GST_VAAPI_DISPLAY_TYPE_DRM:
1102         success = TRUE;
1103         break;
1104 #endif
1105 #if USE_GLX
1106     case GST_VAAPI_DISPLAY_TYPE_GLX:
1107         if (!sink->use_glx)
1108             goto put_surface_x11;
1109         success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
1110             flags);
1111         break;
1112 #endif
1113 #if USE_X11
1114     case GST_VAAPI_DISPLAY_TYPE_X11:
1115     put_surface_x11:
1116         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1117         break;
1118 #endif
1119 #if USE_WAYLAND
1120     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1121         success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1122         break;
1123 #endif
1124     default:
1125         GST_ERROR("unsupported display type %d", sink->display_type);
1126         success = FALSE;
1127         break;
1128     }
1129     if (!success)
1130         goto error;
1131
1132     /* Retain VA surface until the next one is displayed */
1133     if (sink->use_overlay)
1134         gst_buffer_replace(&sink->video_buffer, buffer);
1135     gst_buffer_unref(buffer);
1136     return GST_FLOW_OK;
1137
1138 error:
1139     gst_buffer_unref(buffer);
1140     return GST_FLOW_EOS;
1141 }
1142
1143 #if GST_CHECK_VERSION(1,0,0)
1144 static gboolean
1145 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
1146 {
1147     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1148     GstCaps *caps = NULL;
1149     gboolean need_pool;
1150
1151     gst_query_parse_allocation(query, &caps, &need_pool);
1152
1153     if (need_pool) {
1154         if (!caps)
1155             goto error_no_caps;
1156         if (!gst_vaapisink_ensure_video_buffer_pool(sink, caps))
1157             return FALSE;
1158         gst_query_add_allocation_pool(query, sink->video_buffer_pool,
1159             sink->video_buffer_size, 0, 0);
1160     }
1161
1162     gst_query_add_allocation_meta(query,
1163         GST_VAAPI_VIDEO_META_API_TYPE, NULL);
1164     gst_query_add_allocation_meta(query,
1165         GST_VIDEO_META_API_TYPE, NULL);
1166     gst_query_add_allocation_meta(query,
1167         GST_VIDEO_CROP_META_API_TYPE, NULL);
1168     gst_query_add_allocation_meta(query,
1169         GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1170     return TRUE;
1171
1172     /* ERRORS */
1173 error_no_caps:
1174     {
1175         GST_ERROR("no caps specified");
1176         return FALSE;
1177     }
1178 }
1179 #else
1180 static GstFlowReturn
1181 gst_vaapisink_buffer_alloc(
1182     GstBaseSink        *base_sink,
1183     guint64             offset,
1184     guint               size,
1185     GstCaps            *caps,
1186     GstBuffer         **pbuf
1187 )
1188 {
1189     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1190     GstVideoInfo vi;
1191     GstBuffer *buf;
1192
1193     *pbuf = NULL;
1194
1195     if (!sink->use_video_raw) {
1196         /* Note: this code path is rarely used but for raw YUV formats
1197            from custom pipeline. Otherwise, GstBaseSink::set_caps() is
1198            called first, and GstBaseSink::buffer_alloc() is not called
1199            in VA surface format mode */
1200         if (!gst_video_info_from_caps(&vi, caps))
1201             return GST_FLOW_NOT_SUPPORTED;
1202         if (!GST_VIDEO_INFO_IS_YUV(&vi))
1203             return GST_FLOW_OK;
1204     }
1205
1206     if (!gst_vaapi_uploader_ensure_display(sink->uploader, sink->display))
1207         return GST_FLOW_NOT_SUPPORTED;
1208     if (!gst_vaapi_uploader_ensure_caps(sink->uploader, caps, NULL))
1209         return GST_FLOW_NOT_SUPPORTED;
1210
1211     buf = gst_vaapi_uploader_get_buffer(sink->uploader);
1212     if (!buf) {
1213         GST_WARNING("failed to allocate resources for raw YUV buffer");
1214         return GST_FLOW_NOT_SUPPORTED;
1215     }
1216
1217     *pbuf = buf;
1218     return GST_FLOW_OK;
1219 }
1220 #endif
1221
1222 static gboolean
1223 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
1224 {
1225     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1226
1227     if (gst_vaapi_reply_to_query(query, sink->display)) {
1228         GST_DEBUG("sharing display %p", sink->display);
1229         return TRUE;
1230     }
1231     return GST_BASE_SINK_CLASS(gst_vaapisink_parent_class)->query(base_sink,
1232         query);
1233 }
1234
1235 static void
1236 gst_vaapisink_finalize(GObject *object)
1237 {
1238     gst_vaapisink_destroy(GST_VAAPISINK(object));
1239
1240     G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
1241 }
1242
1243 static void
1244 gst_vaapisink_set_property(
1245     GObject      *object,
1246     guint         prop_id,
1247     const GValue *value,
1248     GParamSpec   *pspec
1249 )
1250 {
1251     GstVaapiSink * const sink = GST_VAAPISINK(object);
1252
1253     switch (prop_id) {
1254     case PROP_DISPLAY_TYPE:
1255         sink->display_type = g_value_get_enum(value);
1256         break;
1257     case PROP_FULLSCREEN:
1258         sink->fullscreen = g_value_get_boolean(value);
1259         break;
1260     case PROP_SYNCHRONOUS:
1261         sink->synchronous = g_value_get_boolean(value);
1262         break;
1263     case PROP_USE_GLX:
1264         sink->use_glx = g_value_get_boolean(value);
1265         break;
1266     case PROP_USE_REFLECTION:
1267         sink->use_reflection = g_value_get_boolean(value);
1268         break;
1269     case PROP_ROTATION:
1270         sink->rotation_req = g_value_get_enum(value);
1271         break;
1272     case PROP_FORCE_ASPECT_RATIO:
1273         sink->keep_aspect = g_value_get_boolean(value);
1274         break;
1275     default:
1276         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1277         break;
1278     }
1279 }
1280
1281 static void
1282 gst_vaapisink_get_property(
1283     GObject    *object,
1284     guint       prop_id,
1285     GValue     *value,
1286     GParamSpec *pspec
1287 )
1288 {
1289     GstVaapiSink * const sink = GST_VAAPISINK(object);
1290
1291     switch (prop_id) {
1292     case PROP_DISPLAY_TYPE:
1293         g_value_set_enum(value, sink->display_type);
1294         break;
1295     case PROP_FULLSCREEN:
1296         g_value_set_boolean(value, sink->fullscreen);
1297         break;
1298     case PROP_SYNCHRONOUS:
1299         g_value_set_boolean(value, sink->synchronous);
1300         break;
1301     case PROP_USE_GLX:
1302         g_value_set_boolean(value, sink->use_glx);
1303         break;
1304     case PROP_USE_REFLECTION:
1305         g_value_set_boolean(value, sink->use_reflection);
1306         break;
1307     case PROP_ROTATION:
1308         g_value_set_enum(value, sink->rotation);
1309         break;
1310     case PROP_FORCE_ASPECT_RATIO:
1311         g_value_set_boolean(value, sink->keep_aspect);
1312         break;
1313     default:
1314         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1315         break;
1316     }
1317 }
1318
1319 static void
1320 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
1321 {
1322     GObjectClass * const     object_class   = G_OBJECT_CLASS(klass);
1323     GstElementClass * const  element_class  = GST_ELEMENT_CLASS(klass);
1324     GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
1325     GstPadTemplate *pad_template;
1326
1327     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
1328                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1329
1330     object_class->finalize       = gst_vaapisink_finalize;
1331     object_class->set_property   = gst_vaapisink_set_property;
1332     object_class->get_property   = gst_vaapisink_get_property;
1333
1334     basesink_class->start        = gst_vaapisink_start;
1335     basesink_class->stop         = gst_vaapisink_stop;
1336     basesink_class->get_caps     = gst_vaapisink_get_caps;
1337     basesink_class->set_caps     = gst_vaapisink_set_caps;
1338     basesink_class->preroll      = gst_vaapisink_show_frame;
1339     basesink_class->render       = gst_vaapisink_show_frame;
1340     basesink_class->query        = gst_vaapisink_query;
1341 #if GST_CHECK_VERSION(1,0,0)
1342     basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1343 #else
1344     basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1345 #endif
1346
1347     gst_element_class_set_static_metadata(element_class,
1348         "VA-API sink",
1349         "Sink/Video",
1350         GST_PLUGIN_DESC,
1351         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1352
1353     pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
1354     gst_element_class_add_pad_template(element_class, pad_template);
1355
1356     g_object_class_install_property
1357         (object_class,
1358          PROP_DISPLAY_TYPE,
1359          g_param_spec_enum("display",
1360                            "display type",
1361                            "display type to use",
1362                            GST_VAAPI_TYPE_DISPLAY_TYPE,
1363                            GST_VAAPI_DISPLAY_TYPE_ANY,
1364                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1365
1366 #if USE_GLX
1367     g_object_class_install_property
1368         (object_class,
1369          PROP_USE_GLX,
1370          g_param_spec_boolean("use-glx",
1371                               "OpenGL rendering",
1372                               "Enables OpenGL rendering",
1373                               FALSE,
1374                               G_PARAM_READWRITE));
1375
1376     g_object_class_install_property
1377         (object_class,
1378          PROP_USE_REFLECTION,
1379          g_param_spec_boolean("use-reflection",
1380                               "Reflection effect",
1381                               "Enables OpenGL reflection effect",
1382                               FALSE,
1383                               G_PARAM_READWRITE));
1384 #endif
1385
1386     g_object_class_install_property
1387         (object_class,
1388          PROP_FULLSCREEN,
1389          g_param_spec_boolean("fullscreen",
1390                               "Fullscreen",
1391                               "Requests window in fullscreen state",
1392                               FALSE,
1393                               G_PARAM_READWRITE));
1394
1395     /**
1396      * GstVaapiSink:synchronous:
1397      *
1398      * When enabled, runs the X display in synchronous mode. Note that
1399      * this is used only for debugging.
1400      */
1401     g_object_class_install_property
1402         (object_class,
1403          PROP_SYNCHRONOUS,
1404          g_param_spec_boolean("synchronous",
1405                               "Synchronous mode",
1406                               "Toggles X display synchronous mode",
1407                               FALSE,
1408                               G_PARAM_READWRITE));
1409
1410     /**
1411      * GstVaapiSink:rotation:
1412      *
1413      * The VA display rotation mode, expressed as a #GstVaapiRotation.
1414      */
1415     g_object_class_install_property
1416         (object_class,
1417          PROP_ROTATION,
1418          g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
1419                            "rotation",
1420                            "The display rotation mode",
1421                            GST_VAAPI_TYPE_ROTATION,
1422                            DEFAULT_ROTATION,
1423                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1424
1425     /**
1426      * GstVaapiSink:force-aspect-ratio:
1427      *
1428      * When enabled, scaling respects video aspect ratio; when disabled, the
1429      * video is distorted to fit the window.
1430      */
1431     g_object_class_install_property
1432         (object_class,
1433          PROP_FORCE_ASPECT_RATIO,
1434          g_param_spec_boolean("force-aspect-ratio",
1435                               "Force aspect ratio",
1436                               "When enabled, scaling will respect original aspect ratio",
1437                               TRUE,
1438                               G_PARAM_READWRITE));
1439 }
1440
1441 static void
1442 gst_vaapisink_init(GstVaapiSink *sink)
1443 {
1444     sink->caps           = NULL;
1445     sink->display        = NULL;
1446     sink->window         = NULL;
1447     sink->window_width   = 0;
1448     sink->window_height  = 0;
1449     sink->texture        = NULL;
1450     sink->video_buffer   = NULL;
1451     sink->video_width    = 0;
1452     sink->video_height   = 0;
1453     sink->video_par_n    = 1;
1454     sink->video_par_d    = 1;
1455     sink->foreign_window = FALSE;
1456     sink->fullscreen     = FALSE;
1457     sink->synchronous    = FALSE;
1458     sink->display_type   = DEFAULT_DISPLAY_TYPE;
1459     sink->rotation       = DEFAULT_ROTATION;
1460     sink->rotation_req   = DEFAULT_ROTATION;
1461     sink->use_reflection = FALSE;
1462     sink->use_overlay    = FALSE;
1463     sink->use_rotation   = FALSE;
1464     sink->keep_aspect    = TRUE;
1465 }