[vlc-devel] [PATCH 2/2][RFC] VAAPI XCB video output

Petri Hintukainen phintuka at gmail.com
Tue Aug 23 11:50:49 CEST 2016


---
 modules/MODULES_LIST        |   2 +
 modules/Makefile.am         |   1 +
 modules/hw/va/Makefile.am   |  19 ++
 modules/hw/va/chroma.c      | 187 ++++++++++++++++
 modules/hw/va/vlc_va.c      | 528 ++++++++++++++++++++++++++++++++++++++++++++
 modules/hw/va/vlc_va.h      | 152 +++++++++++++
 modules/hw/va/xcb_display.c | 485 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 1374 insertions(+)
 create mode 100644 modules/hw/va/Makefile.am
 create mode 100644 modules/hw/va/chroma.c
 create mode 100644 modules/hw/va/vlc_va.c
 create mode 100644 modules/hw/va/vlc_va.h
 create mode 100644 modules/hw/va/xcb_display.c

diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 696412f..9be54e8 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -420,6 +420,8 @@ $Id$
  * v4l2: Video 4 Linux 2 input module
  * vaapi_drm: VAAPI hardware-accelerated decoding with drm backend
  * vaapi_x11: VAAPI hardware-accelerated decoding with x11 backend
+ * va_chroma: VAAPI hardware surfaces conversion
+ * va_xcb_display: VAAPI xcb video display
  * vc1: VC-1 Video demuxer
  * vcd: input module for accessing Video CDs
  * vda: VDADecoder hardware-accelerated decoding
diff --git a/modules/Makefile.am b/modules/Makefile.am
index a4a9977..7937891 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -31,6 +31,7 @@ include codec/Makefile.am
 include control/Makefile.am
 include demux/Makefile.am
 include gui/Makefile.am
+include hw/va/Makefile.am
 include hw/vdpau/Makefile.am
 include keystore/Makefile.am
 include logger/Makefile.am
