2 * gstvaapisink.c - VA-API video sink
4 * Copyright (C) 2010-2011 Splitted-Desktop Systems
5 * Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6 * Copyright (C) 2011-2014 Intel Corporation
7 * Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA
26 * SECTION:gstvaapisink
27 * @short_description: A VA-API based videosink
29 * vaapisink renders video frames to a drawable (X #Window) on a local
30 * display using the Video Acceleration (VA) API. The element will
31 * create its own internal window and render into it.
34 #include "gst/vaapi/sysdeps.h"
36 #include <gst/video/video.h>
38 #include <gst/vaapi/gstvaapivalue.h>
40 # include <gst/vaapi/gstvaapidisplay_drm.h>
43 # include <gst/vaapi/gstvaapidisplay_x11.h>
44 # include <gst/vaapi/gstvaapiwindow_x11.h>
47 # include <gst/vaapi/gstvaapidisplay_glx.h>
48 # include <gst/vaapi/gstvaapiwindow_glx.h>
51 # include <gst/vaapi/gstvaapidisplay_wayland.h>
52 # include <gst/vaapi/gstvaapiwindow_wayland.h>
55 /* Supported interfaces */
56 #if GST_CHECK_VERSION(1,0,0)
57 # include <gst/video/videooverlay.h>
59 # include <gst/interfaces/xoverlay.h>
61 # define GST_TYPE_VIDEO_OVERLAY GST_TYPE_X_OVERLAY
62 # define GST_VIDEO_OVERLAY GST_X_OVERLAY
63 # define GstVideoOverlay GstXOverlay
64 # define GstVideoOverlayInterface GstXOverlayClass
66 # define gst_video_overlay_prepare_window_handle(sink) \
67 gst_x_overlay_prepare_xwindow_id(sink)
68 # define gst_video_overlay_got_window_handle(sink, window_handle) \
69 gst_x_overlay_got_window_handle(sink, window_handle)
72 #include "gstvaapisink.h"
73 #include "gstvaapipluginutil.h"
74 #include "gstvaapivideometa.h"
75 #if GST_CHECK_VERSION(1,0,0)
76 #include "gstvaapivideobufferpool.h"
77 #include "gstvaapivideomemory.h"
80 #define GST_PLUGIN_NAME "vaapisink"
81 #define GST_PLUGIN_DESC "A VA-API based videosink"
83 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapisink);
84 #define GST_CAT_DEFAULT gst_debug_vaapisink
86 /* Default template */
87 static const char gst_vaapisink_sink_caps_str[] =
88 #if GST_CHECK_VERSION(1,1,0)
89 GST_VIDEO_CAPS_MAKE_WITH_FEATURES(
90 GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE, "{ ENCODED, NV12, I420, YV12 }") ";"
91 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL);
93 #if GST_CHECK_VERSION(1,0,0)
94 GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; "
97 "width = (int) [ 1, MAX ], "
98 "height = (int) [ 1, MAX ]; "
100 GST_VAAPI_SURFACE_CAPS;
103 static GstStaticPadTemplate gst_vaapisink_sink_factory =
104 GST_STATIC_PAD_TEMPLATE(
108 GST_STATIC_CAPS(gst_vaapisink_sink_caps_str));
111 gst_vaapisink_has_interface(GstVaapiPluginBase *plugin, GType type)
113 return type == GST_TYPE_VIDEO_OVERLAY;
117 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface);
119 G_DEFINE_TYPE_WITH_CODE(
123 GST_VAAPI_PLUGIN_BASE_INIT_INTERFACES
124 G_IMPLEMENT_INTERFACE(GST_TYPE_VIDEO_OVERLAY,
125 gst_vaapisink_video_overlay_iface_init))
137 PROP_FORCE_ASPECT_RATIO,
140 #define DEFAULT_DISPLAY_TYPE GST_VAAPI_DISPLAY_TYPE_ANY
141 #define DEFAULT_ROTATION GST_VAAPI_ROTATION_0
143 static inline gboolean
144 gst_vaapisink_ensure_display(GstVaapiSink *sink);
146 /* GstVideoOverlay interface */
150 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id);
154 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer);
157 gst_vaapisink_video_overlay_set_window_handle(GstVideoOverlay *overlay,
160 GstVaapiSink * const sink = GST_VAAPISINK(overlay);
161 GstVaapiDisplayType display_type;
163 if (!gst_vaapisink_ensure_display(sink))
165 display_type = GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
167 /* Disable GLX rendering when vaapisink is using a foreign X
168 window. It's pretty much useless */
169 if (display_type == GST_VAAPI_DISPLAY_TYPE_GLX) {
170 display_type = GST_VAAPI_DISPLAY_TYPE_X11;
171 gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
175 sink->foreign_window = TRUE;
177 switch (display_type) {
179 case GST_VAAPI_DISPLAY_TYPE_X11:
180 gst_vaapisink_ensure_window_xid(sink, window);
189 gst_vaapisink_video_overlay_set_render_rectangle(
190 GstVideoOverlay *overlay,
197 GstVaapiSink * const sink = GST_VAAPISINK(overlay);
198 GstVaapiRectangle * const display_rect = &sink->display_rect;
202 display_rect->width = width;
203 display_rect->height = height;
205 GST_DEBUG("render rect (%d,%d):%ux%u",
206 display_rect->x, display_rect->y,
207 display_rect->width, display_rect->height);
211 gst_vaapisink_video_overlay_expose(GstVideoOverlay *overlay)
213 GstVaapiSink * const sink = GST_VAAPISINK(overlay);
215 if (sink->video_buffer)
216 gst_vaapisink_show_frame(GST_BASE_SINK_CAST(sink),
217 gst_buffer_ref(sink->video_buffer));
221 gst_vaapisink_video_overlay_iface_init(GstVideoOverlayInterface *iface)
223 iface->set_window_handle = gst_vaapisink_video_overlay_set_window_handle;
224 iface->set_render_rectangle = gst_vaapisink_video_overlay_set_render_rectangle;
225 iface->expose = gst_vaapisink_video_overlay_expose;
229 gst_vaapisink_destroy(GstVaapiSink *sink)
231 gst_buffer_replace(&sink->video_buffer, NULL);
233 gst_vaapi_texture_replace(&sink->texture, NULL);
235 gst_caps_replace(&sink->caps, NULL);
239 /* Checks whether a ConfigureNotify event is in the queue */
240 typedef struct _ConfigureNotifyEventPendingArgs ConfigureNotifyEventPendingArgs;
241 struct _ConfigureNotifyEventPendingArgs {
249 configure_notify_event_pending_cb(Display *dpy, XEvent *xev, XPointer arg)
251 ConfigureNotifyEventPendingArgs * const args =
252 (ConfigureNotifyEventPendingArgs *)arg;
254 if (xev->type == ConfigureNotify &&
255 xev->xconfigure.window == args->window &&
256 xev->xconfigure.width == args->width &&
257 xev->xconfigure.height == args->height)
260 /* XXX: this is a hack to traverse the whole queue because we
261 can't use XPeekIfEvent() since it could block */
266 configure_notify_event_pending(
273 GstVaapiDisplayX11 * const display =
274 GST_VAAPI_DISPLAY_X11(GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
275 ConfigureNotifyEventPendingArgs args;
278 args.window = window;
280 args.height = height;
283 /* XXX: don't use XPeekIfEvent() because it might block */
285 gst_vaapi_display_x11_get_display(display),
287 configure_notify_event_pending_cb, (XPointer)&args
294 get_display_type_name(GstVaapiDisplayType display_type)
296 gpointer const klass = g_type_class_peek(GST_VAAPI_TYPE_DISPLAY_TYPE);
297 GEnumValue * const e = g_enum_get_value(klass, display_type);
300 return e->value_name;
301 return "<unknown-type>";
304 static inline gboolean
305 gst_vaapisink_ensure_display(GstVaapiSink *sink)
307 return gst_vaapi_plugin_base_ensure_display(GST_VAAPI_PLUGIN_BASE(sink));
311 gst_vaapisink_display_changed(GstVaapiPluginBase *plugin)
313 GstVaapiSink * const sink = GST_VAAPISINK(plugin);
314 GstVaapiRenderMode render_mode;
316 GST_INFO("created %s %p", get_display_type_name(plugin->display_type),
320 gst_vaapi_display_get_render_mode(plugin->display, &render_mode) &&
321 render_mode == GST_VAAPI_RENDER_MODE_OVERLAY;
322 GST_DEBUG("use %s rendering mode",
323 sink->use_overlay ? "overlay" : "texture");
325 sink->use_rotation = gst_vaapi_display_has_property(plugin->display,
326 GST_VAAPI_DISPLAY_PROP_ROTATION);
330 gst_vaapisink_ensure_uploader(GstVaapiSink *sink)
332 if (!gst_vaapisink_ensure_display(sink))
334 if (!gst_vaapi_plugin_base_ensure_uploader(GST_VAAPI_PLUGIN_BASE(sink)))
340 gst_vaapisink_ensure_render_rect(GstVaapiSink *sink, guint width, guint height)
342 GstVaapiRectangle * const display_rect = &sink->display_rect;
343 guint num, den, display_par_n, display_par_d;
346 /* Return success if caps are not set yet */
350 if (!sink->keep_aspect) {
351 display_rect->width = width;
352 display_rect->height = height;
356 GST_DEBUG("force-aspect-ratio is false; distorting while scaling video");
357 GST_DEBUG("render rect (%d,%d):%ux%u",
358 display_rect->x, display_rect->y,
359 display_rect->width, display_rect->height);
363 GST_DEBUG("ensure render rect within %ux%u bounds", width, height);
365 gst_vaapi_display_get_pixel_aspect_ratio(
366 GST_VAAPI_PLUGIN_BASE_DISPLAY(sink),
367 &display_par_n, &display_par_d
369 GST_DEBUG("display pixel-aspect-ratio %d/%d",
370 display_par_n, display_par_d);
372 success = gst_video_calculate_display_ratio(
374 sink->video_width, sink->video_height,
375 sink->video_par_n, sink->video_par_d,
376 display_par_n, display_par_d
380 GST_DEBUG("video size %dx%d, calculated ratio %d/%d",
381 sink->video_width, sink->video_height, num, den);
383 display_rect->width = gst_util_uint64_scale_int(height, num, den);
384 if (display_rect->width <= width) {
385 GST_DEBUG("keeping window height");
386 display_rect->height = height;
389 GST_DEBUG("keeping window width");
390 display_rect->width = width;
391 display_rect->height =
392 gst_util_uint64_scale_int(width, den, num);
394 GST_DEBUG("scaling video to %ux%u", display_rect->width, display_rect->height);
396 g_assert(display_rect->width <= width);
397 g_assert(display_rect->height <= height);
399 display_rect->x = (width - display_rect->width) / 2;
400 display_rect->y = (height - display_rect->height) / 2;
402 GST_DEBUG("render rect (%d,%d):%ux%u",
403 display_rect->x, display_rect->y,
404 display_rect->width, display_rect->height);
409 gst_vaapisink_ensure_window_size(GstVaapiSink *sink, guint *pwidth, guint *pheight)
411 GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
412 GstVideoRectangle src_rect, dst_rect, out_rect;
413 guint num, den, display_width, display_height, display_par_n, display_par_d;
414 gboolean success, scale;
416 if (sink->foreign_window) {
417 *pwidth = sink->window_width;
418 *pheight = sink->window_height;
422 gst_vaapi_display_get_size(display, &display_width, &display_height);
423 if (sink->fullscreen) {
424 *pwidth = display_width;
425 *pheight = display_height;
429 gst_vaapi_display_get_pixel_aspect_ratio(
431 &display_par_n, &display_par_d
434 success = gst_video_calculate_display_ratio(
436 sink->video_width, sink->video_height,
437 sink->video_par_n, sink->video_par_d,
438 display_par_n, display_par_d
441 num = sink->video_par_n;
442 den = sink->video_par_d;
447 src_rect.w = gst_util_uint64_scale_int(sink->video_height, num, den);
448 src_rect.h = sink->video_height;
451 dst_rect.w = display_width;
452 dst_rect.h = display_height;
453 scale = (src_rect.w > dst_rect.w || src_rect.h > dst_rect.h);
454 gst_video_sink_center_rect(src_rect, dst_rect, &out_rect, scale);
455 *pwidth = out_rect.w;
456 *pheight = out_rect.h;
459 static inline gboolean
460 gst_vaapisink_ensure_window(GstVaapiSink *sink, guint width, guint height)
462 GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
465 const GstVaapiDisplayType display_type =
466 GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink);
467 switch (display_type) {
469 case GST_VAAPI_DISPLAY_TYPE_GLX:
470 sink->window = gst_vaapi_window_glx_new(display, width, height);
471 goto notify_video_overlay_interface;
474 case GST_VAAPI_DISPLAY_TYPE_X11:
475 sink->window = gst_vaapi_window_x11_new(display, width, height);
476 notify_video_overlay_interface:
479 gst_video_overlay_got_window_handle(
480 GST_VIDEO_OVERLAY(sink),
481 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window))
486 case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
487 sink->window = gst_vaapi_window_wayland_new(display, width, height);
491 GST_ERROR("unsupported display type %d", display_type);
495 return sink->window != NULL;
500 gst_vaapisink_ensure_window_xid(GstVaapiSink *sink, guintptr window_id)
502 GstVaapiDisplay *display;
504 unsigned int width, height, border_width, depth;
508 if (!gst_vaapisink_ensure_display(sink))
510 display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
512 gst_vaapi_display_lock(display);
514 gst_vaapi_display_x11_get_display(GST_VAAPI_DISPLAY_X11(display)),
517 &x, &y, &width, &height, &border_width, &depth
519 gst_vaapi_display_unlock(display);
521 if ((width != sink->window_width || height != sink->window_height) &&
522 !configure_notify_event_pending(sink, xid, width, height)) {
523 if (!gst_vaapisink_ensure_render_rect(sink, width, height))
525 sink->window_width = width;
526 sink->window_height = height;
530 gst_vaapi_window_x11_get_xid(GST_VAAPI_WINDOW_X11(sink->window)) == xid)
533 gst_vaapi_window_replace(&sink->window, NULL);
535 switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
537 case GST_VAAPI_DISPLAY_TYPE_GLX:
538 sink->window = gst_vaapi_window_glx_new_with_xid(display, xid);
541 case GST_VAAPI_DISPLAY_TYPE_X11:
542 sink->window = gst_vaapi_window_x11_new_with_xid(display, xid);
545 GST_ERROR("unsupported display type %d",
546 GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
549 return sink->window != NULL;
554 gst_vaapisink_ensure_rotation(GstVaapiSink *sink, gboolean recalc_display_rect)
556 GstVaapiDisplay * const display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
557 gboolean success = FALSE;
559 g_return_val_if_fail(display, FALSE);
561 if (sink->rotation == sink->rotation_req)
564 if (!sink->use_rotation) {
565 GST_WARNING("VA display does not support rotation");
569 gst_vaapi_display_lock(display);
570 success = gst_vaapi_display_set_rotation(display, sink->rotation_req);
571 gst_vaapi_display_unlock(display);
573 GST_ERROR("failed to change VA display rotation mode");
577 if (((sink->rotation + sink->rotation_req) % 180) == 90) {
578 /* Orientation changed */
579 G_PRIMITIVE_SWAP(guint, sink->video_width, sink->video_height);
580 G_PRIMITIVE_SWAP(gint, sink->video_par_n, sink->video_par_d);
583 if (recalc_display_rect && !sink->foreign_window)
584 gst_vaapisink_ensure_render_rect(sink, sink->window_width,
585 sink->window_height);
589 sink->rotation = sink->rotation_req;
594 gst_vaapisink_start(GstBaseSink *base_sink)
596 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
598 if (!gst_vaapi_plugin_base_open(GST_VAAPI_PLUGIN_BASE(sink)))
600 return gst_vaapisink_ensure_uploader(sink);
604 gst_vaapisink_stop(GstBaseSink *base_sink)
606 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
608 gst_buffer_replace(&sink->video_buffer, NULL);
609 #if GST_CHECK_VERSION(1,0,0)
610 g_clear_object(&sink->video_buffer_pool);
612 gst_vaapi_window_replace(&sink->window, NULL);
614 gst_vaapi_plugin_base_close(GST_VAAPI_PLUGIN_BASE(sink));
619 gst_vaapisink_get_caps_impl(GstBaseSink *base_sink)
621 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
622 GstCaps *out_caps, *yuv_caps;
624 #if GST_CHECK_VERSION(1,1,0)
625 out_caps = gst_static_pad_template_get_caps(&gst_vaapisink_sink_factory);
627 out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS);
632 if (gst_vaapisink_ensure_uploader(sink)) {
633 yuv_caps = GST_VAAPI_PLUGIN_BASE_UPLOADER_CAPS(sink);
635 out_caps = gst_caps_make_writable(out_caps);
636 gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
642 #if GST_CHECK_VERSION(1,0,0)
643 static inline GstCaps *
644 gst_vaapisink_get_caps(GstBaseSink *base_sink, GstCaps *filter)
646 GstCaps *caps, *out_caps;
648 caps = gst_vaapisink_get_caps_impl(base_sink);
649 if (caps && filter) {
650 out_caps = gst_caps_intersect_full(caps, filter,
651 GST_CAPS_INTERSECT_FIRST);
652 gst_caps_unref(caps);
659 #define gst_vaapisink_get_caps gst_vaapisink_get_caps_impl
663 update_colorimetry(GstVaapiSink *sink, GstVideoColorimetry *cinfo)
665 #if GST_CHECK_VERSION(1,0,0)
666 if (gst_video_colorimetry_matches(cinfo,
667 GST_VIDEO_COLORIMETRY_BT601))
668 sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_601;
669 else if (gst_video_colorimetry_matches(cinfo,
670 GST_VIDEO_COLORIMETRY_BT709))
671 sink->color_standard = GST_VAAPI_COLOR_STANDARD_ITUR_BT_709;
672 else if (gst_video_colorimetry_matches(cinfo,
673 GST_VIDEO_COLORIMETRY_SMPTE240M))
674 sink->color_standard = GST_VAAPI_COLOR_STANDARD_SMPTE_240M;
676 sink->color_standard = 0;
678 GST_DEBUG("colorimetry %s", gst_video_colorimetry_to_string(cinfo));
683 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
685 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
686 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
687 GstVideoInfo * const vip = GST_VAAPI_PLUGIN_BASE_SINK_PAD_INFO(sink);
688 GstVaapiDisplay *display;
689 guint win_width, win_height;
691 if (!gst_vaapisink_ensure_display(sink))
693 display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
696 if (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink) == GST_VAAPI_DISPLAY_TYPE_DRM)
700 if (!gst_vaapi_plugin_base_set_caps(plugin, caps, NULL))
703 sink->video_width = GST_VIDEO_INFO_WIDTH(vip);
704 sink->video_height = GST_VIDEO_INFO_HEIGHT(vip);
705 sink->video_par_n = GST_VIDEO_INFO_PAR_N(vip);
706 sink->video_par_d = GST_VIDEO_INFO_PAR_D(vip);
707 GST_DEBUG("video pixel-aspect-ratio %d/%d",
708 sink->video_par_n, sink->video_par_d);
710 update_colorimetry(sink, &vip->colorimetry);
711 gst_caps_replace(&sink->caps, caps);
713 gst_vaapisink_ensure_rotation(sink, FALSE);
715 gst_vaapisink_ensure_window_size(sink, &win_width, &win_height);
717 if (!sink->foreign_window || sink->fullscreen)
718 gst_vaapi_window_set_size(sink->window, win_width, win_height);
721 gst_vaapi_display_lock(display);
722 gst_video_overlay_prepare_window_handle(GST_VIDEO_OVERLAY(sink));
723 gst_vaapi_display_unlock(display);
726 if (!gst_vaapisink_ensure_window(sink, win_width, win_height))
728 gst_vaapi_window_set_fullscreen(sink->window, sink->fullscreen);
729 gst_vaapi_window_show(sink->window);
730 gst_vaapi_window_get_size(sink->window, &win_width, &win_height);
732 sink->window_width = win_width;
733 sink->window_height = win_height;
734 GST_DEBUG("window size %ux%u", win_width, win_height);
736 return gst_vaapisink_ensure_render_rect(sink, win_width, win_height);
741 render_background(GstVaapiSink *sink)
743 /* Original code from Mirco Muller (MacSlow):
744 <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
745 GLfloat fStartX = 0.0f;
746 GLfloat fStartY = 0.0f;
747 GLfloat fWidth = (GLfloat)sink->window_width;
748 GLfloat fHeight = (GLfloat)sink->window_height;
750 glClear(GL_COLOR_BUFFER_BIT);
753 /* top third, darker grey to white */
754 glColor3f(0.85f, 0.85f, 0.85f);
755 glVertex3f(fStartX, fStartY, 0.0f);
756 glColor3f(0.85f, 0.85f, 0.85f);
757 glVertex3f(fStartX + fWidth, fStartY, 0.0f);
758 glColor3f(1.0f, 1.0f, 1.0f);
759 glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
760 glColor3f(1.0f, 1.0f, 1.0f);
761 glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
763 /* middle third, just plain white */
764 glColor3f(1.0f, 1.0f, 1.0f);
765 glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
766 glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
767 glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
768 glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
770 /* bottom third, white to lighter grey */
771 glColor3f(1.0f, 1.0f, 1.0f);
772 glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
773 glColor3f(1.0f, 1.0f, 1.0f);
774 glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
775 glColor3f(0.62f, 0.66f, 0.69f);
776 glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
777 glColor3f(0.62f, 0.66f, 0.69f);
778 glVertex3f(fStartX, fStartY + fHeight, 0.0f);
784 render_frame(GstVaapiSink *sink, GstVaapiSurface *surface,
785 const GstVaapiRectangle *surface_rect)
787 const guint x1 = sink->display_rect.x;
788 const guint x2 = sink->display_rect.x + sink->display_rect.width;
789 const guint y1 = sink->display_rect.y;
790 const guint y2 = sink->display_rect.y + sink->display_rect.height;
791 gfloat tx1, tx2, ty1, ty2;
795 gst_vaapi_surface_get_size(surface, &width, &height);
796 tx1 = (gfloat)surface_rect->x / width;
797 ty1 = (gfloat)surface_rect->y / height;
798 tx2 = (gfloat)(surface_rect->x + surface_rect->width) / width;
799 ty2 = (gfloat)(surface_rect->y + surface_rect->height) / height;
808 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
811 glTexCoord2f(tx1, ty1); glVertex2i(x1, y1);
812 glTexCoord2f(tx1, ty2); glVertex2i(x1, y2);
813 glTexCoord2f(tx2, ty2); glVertex2i(x2, y2);
814 glTexCoord2f(tx2, ty1); glVertex2i(x2, y1);
820 render_reflection(GstVaapiSink *sink)
822 const guint x1 = sink->display_rect.x;
823 const guint x2 = sink->display_rect.x + sink->display_rect.width;
824 const guint y1 = sink->display_rect.y;
825 const guint rh = sink->display_rect.height / 5;
826 GLfloat ry = 1.0f - (GLfloat)rh / (GLfloat)sink->display_rect.height;
830 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
831 glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y1);
832 glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y1);
834 glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
835 glTexCoord2f(1.0f, ry); glVertex2i(x2, y1 + rh);
836 glTexCoord2f(0.0f, ry); glVertex2i(x1, y1 + rh);
842 gst_vaapisink_ensure_texture(GstVaapiSink *sink, GstVaapiSurface *surface)
844 GstVaapiDisplay * display = GST_VAAPI_PLUGIN_BASE_DISPLAY(sink);
845 GstVideoRectangle tex_rect, dis_rect, out_rect;
851 gst_vaapi_surface_get_size(surface, &width, &height);
857 gst_vaapi_display_get_size(display, &width, &height);
863 gst_video_sink_center_rect(tex_rect, dis_rect, &out_rect, TRUE);
865 /* XXX: use surface size for now since some VA drivers have issues
866 with downscaling to the provided texture size. i.e. we should be
867 using the resulting out_rect size, which preserves the aspect
868 ratio of the surface */
871 GST_INFO("texture size %ux%u", width, height);
873 sink->texture = gst_vaapi_texture_new(display,
874 GL_TEXTURE_2D, GL_BGRA, width, height);
875 return sink->texture != NULL;
879 gst_vaapisink_show_frame_glx(
881 GstVaapiSurface *surface,
882 const GstVaapiRectangle *surface_rect,
886 GstVaapiWindowGLX *window;
892 window = GST_VAAPI_WINDOW_GLX(sink->window);
894 gst_vaapi_window_glx_make_current(window);
895 if (!gst_vaapisink_ensure_texture(sink, surface))
896 goto error_create_texture;
897 if (!gst_vaapi_texture_put_surface(sink->texture, surface, flags))
898 goto error_transfer_surface;
900 target = gst_vaapi_texture_get_target(sink->texture);
901 texture = gst_vaapi_texture_get_id(sink->texture);
902 if (target != GL_TEXTURE_2D || !texture)
905 if (sink->use_reflection)
906 render_background(sink);
909 glBindTexture(target, texture);
911 if (sink->use_reflection) {
913 glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
914 glTranslatef(50.0f, 0.0f, 0.0f);
916 render_frame(sink, surface, surface_rect);
917 if (sink->use_reflection) {
919 glTranslatef(0.0, (GLfloat)sink->display_rect.height + 5.0f, 0.0f);
920 render_reflection(sink);
925 glBindTexture(target, 0);
927 gst_vaapi_window_glx_swap_buffers(window);
931 error_create_texture:
933 GST_DEBUG("could not create VA/GLX texture");
936 error_transfer_surface:
938 GST_DEBUG("could not transfer VA surface to texture");
944 static inline gboolean
945 gst_vaapisink_put_surface(
947 GstVaapiSurface *surface,
948 const GstVaapiRectangle *surface_rect,
955 if (!gst_vaapi_window_put_surface(sink->window, surface,
956 surface_rect, &sink->display_rect, flags)) {
957 GST_DEBUG("could not render VA surface");
964 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *src_buffer)
966 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
967 GstVaapiVideoMeta *meta;
968 GstVaapiSurface *surface;
972 GstVaapiRectangle *surface_rect = NULL;
973 #if GST_CHECK_VERSION(1,0,0)
974 GstVaapiRectangle tmp_rect;
978 #if GST_CHECK_VERSION(1,0,0)
979 GstVideoCropMeta * const crop_meta =
980 gst_buffer_get_video_crop_meta(src_buffer);
982 surface_rect = &tmp_rect;
983 surface_rect->x = crop_meta->x;
984 surface_rect->y = crop_meta->y;
985 surface_rect->width = crop_meta->width;
986 surface_rect->height = crop_meta->height;
990 ret = gst_vaapi_plugin_base_get_input_buffer(GST_VAAPI_PLUGIN_BASE(sink),
991 src_buffer, &buffer);
992 if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_SUPPORTED)
995 meta = gst_buffer_get_vaapi_video_meta(buffer);
996 GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE(sink,
997 gst_vaapi_video_meta_get_display(meta));
999 gst_vaapisink_ensure_rotation(sink, TRUE);
1001 surface = gst_vaapi_video_meta_get_surface(meta);
1005 GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
1006 GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
1009 surface_rect = (GstVaapiRectangle *)
1010 gst_vaapi_video_meta_get_render_rect(meta);
1013 GST_DEBUG("render rect (%d,%d), size %ux%u",
1014 surface_rect->x, surface_rect->y,
1015 surface_rect->width, surface_rect->height);
1017 flags = gst_vaapi_video_meta_get_render_flags(meta);
1019 /* Append default color standard obtained from caps if none was
1020 available on a per-buffer basis */
1021 if (!(flags & GST_VAAPI_COLOR_STANDARD_MASK))
1022 flags |= sink->color_standard;
1024 if (!gst_vaapi_apply_composition(surface, src_buffer))
1025 GST_WARNING("could not update subtitles");
1027 switch (GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink)) {
1029 case GST_VAAPI_DISPLAY_TYPE_DRM:
1034 case GST_VAAPI_DISPLAY_TYPE_GLX:
1036 goto put_surface_x11;
1037 success = gst_vaapisink_show_frame_glx(sink, surface, surface_rect,
1042 case GST_VAAPI_DISPLAY_TYPE_X11:
1044 success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1048 case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1049 success = gst_vaapisink_put_surface(sink, surface, surface_rect, flags);
1053 GST_ERROR("unsupported display type %d",
1054 GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1061 /* Retain VA surface until the next one is displayed */
1062 gst_buffer_replace(&sink->video_buffer, buffer);
1063 gst_buffer_unref(buffer);
1067 gst_buffer_unref(buffer);
1068 return GST_FLOW_EOS;
1071 #if GST_CHECK_VERSION(1,0,0)
1073 gst_vaapisink_propose_allocation(GstBaseSink *base_sink, GstQuery *query)
1075 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(base_sink);
1077 if (!gst_vaapi_plugin_base_propose_allocation(plugin, query))
1080 gst_query_add_allocation_meta(query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1081 gst_query_add_allocation_meta(query,
1082 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
1086 static GstFlowReturn
1087 gst_vaapisink_buffer_alloc(GstBaseSink *base_sink, guint64 offset, guint size,
1088 GstCaps *caps, GstBuffer **outbuf_ptr)
1090 return gst_vaapi_plugin_base_allocate_input_buffer(
1091 GST_VAAPI_PLUGIN_BASE(base_sink), caps, outbuf_ptr);
1096 gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
1098 GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
1100 GST_INFO_OBJECT(sink, "query type %s", GST_QUERY_TYPE_NAME(query));
1102 if (gst_vaapi_reply_to_query(query, GST_VAAPI_PLUGIN_BASE_DISPLAY(sink))) {
1103 GST_DEBUG("sharing display %p", GST_VAAPI_PLUGIN_BASE_DISPLAY(sink));
1107 return GST_BASE_SINK_CLASS(gst_vaapisink_parent_class)->query(base_sink,
1112 gst_vaapisink_finalize(GObject *object)
1114 gst_vaapisink_destroy(GST_VAAPISINK(object));
1116 gst_vaapi_plugin_base_finalize(GST_VAAPI_PLUGIN_BASE(object));
1117 G_OBJECT_CLASS(gst_vaapisink_parent_class)->finalize(object);
1121 gst_vaapisink_set_property(
1124 const GValue *value,
1128 GstVaapiSink * const sink = GST_VAAPISINK(object);
1131 case PROP_DISPLAY_TYPE:
1132 gst_vaapi_plugin_base_set_display_type(GST_VAAPI_PLUGIN_BASE(sink),
1133 g_value_get_enum(value));
1135 case PROP_DISPLAY_NAME:
1136 gst_vaapi_plugin_base_set_display_name(GST_VAAPI_PLUGIN_BASE(sink),
1137 g_value_get_string(value));
1139 case PROP_FULLSCREEN:
1140 sink->fullscreen = g_value_get_boolean(value);
1142 case PROP_SYNCHRONOUS:
1143 sink->synchronous = g_value_get_boolean(value);
1146 sink->use_glx = g_value_get_boolean(value);
1148 case PROP_USE_REFLECTION:
1149 sink->use_reflection = g_value_get_boolean(value);
1152 sink->rotation_req = g_value_get_enum(value);
1154 case PROP_FORCE_ASPECT_RATIO:
1155 sink->keep_aspect = g_value_get_boolean(value);
1158 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1164 gst_vaapisink_get_property(
1171 GstVaapiSink * const sink = GST_VAAPISINK(object);
1174 case PROP_DISPLAY_TYPE:
1175 g_value_set_enum(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_TYPE(sink));
1177 case PROP_DISPLAY_NAME:
1178 g_value_set_string(value, GST_VAAPI_PLUGIN_BASE_DISPLAY_NAME(sink));
1180 case PROP_FULLSCREEN:
1181 g_value_set_boolean(value, sink->fullscreen);
1183 case PROP_SYNCHRONOUS:
1184 g_value_set_boolean(value, sink->synchronous);
1187 g_value_set_boolean(value, sink->use_glx);
1189 case PROP_USE_REFLECTION:
1190 g_value_set_boolean(value, sink->use_reflection);
1193 g_value_set_enum(value, sink->rotation);
1195 case PROP_FORCE_ASPECT_RATIO:
1196 g_value_set_boolean(value, sink->keep_aspect);
1199 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1205 gst_vaapisink_set_bus(GstElement *element, GstBus *bus)
1207 /* Make sure to allocate a VA display in the sink element first,
1208 so that upstream elements could query a display that was
1209 allocated here, and that exactly matches what the user
1210 requested through the "display" property */
1211 if (!GST_ELEMENT_BUS(element) && bus)
1212 gst_vaapisink_ensure_display(GST_VAAPISINK(element));
1214 GST_ELEMENT_CLASS(gst_vaapisink_parent_class)->set_bus(element, bus);
1218 gst_vaapisink_class_init(GstVaapiSinkClass *klass)
1220 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
1221 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
1222 GstBaseSinkClass * const basesink_class = GST_BASE_SINK_CLASS(klass);
1223 GstVaapiPluginBaseClass * const base_plugin_class =
1224 GST_VAAPI_PLUGIN_BASE_CLASS(klass);
1225 GstPadTemplate *pad_template;
1227 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapisink,
1228 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
1230 gst_vaapi_plugin_base_class_init(base_plugin_class);
1231 base_plugin_class->has_interface = gst_vaapisink_has_interface;
1232 base_plugin_class->display_changed = gst_vaapisink_display_changed;
1234 object_class->finalize = gst_vaapisink_finalize;
1235 object_class->set_property = gst_vaapisink_set_property;
1236 object_class->get_property = gst_vaapisink_get_property;
1238 basesink_class->start = gst_vaapisink_start;
1239 basesink_class->stop = gst_vaapisink_stop;
1240 basesink_class->get_caps = gst_vaapisink_get_caps;
1241 basesink_class->set_caps = gst_vaapisink_set_caps;
1242 basesink_class->preroll = gst_vaapisink_show_frame;
1243 basesink_class->render = gst_vaapisink_show_frame;
1244 basesink_class->query = gst_vaapisink_query;
1245 #if GST_CHECK_VERSION(1,0,0)
1246 basesink_class->propose_allocation = gst_vaapisink_propose_allocation;
1248 basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
1251 element_class->set_bus = gst_vaapisink_set_bus;
1252 gst_element_class_set_static_metadata(element_class,
1256 "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
1258 pad_template = gst_static_pad_template_get(&gst_vaapisink_sink_factory);
1259 gst_element_class_add_pad_template(element_class, pad_template);
1261 g_object_class_install_property
1264 g_param_spec_enum("display",
1266 "display type to use",
1267 GST_VAAPI_TYPE_DISPLAY_TYPE,
1268 GST_VAAPI_DISPLAY_TYPE_ANY,
1269 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1271 g_object_class_install_property
1274 g_param_spec_string("display-name",
1276 "display name to use",
1278 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1281 g_object_class_install_property
1284 g_param_spec_boolean("use-glx",
1286 "Enables OpenGL rendering",
1288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1290 g_object_class_install_property
1292 PROP_USE_REFLECTION,
1293 g_param_spec_boolean("use-reflection",
1294 "Reflection effect",
1295 "Enables OpenGL reflection effect",
1297 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1300 g_object_class_install_property
1303 g_param_spec_boolean("fullscreen",
1305 "Requests window in fullscreen state",
1307 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1310 * GstVaapiSink:synchronous:
1312 * When enabled, runs the X display in synchronous mode. Note that
1313 * this is used only for debugging.
1315 g_object_class_install_property
1318 g_param_spec_boolean("synchronous",
1320 "Toggles X display synchronous mode",
1322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1325 * GstVaapiSink:rotation:
1327 * The VA display rotation mode, expressed as a #GstVaapiRotation.
1329 g_object_class_install_property
1332 g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
1334 "The display rotation mode",
1335 GST_VAAPI_TYPE_ROTATION,
1337 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1340 * GstVaapiSink:force-aspect-ratio:
1342 * When enabled, scaling respects video aspect ratio; when disabled, the
1343 * video is distorted to fit the window.
1345 g_object_class_install_property
1347 PROP_FORCE_ASPECT_RATIO,
1348 g_param_spec_boolean("force-aspect-ratio",
1349 "Force aspect ratio",
1350 "When enabled, scaling will respect original aspect ratio",
1352 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1356 gst_vaapisink_init(GstVaapiSink *sink)
1358 GstVaapiPluginBase * const plugin = GST_VAAPI_PLUGIN_BASE(sink);
1360 gst_vaapi_plugin_base_init(plugin, GST_CAT_DEFAULT);
1361 gst_vaapi_plugin_base_set_display_type(plugin, DEFAULT_DISPLAY_TYPE);
1364 sink->window = NULL;
1365 sink->window_width = 0;
1366 sink->window_height = 0;
1367 sink->texture = NULL;
1368 sink->video_buffer = NULL;
1369 sink->video_width = 0;
1370 sink->video_height = 0;
1371 sink->video_par_n = 1;
1372 sink->video_par_d = 1;
1373 sink->foreign_window = FALSE;
1374 sink->fullscreen = FALSE;
1375 sink->synchronous = FALSE;
1376 sink->rotation = DEFAULT_ROTATION;
1377 sink->rotation_req = DEFAULT_ROTATION;
1378 sink->use_reflection = FALSE;
1379 sink->use_overlay = FALSE;
1380 sink->use_rotation = FALSE;
1381 sink->keep_aspect = TRUE;
1382 gst_video_info_init(&sink->video_info);