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