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