[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