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