diff --git a/modules/hw/va/Makefile.am b/modules/hw/va/Makefile.am
new file mode 100644
index 0000000..b2be63b
--- /dev/null
+++ b/modules/hw/va/Makefile.am
@@ -0,0 +1,19 @@
+vadir = $(pluginsdir)/va
+
+libva_chroma_plugin_la_SOURCES = hw/va/chroma.c hw/va/vlc_va.c hw/va/vlc_va.h
+libva_chroma_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBVA_CFLAGS)
+libva_chroma_plugin_la_LIBADD = $(AM_LIBADD) $(LIBVA_LIBS) $(LIBVA_X11_LIBS)
+
+libva_xcb_display_plugin_la_SOURCES = hw/va/xcb_display.c hw/va/vlc_va.c hw/va/vlc_va.h
+libva_xcb_display_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/video_output/xcb
+libva_acb_display_plugin_la_CFLAGS = $(AM_CFLAGS) $(XCB_CFLAGS) $(LIBVA_CFLAGS)
+libva_xcb_display_plugin_la_LIBADD = libvlc_xcb_events.la \
+	$(AM_LIBADD) $(X_LIBS) $(X_PRE_LIBS) -lX11 $(XCB_LIBS) \
+	$(LIBVA_LIBS) $(LIBVA_X11_LIBS)
+
+if HAVE_VAAPI_X11
+va_LTLIBRARIES = libva_chroma_plugin.la
+if HAVE_XCB
+va_LTLIBRARIES += libva_xcb_display_plugin.la
+endif
+endif
diff --git a/modules/hw/va/chroma.c b/modules/hw/va/chroma.c
new file mode 100644
index 0000000..425f0d6
--- /dev/null
+++ b/modules/hw/va/chroma.c
@@ -0,0 +1,187 @@
+/*****************************************************************************
+ * chroma.c: VLC picture to VAAPI surface
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ *
+ * Authors: Petri Hintukainen <phintuka at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_filter.h>
+
+#include <va/va.h>
+
+#include "vlc_va.h"
+
+
+struct filter_sys_t
+{
+    VAImageFormat va_image_format;
+};
+
+static void Flush(filter_t *filter)
+{
+    VLC_UNUSED(filter);
+}
+
+static picture_t *UploadSurface(filter_t *filter, picture_t *src)
+{
+    filter_sys_t  *sys = filter->p_sys;
+    VAStatus       status;
+    picture_t     *dst = NULL;
+    picture_sys_t *picsys;
+
+    dst = filter_NewPicture(filter);
+    if (dst == NULL) {
+        msg_Err(filter, "filter_NewPicture failed\n");
+        goto error;
+    }
+
+    picsys = dst->p_sys;
+    assert(picsys != NULL);
+    assert(picsys->va_dpy != NULL);
+
+    status = vlc_va_PutSurface(VLC_OBJECT(filter), picsys->va_dpy, picsys->va_surface_id,
+                               &sys->va_image_format, src,
+                               filter->fmt_in.video.i_width,
+                               filter->fmt_in.video.i_visible_height,
+                               filter->fmt_out.video.i_width,
+                               filter->fmt_out.video.i_visible_height);
+    if (status != VA_STATUS_SUCCESS) {
+        goto error;
+    }
+
+    picture_CopyProperties(dst, src);
+    picture_Release(src);
+
+    return dst;
+
+ error:
+    if (dst) {
+        picture_Release(dst);
+    }
+    picture_Release(src);
+    return NULL;
+}
+
+static void OutputClose(vlc_object_t *obj)
+{
+    filter_t *filter = (filter_t *)obj;
+    filter_sys_t *sys = filter->p_sys;
+
+    free(sys);
+}
+
+static int OutputOpen(vlc_object_t *obj)
+{
+    filter_t      *filter = (filter_t *)obj;
+    unsigned int   va_rt_format;
+    unsigned int   va_fourcc;
+    picture_t     *pic = NULL;
+    picture_sys_t *picsys;
+    VAStatus       status;
+
+    if (filter->fmt_out.video.i_chroma != VLC_CODEC_VAAPI_OPAQUE) {
+        return VLC_EGENERIC;
+    }
+
+    if (filter->fmt_in.video.orientation != filter->fmt_out.video.orientation) {
+        return VLC_EGENERIC;
+    }
+
+    status = vlc_va_VaFourcc(filter->fmt_in.video.i_chroma, &va_fourcc, &va_rt_format);
+    if (status != VA_STATUS_SUCCESS) {
+        return VLC_EGENERIC;
+    }
+
+    msg_Dbg(filter, "fourcc: %4.4s, %dx%d --> %dx%d",
+            (const char *)&filter->fmt_in.video.i_chroma,
+            filter->fmt_in.video.i_visible_width,
+            filter->fmt_in.video.i_visible_height,
+            filter->fmt_out.video.i_visible_width,
+            filter->fmt_out.video.i_visible_height);
+
+    filter_sys_t *sys = calloc(1, sizeof (*sys));
+    if (unlikely(sys == NULL)) {
+        return VLC_ENOMEM;
+    }
+    filter->p_sys = sys;
+
+    /* check output picture */
+
+    pic = filter_NewPicture(filter);
+    if (pic == NULL) {
+        msg_Err(filter, "filter_NewPicture() failed");
+        goto error;
+    }
+    assert(pic->format.i_chroma == VLC_CODEC_VAAPI_OPAQUE);
+
+    picsys = pic->p_sys;
+    assert(picsys != NULL);
+    assert(picsys->va_dpy != NULL);
+
+    /* find VAAPI image format */
+
+    status = vlc_va_FindImageFormat(picsys->va_dpy, &sys->va_image_format, va_fourcc, 0);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(filter, "No VAAPI image format for %4.4s", (const char *)&va_fourcc);
+        goto error;
+    }
+
+    /* test PutImage */
+
+    status = vlc_va_TestPutImage(picsys->va_dpy, &sys->va_image_format,
+                                 picsys->va_surface_id, NULL,
+                                 filter->fmt_in.video.i_width,
+                                 filter->fmt_in.video.i_visible_height);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(filter, "vlc_va_TestPutImage() failed: %d\n", status);
+        goto error;
+    }
+
+    picture_Release(pic);
+
+    filter->pf_video_filter = UploadSurface;
+    filter->pf_flush = Flush;
+    return VLC_SUCCESS;
+
+error:
+    if (pic) {
+        picture_Release(pic);
+    }
+    OutputClose(obj);
+    return VLC_EGENERIC;
+}
+
+
+vlc_module_begin()
+    set_shortname(N_("VAAPI"))
+    set_description(N_("VAAPI surface conversions"))
+    set_capability("video filter2", 10)
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VFILTER)
+    set_callbacks(OutputOpen, OutputClose)
+vlc_module_end()
diff --git a/modules/hw/va/vlc_va.c b/modules/hw/va/vlc_va.c
new file mode 100644
index 0000000..d634024
--- /dev/null
+++ b/modules/hw/va/vlc_va.c
@@ -0,0 +1,528 @@
+/*****************************************************************************
+ * vlc_va.c: VAAPI helper for VLC
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ *
+ * Authors: Petri Hintukainen <phintuka at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "vlc_va.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <va/va.h>
+
+#include <vlc_common.h>
+#include <vlc_fourcc.h>
+#include <vlc_picture_pool.h>
+#include <vlc_subpicture.h>
+
+int vlc_va_Initialize(vlc_object_t *o, VADisplay va_dpy)
+{
+    VAStatus status;
+    int major = 0, minor = 0;
+    const char *vendor;
+
+    status = vaInitialize(va_dpy, &major, &minor);
+    if (status != VA_STATUS_SUCCESS) {
+        return status;
+    }
+
+    vendor = vaQueryVendorString(va_dpy);
+
+    msg_Info(o, "VA-API: v%d.%d (%s)\n", major, minor, vendor);
+
+    return VA_STATUS_SUCCESS;
+}
+
+void vlc_va_Terminate(VADisplay va_dpy)
+{
+    if (va_dpy) {
+        vaTerminate(va_dpy);
+    }
+}
+
+int vlc_va_SetDisplayAttribute(VADisplay va_dpy, VADisplayAttribType type, int value)
+{
+    VADisplayAttribute attr;
+
+    attr.type  = type;
+    attr.value = value;
+    attr.flags = VA_DISPLAY_ATTRIB_SETTABLE;
+
+    return vaSetDisplayAttributes(va_dpy, &attr, 1);
+}
+
+/*
+ * VAAPI image format
+ */
+
+VAImageFormat *vlc_va_GetImageFormats(VADisplay va_dpy, int *num_formats, int spu)
+{
+    VAImageFormat *formats = NULL;
+    VAStatus status;
+    int max_formats;
+
+    *num_formats = 0;
+
+    if (spu) {
+        max_formats = vaMaxNumSubpictureFormats(va_dpy);
+    } else {
+        max_formats = vaMaxNumImageFormats(va_dpy);
+    }
+    if (max_formats < 1) {
+        return NULL;
+    }
+
+    formats = malloc(max_formats * sizeof(*formats));
+    if (!formats) {
+        return NULL;
+    }
+
+    if (spu) {
+        status = vaQuerySubpictureFormats(va_dpy, formats, NULL, (unsigned *)num_formats);
+    } else {
+        status = vaQueryImageFormats(va_dpy, formats, num_formats);
+    }
+    if (status != VA_STATUS_SUCCESS || *num_formats < 1) {
+        free(formats);
+        return NULL;
+    }
+
+    return formats;
+}
+
+int vlc_va_FindImageFormat(VADisplay va_dpy, VAImageFormat *va_format,
+                           unsigned int va_fourcc, int spu)
+{
+    VAImageFormat *formats;
+    int num_formats, i;
+
+    formats = vlc_va_GetImageFormats(va_dpy, &num_formats, spu);
+
+    for (i = 0; i < num_formats; i++) {
+        if (formats[i].fourcc == va_fourcc) {
+            if (va_format) {
+                *va_format = formats[i];
+            }
+            free(formats);
+            return VA_STATUS_SUCCESS;
+        }
+    }
+
+    free(formats);
+    return VA_STATUS_ERROR_UNKNOWN;
+}
+
+/*
+ * Picture
+ */
+
+int vlc_va_TestPutImage(VADisplay va_dpy, VAImageFormat *va_format,
+                        VASurfaceID va_surface_id, int *derive,
+                        int width, int height)
+{
+    VAImage image;
+    VAStatus status;
+
+    status = vaCreateImage(va_dpy, va_format, width, height, &image);
+    if (status != VA_STATUS_SUCCESS) {
+        return status;
+    }
+
+    status = vaPutImage(va_dpy, va_surface_id, image.image_id,
+                        0, 0, width, height,
+                        0, 0, width, height);
+    vaDestroyImage(va_dpy, image.image_id);
+    if (status != VA_STATUS_SUCCESS) {
+        return status;
+    }
+
+    /* test vaDeriveImage */
+    if (derive) {
+        *derive = 0;
+        status = vaDeriveImage(va_dpy, va_surface_id, &image);
+        if (status == VA_STATUS_SUCCESS) {
+            if (image.format.fourcc == va_format->fourcc) {
+                *derive = 1;
+            }
+            vaDestroyImage(va_dpy, image.image_id);
+        }
+    }
+
+    return VA_STATUS_SUCCESS;
+}
+
+/*
+ *
+ */
+
+static void PictureSysDestroyVAAPI(picture_sys_t *sys)
+{
+    vaDestroySurfaces(sys->va_dpy, &sys->va_surface_id, 1);
+    free(sys);
+}
+
+static void PictureDestroyVAAPI(picture_t *pic)
+{
+    PictureSysDestroyVAAPI(pic->p_sys);
+    free(pic);
+}
+
+static int PictureNew(VADisplay va_dpy,
+                      const video_format_t *fmt,
+                      picture_t **picp, VASurfaceID id)
+{
+    picture_sys_t *sys = malloc(sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    sys->va_dpy        = va_dpy;
+    sys->va_surface_id = id;
+
+    picture_resource_t res = {
+        .p_sys = sys,
+        .pf_destroy = PictureDestroyVAAPI,
+    };
+
+    picture_t *pic = picture_NewFromResource(fmt, &res);
+    if (!pic) {
+        PictureSysDestroyVAAPI(sys);
+        return VLC_ENOMEM;
+    }
+
+    *picp = pic;
+    return VLC_SUCCESS;
+}
+
+picture_pool_t *vlc_va_PoolAlloc(vlc_object_t *o, VADisplay va_dpy, unsigned requested_count,
+                                 const video_format_t *restrict fmt, unsigned int va_rt_format)
+{
+    picture_t   *pics[requested_count];
+    VASurfaceID  va_surface_ids[requested_count];
+    VAStatus     status;
+    unsigned     count;
+
+    status = vaCreateSurfaces(va_dpy, va_rt_format,
+                              fmt->i_visible_width, fmt->i_visible_height,
+                              va_surface_ids, requested_count, NULL, 0);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaCreateSurfaces(%d) failed: %d\n", va_rt_format, status);
+        return NULL;
+    }
+
+#if 0
+    /* too late to test here ? */
+    status = vlc_va_TestPutImage(va_dpy, &va_format,
+                                 va_surface_ids[0], NULL,
+                                 fmt->i_visible_width, fmt->i_visible_height);
+#endif
+
+    for (count = 0; count < requested_count; count++) {
+        int err = PictureNew(va_dpy, fmt, pics + count, va_surface_ids[count]);
+        if (err != VLC_SUCCESS) {
+            break;
+        }
+    }
+
+    if (count != requested_count) {
+        vaDestroySurfaces(va_dpy, &va_surface_ids[count], requested_count - count);
+    }
+
+    if (count == 0) {
+        return NULL;
+    }
+
+    picture_pool_t *pool = picture_pool_New(count, pics);
+    if (!pool) {
+        while (count > 0) {
+            picture_Release(pics[--count]);
+        }
+    }
+    return pool;
+}
+
+/*
+ *
+ */
+
+static int CopyPicture(vlc_object_t *o,
+                       VAImage *va_image, uint8_t *base,
+                       int dst_x, int dst_y, const picture_t *src)
+{
+    plane_t dst_planes[3];
+
+    for (int i = 0; i < src->i_planes; i++) {
+        dst_planes[i].p_pixels        = base + va_image->offsets[i];
+        dst_planes[i].i_pitch         = va_image->pitches[i];
+        dst_planes[i].i_visible_pitch = va_image->pitches[i];
+        dst_planes[i].i_lines         = src->p[i].i_visible_lines;
+        dst_planes[i].i_visible_lines = src->p[i].i_visible_lines;
+        dst_planes[i].i_pixel_pitch   = src->p[i].i_pixel_pitch;
+    }
+
+    if (src->format.i_chroma == VLC_CODEC_I420 ||
+        src->format.i_chroma == VLC_CODEC_I422 ||
+        src->format.i_chroma == VLC_CODEC_I444) {
+
+        plane_t tmp = dst_planes[1];
+        dst_planes[1] = dst_planes[2];
+        dst_planes[2] = tmp;
+    }
+
+    switch (va_image->format.fourcc) {
+        case VA_FOURCC_ARGB:
+        case VA_FOURCC_RGBA:
+            dst_planes[0].p_pixels += dst_x * 4 + dst_y * dst_planes[0].i_pitch;
+            break;
+
+        case VA_FOURCC_IYUV:
+        case VA_FOURCC_YV12:
+            dst_planes[0].p_pixels += dst_x     + dst_y     * dst_planes[0].i_pitch;
+            dst_planes[1].p_pixels += dst_x / 2 + dst_y / 2 * dst_planes[1].i_pitch;
+            dst_planes[2].p_pixels += dst_x / 2 + dst_y / 2 * dst_planes[2].i_pitch;
+            break;
+
+        default:
+            msg_Err(o, "Unsupported va fourcc (%4.4s)", (const char *)&va_image->format.fourcc);
+            return VA_STATUS_ERROR_UNIMPLEMENTED;
+    }
+
+    for (int i = 0; i < src->i_planes; i++) {
+        plane_CopyPixels(&dst_planes[i], &src->p[i]);
+    }
+
+    return VA_STATUS_SUCCESS;
+}
+
+int vlc_va_PutSurface(vlc_object_t *o, VADisplay va_dpy,
+                      VASurfaceID va_surface_id,
+                      VAImageFormat *va_image_format, const picture_t *src,
+                      int in_width, int in_height, int out_width, int out_height)
+{
+    VAImage   surface_image;
+    VAStatus  status;
+    uint8_t  *base;
+    int       derived = 0;
+
+    /* create VAAPI image */
+
+    /* try DeriveImage if no scaling required */
+    if (in_width == out_width && in_height == out_height) {
+
+        status = vaDeriveImage(va_dpy, va_surface_id, &surface_image);
+        derived = (status == VA_STATUS_SUCCESS);
+    }
+    if (!derived) {
+        /* fall back to PutImage */
+        status = vaCreateImage(va_dpy, va_image_format, in_width, in_height, &surface_image);
+        if (status != VA_STATUS_SUCCESS) {
+            msg_Err(o, "vaCreateImage(0x%x) failed\n", va_image_format->fourcc);
+            return status;
+        }
+    }
+
+    /* copy bits */
+
+    status = vaMapBuffer(va_dpy, surface_image.buf, (void **)&base);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaMapBuffer() failed\n");
+        goto out;
+    }
+
+    status = CopyPicture(o, &surface_image, base, 0, 0, src);
+    vaUnmapBuffer(va_dpy, surface_image.buf);
+    if (status != VA_STATUS_SUCCESS) {
+        goto out;
+    }
+
+    if (!derived) {
+        status = vaPutImage(va_dpy, va_surface_id, surface_image.image_id,
+                            0, 0, in_width, in_height,
+                            0, 0, out_width, out_height);
+        if (status != VA_STATUS_SUCCESS) {
+            msg_Err(o, "vaPutImage(0x%x) failed\n", va_image_format->fourcc);
+            //goto out;
+        }
+    }
+
+ out:
+    vaDestroyImage(va_dpy, surface_image.image_id);
+    return status;
+}
+
+/*
+ * Subpictures
+ */
+
+  vlc_va_subpicture *vlc_va_SubpictureNew(void)
+{
+    vlc_va_subpicture *spu = calloc(1, sizeof(*spu));
+    if (spu) {
+        spu->va_subpicture_id  = VA_INVALID_ID;
+        spu->va_image.image_id = VA_INVALID_ID;
+    }
+    return spu;
+}
+
+static void DestroySubpicture(VADisplay va_dpy, vlc_va_subpicture *spu)
+{
+    if (spu->va_subpicture_id != VA_INVALID_ID) {
+        vaDestroySubpicture(va_dpy, spu->va_subpicture_id);
+        spu->va_subpicture_id = VA_INVALID_ID;
+    }
+    if (spu->va_image.image_id != VA_INVALID_ID) {
+        vaDestroyImage(va_dpy, spu->va_image.image_id);
+        spu->va_image.image_id = VA_INVALID_ID;
+    }
+}
+
+void vlc_va_SubpictureDestroy(VADisplay va_dpy, vlc_va_subpicture *spu)
+{
+    DestroySubpicture(va_dpy, spu);
+    free(spu);
+}
+
+static int CreateSubpicture(vlc_object_t *o, VADisplay va_dpy, vlc_va_subpicture *spu,
+                            VAImageFormat *va_format, int width, int height)
+{
+    VAStatus status;
+    void *base = NULL;
+
+    status = vaCreateImage(va_dpy, va_format, width, height, &spu->va_image);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaCreateImage(SPU) failed: %d\n", status);
+        goto error;
+    }
+
+    status = vaCreateSubpicture(va_dpy, spu->va_image.image_id, &spu->va_subpicture_id);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaCreateSubpicture() failed: %d\n", status);
+        goto error;
+    }
+
+    status = vaMapBuffer(va_dpy, spu->va_image.buf, &base);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaMapBuffer(SPU) failed: %d\n", status);
+        goto error;
+    }
+
+    memset(base, 0, spu->va_image.data_size);
+
+    status = vaUnmapBuffer(va_dpy, spu->va_image.buf);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaUnmapBuffer(SPU) failed: %d\n", status);
+        goto error;
+    }
+
+    spu->place.x = 0;
+    spu->place.y = 0;
+    spu->place.w = width;
+    spu->place.h = height;
+
+    return VA_STATUS_SUCCESS;
+
+error:
+    DestroySubpicture(va_dpy, spu);
+    if (status == VA_STATUS_SUCCESS) {
+        status = VA_STATUS_ERROR_UNKNOWN;
+    }
+    return status;
+}
+
+static int SubpictureRect(const subpicture_t *subpic, vlc_va_rect *r)
+{
+    unsigned int x0 = 0xffff, y0 = 0xffff, x1 = 0, y1 = 0;
+    for (subpicture_region_t *r = subpic->p_region; r != NULL; r = r->p_next) {
+        x0 = __MIN(x0, (unsigned)r->i_x);
+        y0 = __MIN(y0, (unsigned)r->i_y);
+        x1 = __MAX(x1, r->i_x + r->fmt.i_visible_width);
+        y1 = __MAX(y1, r->i_y + r->fmt.i_visible_height);
+    }
+    if (x1 < x0 || y1 < y0) {
+        return -1;
+    }
+    r->x = x0;
+    r->y = y0;
+    r->w = x1 - x0;
+    r->h = y1 - y0;
+    return 0;
+}
+
+int vlc_va_SubpictureUpdate(vlc_object_t *o, VADisplay va_dpy, VAImageFormat *va_format,
+                            vlc_va_subpicture *spu, subpicture_t *subpic)
+{
+    VAStatus status;
+    void *base = NULL;
+    vlc_va_rect rect;
+
+    /* empty subpicture ? */
+    if (!subpic || SubpictureRect(subpic, &rect) < 0) {
+        DestroySubpicture(va_dpy, spu);
+        return VA_STATUS_ERROR_UNKNOWN;
+    }
+
+    /* size changed ? */
+    if (rect.w != spu->place.w || rect.h != spu->place.h) {
+        DestroySubpicture(va_dpy, spu);
+        /* TODO: can update (bind new picture to subpicture) */
+    }
+
+    if (spu->va_subpicture_id == VA_INVALID_ID) {
+        status = CreateSubpicture(o, va_dpy, spu, va_format, rect.w, rect.h);
+        if (status != VA_STATUS_SUCCESS) {
+            return status;
+        }
+    }
+
+    spu->place = rect;
+
+    status = vaMapBuffer(va_dpy, spu->va_image.buf, &base);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaMapBuffer(SPU) failed: %d", status);
+        return status;
+    }
+
+    if (!subpic->p_region || subpic->p_region->p_next) {
+        /* no regions or more than one region */
+        memset(base, 0, spu->va_image.data_size);
+    }
+
+    for (subpicture_region_t *r = subpic->p_region; r != NULL; r = r->p_next) {
+        if (r->p_picture) {
+            status = CopyPicture(o, &spu->va_image, base,
+                                 r->i_x - rect.x, r->i_y - rect.y,
+                                 r->p_picture);
+            assert(status == VA_STATUS_SUCCESS);
+        }
+    }
+
+    status = vaUnmapBuffer(va_dpy, spu->va_image.buf);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(o, "vaUmmapBuffer(SPU) failed: %d", status);
+        return status;
+    }
+
+    return VA_STATUS_SUCCESS;
+}
diff --git a/modules/hw/va/vlc_va.h b/modules/hw/va/vlc_va.h
new file mode 100644
index 0000000..5088dfc
--- /dev/null
+++ b/modules/hw/va/vlc_va.h
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * vlc_va.h: VAAPI helper for VLC
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ *
+ * Authors: Petri Hintukainen <phintuka at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_VA_H
+
+#include <stdint.h>
+
+#include <va/va.h>
+
+#include <vlc_common.h>
+#include <vlc_fourcc.h>
+#include <vlc_picture_pool.h>
+
+
+/*
+ * VAAPI display
+ */
+
+int vlc_va_Initialize(vlc_object_t *o, VADisplay va_dpy);
+void vlc_va_Terminate(VADisplay va_dpy);
+
+int vlc_va_SetDisplayAttribute(VADisplay va_dpy, VADisplayAttribType type, int value);
+
+
+/*
+ * VAAPI image format
+ */
+
+VAImageFormat *vlc_va_GetImageFormats(VADisplay va_dpy, int *num_formats, int spu);
+
+int vlc_va_FindImageFormat(VADisplay va_dpy, VAImageFormat *va_format,
+                           unsigned int va_fourcc, int spu);
+
+static inline
+int vlc_va_OrientationToVaRotation(int orientation, int *va_rotation)
+{
+    switch (orientation) {
+        case ORIENT_TOP_LEFT:    *va_rotation = VA_ROTATION_NONE; break;
+        case ORIENT_ROTATED_90:  *va_rotation = VA_ROTATION_90;   break;
+        case ORIENT_ROTATED_180: *va_rotation = VA_ROTATION_180;  break;
+        case ORIENT_ROTATED_270: *va_rotation = VA_ROTATION_270;  break;
+        default:
+            *va_rotation = VA_ROTATION_NONE;
+            return VA_STATUS_ERROR_UNIMPLEMENTED;
+    }
+    return VA_STATUS_SUCCESS;
+}
+
+static inline
+int vlc_va_VaFourcc(vlc_fourcc_t fourcc,
+                    unsigned int *va_fourcc,
+                    unsigned int *va_rt_format)
+{
+    switch (fourcc)
+    {
+        case VLC_CODEC_I420:
+        case VLC_CODEC_YV12:
+            *va_fourcc    = VA_FOURCC_YV12;
+            *va_rt_format = VA_RT_FORMAT_YUV420;
+            break;
+        case VLC_CODEC_NV12:
+            *va_fourcc    = VA_FOURCC_NV12;
+            *va_rt_format = VA_RT_FORMAT_YUV420;
+            break;
+        case VLC_CODEC_I422:
+            *va_fourcc    = VA_FOURCC_422H;
+            *va_rt_format = VA_RT_FORMAT_YUV422;
+            break;
+        case VLC_CODEC_UYVY:
+            *va_fourcc    = VA_FOURCC_UYVY;
+            *va_rt_format = VA_RT_FORMAT_YUV422;
+            break;
+        case VLC_CODEC_I444:
+            *va_fourcc    = VA_FOURCC_444P;
+            *va_rt_format = VA_RT_FORMAT_YUV444;
+            break;
+        default:
+            return VA_STATUS_ERROR_UNIMPLEMENTED;
+    }
+    return VA_STATUS_SUCCESS;
+}
+
+/*
+ * Picture
+ */
+
+struct picture_sys_t {
+    VADisplay     va_dpy;
+    VASurfaceID   va_surface_id;
+};
+
+picture_pool_t *vlc_va_PoolAlloc(vlc_object_t *o, VADisplay va_dpy, unsigned requested_count,
+                                 const video_format_t *fmt, unsigned int va_rt_format);
+
+/*
+ * VAAPI images
+ */
+
+int vlc_va_TestPutImage(VADisplay va_dpy, VAImageFormat *va_format,
+                        VASurfaceID va_surface_id, int *derive,
+                        int width, int height);
+
+int vlc_va_PutSurface(vlc_object_t *o, VADisplay va_dpy,
+                      VASurfaceID va_surface_id,
+                      VAImageFormat *va_image_format, const picture_t *src,
+                      int in_width, int in_height, int out_width, int out_height);
+
+/*
+ * Subpicture
+ */
+
+typedef struct {
+    unsigned int x;
+    unsigned int y;
+    unsigned int w;
+    unsigned int h;
+} vlc_va_rect;
+
+typedef struct {
+    vlc_va_rect    place; /* may be different than VAImage dimensions */
+
+    VASubpictureID va_subpicture_id;
+    VAImage        va_image;
+} vlc_va_subpicture;
+
+vlc_va_subpicture *vlc_va_SubpictureNew(void);
+
+int vlc_va_SubpictureUpdate(vlc_object_t *o, VADisplay va_dpy, VAImageFormat *va_format,
+                            vlc_va_subpicture *spu, subpicture_t *subpic);
+
+void vlc_va_SubpictureDestroy(VADisplay va_dpy, vlc_va_subpicture *spu);
+
+#endif /* VLC_VA_H */
diff --git a/modules/hw/va/xcb_display.c b/modules/hw/va/xcb_display.c
new file mode 100644
index 0000000..2249edb
--- /dev/null
+++ b/modules/hw/va/xcb_display.c
@@ -0,0 +1,485 @@
+/*****************************************************************************
+ * xcb_display.c: VAAPI XCB display
+ *****************************************************************************
+ * Copyright (C) 2016 VLC authors and VideoLAN
+ *
+ * Authors: Petri Hintukainen <phintuka at gmail.com>
+ *          RĂ©mi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <xcb/xcb.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_vout_display.h>
+#include <vlc_picture_pool.h>
+#include <vlc_xlib.h>
+
+#include "events.h"
+
+#include <va/va.h>
+#include <va/va_x11.h>
+
+#include "vlc_va.h"
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_shortname(N_("VAAPI XCB"))
+    set_description(N_("VA-API video output (XCB)"))
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout display", 3000)
+    set_callbacks(Open, Close)
+
+    add_shortcut("vaapi", "xid")
+vlc_module_end()
+
+struct vout_display_sys_t
+{
+    /* XCB */
+    vout_window_t     *embed;  /**< parent window */
+    xcb_connection_t  *conn;   /**< XCB connection */
+    xcb_window_t       window; /**< target window */
+    xcb_cursor_t       cursor; /**< blank cursor */
+
+    Display           *dpy;    /**< X11 display */
+
+    picture_pool_t    *pool;
+
+    /* VAAPI */
+    VADisplay          va_dpy;
+    VAImageFormat      va_image_format;
+    VAImageFormat      va_spu_format;
+    unsigned int       va_rt_format;
+
+    vlc_va_subpicture *spu;
+};
+
+static void DestroySubpicture(vout_display_sys_t *sys)
+{
+    if (sys->spu) {
+        vlc_va_SubpictureDestroy(sys->va_dpy, sys->spu);
+        sys->spu = NULL;
+    }
+}
+
+static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    if (sys->pool == NULL) {
+        sys->pool = vlc_va_PoolAlloc(VLC_OBJECT(vd), sys->va_dpy,
+                                       requested_count, &vd->fmt, sys->va_rt_format);
+    }
+    return sys->pool;
+}
+
+static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
+{
+    vout_display_sys_t *sys = vd->sys;
+    picture_sys_t *picsys = pic->p_sys;
+    VAStatus status;
+
+    if (!subpicture || !subpicture->p_region) {
+        DestroySubpicture(sys);
+        return;
+    }
+
+    if (!sys->spu) {
+        sys->spu = vlc_va_SubpictureNew();
+    }
+
+    if (sys->spu) {
+        status = vlc_va_SubpictureUpdate(VLC_OBJECT(vd), sys->va_dpy,
+                                         &sys->va_spu_format, sys->spu, subpicture);
+        if (status != VA_STATUS_SUCCESS) {
+            DestroySubpicture(sys);
+        }
+    }
+
+    /* associate subpicture */
+    if (sys->spu) {
+        int d_x = sys->spu->place.x * pic->format.i_visible_width  / subpicture->i_original_picture_width;
+        int d_y = sys->spu->place.y * pic->format.i_visible_height / subpicture->i_original_picture_height;
+        int d_w = sys->spu->place.w * pic->format.i_visible_width  / subpicture->i_original_picture_width;
+        int d_h = sys->spu->place.h * pic->format.i_visible_height / subpicture->i_original_picture_height;
+
+        status = vaAssociateSubpicture(sys->va_dpy,
+                                       sys->spu->va_subpicture_id,
+                                       &picsys->va_surface_id, 1,
+
+                                       0, 0,
+                                       sys->spu->place.w, sys->spu->place.h,
+
+                                       d_x, d_y, d_w, d_h,
+                                       0);
+        if (status != VA_STATUS_SUCCESS) {
+            msg_Err(vd, "vaAassociateSubpicture failed: %d", status);
+        }
+    }
+}
+
+static void vaapiDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
+{
+    vout_display_sys_t *sys = vd->sys;
+    picture_sys_t *picsys = pic->p_sys;
+    video_format_t *fmt = &vd->fmt;
+    VAStatus status;
+    unsigned int flags = 0;
+    int va_rotation = VA_ROTATION_NONE;
+
+    flags |= VA_CLEAR_DRAWABLE;
+    flags |= VA_FRAME_PICTURE;
+    flags |= VA_FILTER_SCALING_HQ;
+
+    /* CSC */
+    switch (fmt->space)
+    {
+        case COLOR_SPACE_BT601:
+            flags |= VA_SRC_BT601;
+            break;
+        case COLOR_SPACE_BT709:
+            flags |= VA_SRC_BT709;
+            break;
+        default:
+            if (fmt->i_height >= 720) {
+                flags |= VA_SRC_BT709;
+            } else {
+                flags |= VA_SRC_BT601;
+            }
+    }
+
+    /* Set rotation. Ignore here (logged in Open) */
+    vlc_va_OrientationToVaRotation(pic->format.orientation, &va_rotation);
+    vlc_va_SetDisplayAttribute(sys->va_dpy, VADisplayAttribRotation, va_rotation);
+
+    /* render picture to output */
+
+    const video_format_t *src = &vd->source;
+    vout_display_place_t place;
+
+    vout_display_PlacePicture(&place, src, vd->cfg, false);
+
+    int i_visible_width  = place.width;
+    int i_visible_height = place.height;
+    int i_x_offset = src->i_x_offset * place.width / src->i_visible_width;
+    int i_y_offset = src->i_y_offset * place.height / src->i_visible_height;
+
+    status = vaPutSurface(sys->va_dpy, picsys->va_surface_id,
+                          sys->window,
+
+                          0, 0,
+                          pic->format.i_visible_width, pic->format.i_visible_height,
+
+                          i_x_offset, i_y_offset,
+                          i_visible_width, i_visible_height,
+
+                          NULL, 0, flags);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(vd, "vaPutSurface failed: %d", status);
+    }
+
+    status = vaSyncSurface(sys->va_dpy, picsys->va_surface_id);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(vd, "vaSyncSurface failed: %d", status);
+    }
+
+    if (sys->spu) {
+        vaDeassociateSubpicture(sys->va_dpy, sys->spu->va_subpicture_id,
+                                &picsys->va_surface_id, 1);
+    }
+
+    if (subpicture) {
+        subpicture_Delete(subpicture);
+    }
+
+    picture_Release(pic);
+}
+
+static void ConfigureWindow(vout_display_sys_t *sys,
+                            const video_format_t *source,
+                            const vout_display_cfg_t *cfg)
+{
+    vout_display_place_t place;
+    vout_display_PlacePicture(&place, source, cfg, false);
+
+    const uint32_t values[] = {place.x, place.y,
+                               place.width, place.height};
+    xcb_configure_window(sys->conn, sys->window,
+                         XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
+                         XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
+                         values);
+}
+
+static int Control(vout_display_t *vd, int query, va_list ap)
+{
+    vout_display_sys_t *sys = vd->sys;
+
+    switch (query)
+    {
+        case VOUT_DISPLAY_HIDE_MOUSE:
+            xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
+                                         XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
+            break;
+
+        case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+        case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
+        case VOUT_DISPLAY_CHANGE_ZOOM:
+        {
+            const vout_display_cfg_t *cfg = va_arg(ap, const vout_display_cfg_t *);
+            ConfigureWindow(sys, &vd->source, cfg);
+            break;
+        }
+
+        case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+        case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
+        {
+            const video_format_t *source = va_arg (ap, const video_format_t *);
+            ConfigureWindow(sys, source, vd->cfg);
+            break;
+        }
+
+        case VOUT_DISPLAY_RESET_PICTURES:
+            vlc_assert_unreachable();
+        default:
+            msg_Err(vd, "unknown control request %d", query);
+            return VLC_EGENERIC;
+    }
+
+    xcb_flush(sys->conn);
+    return VLC_SUCCESS;
+}
+
+static void Manage(vout_display_t *vd)
+{
+    vout_display_sys_t *sys = vd->sys;
+    bool visible;
+
+    XCB_Manage(vd, sys->conn, &visible);
+}
+
+static int vlc_va_InitializeX11(vout_display_t *vd, vout_display_sys_t *sys)
+{
+    VAStatus status;
+
+    sys->dpy = XOpenDisplay(sys->embed->display.x11);
+    if (!sys->dpy) {
+        return VA_STATUS_ERROR_UNKNOWN;
+    }
+
+    sys->va_dpy = vaGetDisplay(sys->dpy);
+    if (!sys->va_dpy) {
+        status = VA_STATUS_ERROR_UNKNOWN;
+        goto error;
+    }
+
+    status = vlc_va_Initialize(VLC_OBJECT(vd), sys->va_dpy);
+    if (status != VA_STATUS_SUCCESS) {
+        goto error;
+    }
+
+    return VA_STATUS_SUCCESS;
+
+ error:
+    if (sys->va_dpy) {
+        vlc_va_Terminate(sys->va_dpy);
+    }
+    XCloseDisplay(sys->dpy);
+    sys->va_dpy = NULL;
+    sys->dpy = NULL;
+    return status;
+}
+
+static int Open(vlc_object_t *obj)
+{
+    vout_display_t *vd = (vout_display_t *)obj;
+    video_format_t fmt = vd->fmt;
+    vout_display_sys_t *sys;
+    const xcb_screen_t *screen;
+    VAStatus status;
+
+    if (!vlc_xlib_init(obj)) {
+        return VLC_EGENERIC;
+    }
+
+    sys = calloc(1, sizeof (*sys));
+    if (!sys) {
+        return VLC_ENOMEM;
+    }
+
+    sys->embed = XCB_parent_Create(vd, &sys->conn, &screen);
+    if (!sys->embed) {
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    status = vlc_va_InitializeX11(vd, sys);
+    if (status != VA_STATUS_SUCCESS) {
+        goto error;
+    }
+
+    /* Check source format */
+
+    unsigned int va_fourcc;
+    status = vlc_va_VaFourcc(fmt.i_chroma, &va_fourcc, &sys->va_rt_format);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(vd, "unsupported vlc fourcc: %4.4s", (const char *)&fmt.i_chroma);
+        goto error;
+    }
+    msg_Dbg(vd, "VLC %4.4s mapped to VAAPI %4.4s (rt %d)",
+            (const char *)&fmt.i_chroma, (const char *)&va_fourcc, sys->va_rt_format);
+
+    status = vlc_va_FindImageFormat(sys->va_dpy, &sys->va_image_format, va_fourcc, 0);
+    if (status != VA_STATUS_SUCCESS) {
+        msg_Err(vd, "VAAPI image format for %4.4s not found", (const char *)&fmt.i_chroma);
+        goto error;
+    }
+
+    /* accept only VAAPI surfaces */
+    fmt.i_chroma = VLC_CODEC_VAAPI_OPAQUE;
+
+    /* create a window dedicated to the back-end */
+    {
+        xcb_pixmap_t pix = xcb_generate_id(sys->conn);
+        xcb_create_pixmap(sys->conn, screen->root_depth, pix,
+                          screen->root, 1, 1);
+
+        uint32_t mask =
+            XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL |
+            XCB_CW_BORDER_PIXMAP | XCB_CW_BORDER_PIXEL |
+            XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
+        const uint32_t values[] = {
+            pix, screen->black_pixel, pix, screen->black_pixel,
+            XCB_EVENT_MASK_VISIBILITY_CHANGE, screen->default_colormap
+        };
+        vout_display_place_t place;
+
+        vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
+        sys->window = xcb_generate_id(sys->conn);
+
+        xcb_void_cookie_t c =
+            xcb_create_window_checked(sys->conn, screen->root_depth,
+                sys->window, sys->embed->handle.xid, place.x, place.y,
+                place.width, place.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                screen->root_visual, mask, values);
+        if (XCB_error_Check(vd, sys->conn, "window creation failure", c))
+            goto error;
+        msg_Dbg(vd, "using X11 window 0x%08"PRIx32, sys->window);
+        xcb_map_window(sys->conn, sys->window);
+    }
+
+    /* Check subpicture format */
+    const vlc_fourcc_t *spu_chromas = NULL;
+#ifdef WORDS_BIGENDIAN
+    static const vlc_fourcc_t subpicture_chromas[] = { VLC_CODEC_ARGB, 0 };
+    status = vlc_va_FindImageFormat(sys->va_dpy, &sys->va_spu_format, VA_FOURCC_ARGB, 1);
+    if (status == VA_STATUS_SUCCESS) {
+        spu_chromas = subpicture_chromas;
+    }
+#else
+    static const vlc_fourcc_t subpicture_chromas[] = { VLC_CODEC_RGBA, 0 };
+    status = vlc_va_FindImageFormat(sys->va_dpy, &sys->va_spu_format, VA_FOURCC_RGBA, 1);
+    if (status == VA_STATUS_SUCCESS) {
+        spu_chromas = subpicture_chromas;
+    }
+#endif
+
+    /*
+      TODO: add HW SPU format (or add b_changed flag to subpicture_t ?)
+      => avoid uploading unchanged overlay every time frame is rendered
+
+      static const vlc_fourcc_t subpicture_chromas_GPU[] = { VLC_CODEC_VAAPI_SPU, 0 };
+      spu_chromas = subpicture_chromas_GPU;
+    */
+
+    /* check rotation support */
+    if (fmt.orientation != ORIENT_NORMAL) {
+        int va_rotation = VA_ROTATION_NONE;
+        status = vlc_va_OrientationToVaRotation(fmt.orientation, &va_rotation);
+        if (status != VA_STATUS_SUCCESS) {
+            msg_Err(vd, "Unsupported video orientation %d", fmt.orientation);
+            fmt.orientation = ORIENT_NORMAL;
+        } else if (vlc_va_SetDisplayAttribute(sys->va_dpy, VADisplayAttribRotation, va_rotation)
+                   != VA_STATUS_SUCCESS) {
+            msg_Err(vd, "HW does not support video orientation %d", fmt.orientation);
+            fmt.orientation = ORIENT_NORMAL;
+        }
+    }
+
+    sys->cursor = XCB_cursor_Create(sys->conn, screen);
+    sys->pool = NULL;
+
+    /* */
+    vd->sys = sys;
+    vd->info.has_pictures_invalid = true;
+    vd->info.has_event_thread = true;
+    vd->info.subpicture_chromas = spu_chromas;
+    vd->fmt = fmt;
+
+    vd->pool = Pool;
+    vd->prepare = Prepare;
+    vd->display = vaapiDisplay;
+    vd->control = Control;
+    vd->manage = Manage;
+
+    return VLC_SUCCESS;
+
+error:
+    vlc_va_Terminate(sys->va_dpy);
+    xcb_disconnect(sys->conn);
+    vout_display_DeleteWindow(vd, sys->embed);
+    free(sys);
+    return VLC_EGENERIC;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    vout_display_t *vd = (vout_display_t *)obj;
+    vout_display_sys_t *sys = vd->sys;
+
+    DestroySubpicture(sys);
+
+    /* Restore cursor explicitly (parent window connection will survive) */
+    xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
+                                 XCB_CW_CURSOR, &(uint32_t) { XCB_CURSOR_NONE });
+    xcb_flush(sys->conn);
+
+    if (sys->pool) {
+        picture_pool_Release(sys->pool);
+    }
+
+    if (sys->va_dpy) {
+        vlc_va_Terminate(sys->va_dpy);
+    }
+
+    if (sys->dpy) {
+        XCloseDisplay(sys->dpy);
+    }
+
+    xcb_disconnect(sys->conn);
+    vout_display_DeleteWindow(vd, sys->embed);
+    free(sys);
+}
-- 
2.7.4




More information about the vlc-devel mailing list