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