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