---
configure | 5 +
libavutil/Makefile | 1 +
libavutil/hwcontext.c | 3 +
libavutil/hwcontext.h | 1 +
libavutil/hwcontext_internal.h | 1 +
libavutil/hwcontext_vaapi.c | 715 +++++++++++++++++++++++++++++++++++++++++
libavutil/hwcontext_vaapi.h | 33 ++
7 files changed, 759 insertions(+)
create mode 100644 libavutil/hwcontext_vaapi.c
create mode 100644 libavutil/hwcontext_vaapi.h
diff --git a/configure b/configure
index 270a6ec..c0d8d5e 100755
--- a/configure
+++ b/configure
@@ -1736,6 +1736,7 @@ CONFIG_EXTRA="
texturedsp
texturedspenc
tpeldsp
+ vaapi_recent
videodsp
vp3dsp
vp56dsp
@@ -4712,6 +4713,10 @@ enabled vaapi && enabled xlib &&
check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
enable vaapi_x11
+enabled vaapi &&
+ check_code cc "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" &&
+ enable vaapi_recent
+
enabled vdpau &&
check_cpp_condition vdpau/vdpau.h "defined
VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
disable vdpau
diff --git a/libavutil/Makefile b/libavutil/Makefile
index bc85925..7f96523 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -109,6 +109,7 @@ OBJS = adler32.o
\
OBJS-$(CONFIG_LZO) += lzo.o
OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o
OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o
+OBJS-$(CONFIG_VAAPI_RECENT) += hwcontext_vaapi.o
OBJS += $(COMPAT_OBJS:%=../compat/%)
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index b6d0518..98e4824 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -35,6 +35,9 @@ static const HWContextType *hw_table[] = {
#if CONFIG_VDPAU
&ff_hwcontext_type_vdpau,
#endif
+#if CONFIG_VAAPI_RECENT
+ &ff_hwcontext_type_vaapi,
+#endif
NULL,
};
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index 81ae817..c68c191 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -27,6 +27,7 @@
enum AVHWDeviceType {
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
+ AV_HWDEVICE_TYPE_VAAPI,
};
typedef struct AVHWDeviceInternal AVHWDeviceInternal;
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index 641232f..3b27d53 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -88,5 +88,6 @@ struct AVHWFramesInternal {
extern const HWContextType ff_hwcontext_type_cuda;
extern const HWContextType ff_hwcontext_type_vdpau;
+extern const HWContextType ff_hwcontext_type_vaapi;
#endif /* AVUTIL_HWCONTEXT_INTERNAL_H */
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
new file mode 100644
index 0000000..d8e514b
--- /dev/null
+++ b/libavutil/hwcontext_vaapi.c
@@ -0,0 +1,715 @@
+/*
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avassert.h"
+#include "buffer.h"
+#include "common.h"
+#include "mem.h"
+#include "pixdesc.h"
+#include "pixfmt.h"
+
+#include "hwcontext.h"
+#include "hwcontext_internal.h"
+#include "hwcontext_vaapi.h"
+
+typedef struct VAAPISurfaceFormat {
+ enum AVPixelFormat pix_fmt;
+ VAImageFormat image_format;
+} VAAPISurfaceFormat;
+
+typedef struct VAAPIDeviceContext {
+ // Surface formats which can be used with this device.
+ VAAPISurfaceFormat *format_list;
+ int format_count;
+} VAAPIDeviceContext;
+
+typedef struct VAAPIFramesContext {
+ // Surface attributes set at create time.
+ VASurfaceAttrib *attributes;
+ int attribute_count;
+ // RT format of the underlying surface (Intel driver ignores this anyway).
+ unsigned int rt_format;
+ // Whether vaDeriveImage works.
+ int derive_works;
+} VAAPIFramesContext;
+
+enum {
+ VAAPI_MAP_READ = 0x01,
+ VAAPI_MAP_WRITE = 0x02,
+ VAAPI_MAP_DIRECT = 0x04,
+};
+
+typedef struct VAAPISurfaceMap {
+ const AVFrame *source;
+ int flags;
+ VAImage image;
+} VAAPISurfaceMap;
+
+static struct {
+ unsigned int fourcc;
+ unsigned int rt_format;
+ enum AVPixelFormat pix_fmt;
+} vaapi_format_map[] = {
+#define MAP(va, rt, av) { \
+ VA_FOURCC_ ## va, \
+ VA_RT_FORMAT_ ## rt, \
+ AV_PIX_FMT_ ## av \
+ }
+ MAP(NV12, YUV420, NV12),
+ MAP(IYUV, YUV420, YUV420P),
+ MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
+ MAP(YV16, YUV422, YUV422P),
+ MAP(UYVY, YUV422, UYVY422),
+ //MAP(P010, YUV420_10BPP, P010),
+ MAP(Y800, YUV400, GRAY8),
+ MAP(BGRA, RGB32, BGRA),
+ //MAP(BGRX, RGB32, BGR0),
+ MAP(RGBA, RGB32, RGBA),
+ //MAP(RGBX, RGB32, RGB0),
+#undef MAP
+};
+
+static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
+{
+ int i;
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
+ if (vaapi_format_map[i].fourcc == fourcc)
+ return vaapi_format_map[i].pix_fmt;
+ return AV_PIX_FMT_NONE;
+}
+
+static unsigned int vaapi_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt)
+{
+ int i;
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
+ if (vaapi_format_map[i].pix_fmt == pix_fmt)
+ return vaapi_format_map[i].fourcc;
+ return 0;
+}
+
+static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
+ enum AVPixelFormat pix_fmt,
+ VAImageFormat **image_format)
+{
+ VAAPIDeviceContext *ctx = hwdev->internal->priv;
+ int i;
+
+ for (i = 0; i < ctx->format_count; i++) {
+ if (ctx->format_list[i].pix_fmt == pix_fmt) {
+ *image_format = &ctx->format_list[i].image_format;
+ return 0;
+ }
+ }
+ return AVERROR(EINVAL);
+}
+
+static int vaapi_device_init(AVHWDeviceContext *hwdev)
+{
+ VAAPIDeviceContext *ctx = hwdev->internal->priv;
+ AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
+ VAStatus vas;
+ VAConfigID config_id = VA_INVALID_ID;
+ VASurfaceAttrib *attr_list = 0;
+ VAImageFormat *image_list;
+ int err, i, j, attr_count, pix_fmt_count, image_count;
+ enum AVPixelFormat pix_fmt;
+ unsigned int fourcc;
+
+ // Create a temporary pipeline configuration in order to query
+ // supported image formats.
+ vas = vaCreateConfig(hwctx->display,
+ VAProfileNone, VAEntrypointVideoProc,
+ 0, 0, &config_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ // No vpp. We might still be able to do something useful if
+ // codecs are support, so try to make the simplest decoder we
+ // can to query instead.
+ vas = vaCreateConfig(hwctx->display,
+ VAProfileMPEG2Simple, VAEntrypointVLD,
+ 0, 0, &config_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwdev, AV_LOG_ERROR, "VAAPI hardware does not appear "
+ "to support processing or decoding: giving up.\n");
+ return AVERROR(ENOSYS);
+ }
+ }
+
+ attr_count = 0;
+ vas = vaQuerySurfaceAttributes(hwctx->display, config_id,
+ 0, &attr_count);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
+ "%d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ attr_list = av_malloc(attr_count * sizeof(VASurfaceAttrib));
+ if (!attr_list) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ vas = vaQuerySurfaceAttributes(hwctx->display, config_id,
+ attr_list, &attr_count);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
+ "%d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ pix_fmt_count = 0;
+ for (i = 0; i < attr_count; i++) {
+ if (attr_list[i].type == VASurfaceAttribPixelFormat) {
+ fourcc = attr_list[i].value.value.i;
+ pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+ if (pix_fmt != AV_PIX_FMT_NONE) {
+ av_log(hwdev, AV_LOG_DEBUG, "Hardware supports %#x -> %s.\n",
+ fourcc, av_get_pix_fmt_name(pix_fmt));
+ ++pix_fmt_count;
+ } else {
+ av_log(hwdev, AV_LOG_DEBUG, "Hardware supports unknown "
+ "format %#x.\n", fourcc);
+ }
+ }
+ }
+ if (pix_fmt_count == 0) {
+ av_log(hwdev, AV_LOG_DEBUG, "Hardware does not support any usable "
+ "formats.\n");
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ image_count = vaMaxNumImageFormats(hwctx->display);
+ if (image_count <= 0) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ image_list = av_malloc(image_count * sizeof(VAImageFormat));
+ if (!image_list) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
+ if (vas != VA_STATUS_SUCCESS) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ ctx->format_count = 0;
+ ctx->format_list = av_malloc(pix_fmt_count * sizeof(VAAPISurfaceFormat));
+ if (!ctx->format_list) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ for (i = 0; i < attr_count; i++) {
+ if (attr_list[i].type == VASurfaceAttribPixelFormat) {
+ fourcc = attr_list[i].value.value.i;
+ pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
+ if (pix_fmt == AV_PIX_FMT_NONE)
+ continue;
+ for (j = 0; j < image_count; j++) {
+ if (image_list[j].fourcc == fourcc)
+ break;
+ }
+ ctx->format_list[ctx->format_count].pix_fmt = pix_fmt;
+ memcpy(&ctx->format_list[ctx->format_count].image_format,
+ &image_list[j], sizeof(VAImageFormat));
+ ++ctx->format_count;
+ }
+ }
+
+ av_free(image_list);
+ av_free(attr_list);
+ vaDestroyConfig(hwctx->display, config_id);
+
+ return 0;
+ fail:
+ if (ctx->format_list)
+ av_freep(&ctx->format_list);
+ if (image_list)
+ av_free(image_list);
+ if (attr_list)
+ av_free(attr_list);
+ if (config_id != VA_INVALID_ID)
+ vaDestroyConfig(hwctx->display, config_id);
+ return err;
+}
+
+static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
+{
+ VAAPIDeviceContext *ctx = hwdev->internal->priv;
+
+ av_freep(&ctx->format_list);
+}
+
+static void vaapi_buffer_free(void *opaque, uint8_t *data)
+{
+ AVHWFramesContext *hwfc = opaque;
+ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ VASurfaceID surface_id;
+ VAStatus vas;
+
+ surface_id = (VASurfaceID)(uintptr_t)data;
+
+ vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create surface %#x: "
+ "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ }
+}
+
+static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
+{
+ AVHWFramesContext *hwfc = opaque;
+ VAAPIFramesContext *ctx = hwfc->internal->priv;
+ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ AVVAAPIFramesContext *avfc = hwfc->hwctx;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ AVBufferRef *ref;
+
+ vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
+ hwfc->width, hwfc->height,
+ &surface_id, 1,
+ ctx->attributes, ctx->attribute_count);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
+ "%d (%s).\n", vas, vaErrorStr(vas));
+ return NULL;
+ }
+ av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
+
+ ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
+ sizeof(surface_id), &vaapi_buffer_free,
+ hwfc, AV_BUFFER_FLAG_READONLY);
+ if (!ref) {
+ vaDestroySurfaces(hwctx->display, &surface_id, 1);
+ return NULL;
+ }
+
+ if (hwfc->initial_pool_size > 0) {
+ av_assert0(avfc->surface_count < hwfc->initial_pool_size);
+ avfc->surface_ids[avfc->surface_count] = surface_id;
+ }
+ ++avfc->surface_count;
+ if (avfc->surface_count == 1) {
+ // Test whether vaDeriveImage() works for this type of surface.
+ VAImageFormat *expected_format;
+ VAImage test_image;
+ int err;
+
+ ctx->derive_works = 0;
+
+ err = vaapi_get_image_format(hwfc->device_ctx,
+ hwfc->sw_format, &expected_format);
+ if (err == 0) {
+ vas = vaDeriveImage(hwctx->display, surface_id, &test_image);
+ if (vas == VA_STATUS_SUCCESS) {
+ if (expected_format->fourcc == test_image.format.fourcc) {
+ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
+ ctx->derive_works = 1;
+ } else {
+ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
+ "derived image format %08x does not match "
+ "expected format %08x.\n",
+ expected_format->fourcc, test_image.format.fourcc);
+ }
+ vaDestroyImage(hwctx->display, test_image.image_id);
+ } else {
+ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
+ "deriving image does not work: "
+ "%d (%s).\n", vas, vaErrorStr(vas));
+ }
+ } else {
+ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
+ "image format is not supported.\n");
+ }
+ }
+
+ return ref;
+}
+
+static int vaapi_frames_init(AVHWFramesContext *hwfc)
+{
+ AVVAAPIFramesContext *avfc = hwfc->hwctx;
+ VAAPIFramesContext *ctx = hwfc->internal->priv;
+ int err, i;
+ unsigned int fourcc, rt_format;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
+ if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
+ fourcc = vaapi_format_map[i].fourcc;
+ rt_format = vaapi_format_map[i].rt_format;
+ break;
+ }
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
+ av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
+ av_get_pix_fmt_name(hwfc->sw_format));
+ return AVERROR(EINVAL);
+ }
+
+ if (!hwfc->pool) {
+ // Being able to feed additional config attributes into here (or
+ // replace these defaults) would be nice?
+ ctx->attributes = av_malloc(2 * sizeof(VASurfaceAttrib));
+ ctx->attribute_count = 2;
+ ctx->attributes[0] = (VASurfaceAttrib) {
+ .type = VASurfaceAttribMemoryType,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
+ };
+ ctx->attributes[1] = (VASurfaceAttrib) {
+ .type = VASurfaceAttribPixelFormat,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = fourcc,
+ };
+
+ ctx->rt_format = rt_format;
+
+ if (hwfc->initial_pool_size > 0) {
+ // This pool will be usable as a render target, so we need to store
+ // all of the surface IDs somewhere that vaCreateContext() calls
+ // will be able to access them.
+ avfc->surface_count = 0;
+ avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
+ sizeof(VASurfaceID));
+ if (!avfc->surface_ids) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ } else {
+ // This pool allows dynamic sizing, and will not be usable as a
+ // render target.
+ avfc->surface_count = 0;
+ avfc->surface_ids = NULL;
+ }
+
+ hwfc->internal->pool_internal =
+ av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
+ &vaapi_pool_alloc, NULL);
+ if (!hwfc->internal->pool_internal) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface
pool.\n");
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+ fail:
+ if (avfc->surface_ids)
+ av_freep(&avfc->surface_ids);
+ if (ctx->attributes)
+ av_freep(&ctx->attributes);
+ return err;
+}
+
+static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
+{
+ frame->buf[0] = av_buffer_pool_get(hwfc->pool);
+ if (!frame->buf[0])
+ return AVERROR(ENOMEM);
+
+ frame->data[3] = frame->buf[0]->data;
+ frame->format = AV_PIX_FMT_VAAPI_VLD;
+ frame->width = hwfc->width;
+ frame->height = hwfc->height;
+
+ return 0;
+}
+
+static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
+ enum AVHWFrameTransferDirection dir,
+ enum AVPixelFormat **formats)
+{
+ VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
+ enum AVPixelFormat *pix_fmts, preferred_format;
+ int i, k;
+
+ preferred_format = hwfc->sw_format;
+
+ pix_fmts = av_malloc((ctx->format_count + 1) * sizeof(enum AVPixelFormat));
+ if (!pix_fmts)
+ return AVERROR(ENOMEM);
+
+ pix_fmts[0] = preferred_format;
+ k = 1;
+ for (i = 0; i < ctx->format_count; i++) {
+ if (ctx->format_list[i].pix_fmt == preferred_format)
+ continue;
+ av_assert0(k < ctx->format_count);
+ pix_fmts[k++] = ctx->format_list[i].pix_fmt;
+ }
+ av_assert0(k == ctx->format_count);
+ pix_fmts[k] = AV_PIX_FMT_NONE;
+
+ *formats = pix_fmts;
+ return 0;
+}
+
+static void vaapi_unmap_frame(void *opaque, uint8_t *data)
+{
+ AVHWFramesContext *hwfc = opaque;
+ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ VAAPISurfaceMap *map = (VAAPISurfaceMap*)data;
+ const AVFrame *src;
+ VASurfaceID surface_id;
+ VAStatus vas;
+
+ src = map->source;
+ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+ av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
+
+ vas = vaUnmapBuffer(hwctx->display, map->image.buf);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
+ "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ }
+
+ if ((map->flags & VAAPI_MAP_WRITE) &&
+ !(map->flags & VAAPI_MAP_DIRECT)) {
+ vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
+ 0, 0, hwfc->width, hwfc->height,
+ 0, 0, hwfc->width, hwfc->height);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
+ "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ }
+ }
+
+ vas = vaDestroyImage(hwctx->display, map->image.image_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
+ "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ }
+
+ av_free(map);
+}
+
+static int vaapi_map_frame(AVHWFramesContext *hwfc,
+ AVFrame *dst, const AVFrame *src, int flags)
+{
+ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ VAAPIFramesContext *ctx = hwfc->internal->priv;
+ VASurfaceID surface_id;
+ VAImageFormat *image_format;
+ VAAPISurfaceMap *map;
+ VAStatus vas;
+ void *address = 0;
+ int err, i;
+
+ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+ av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
+
+ if (!ctx->derive_works && (flags & VAAPI_MAP_DIRECT)) {
+ // Requested direct mapping but it is not possible.
+ return AVERROR(EINVAL);
+ }
+ if (dst->format == AV_PIX_FMT_NONE)
+ dst->format = hwfc->sw_format;
+ if (dst->format != hwfc->sw_format && (flags & VAAPI_MAP_DIRECT)) {
+ // Requested direct mapping but the formats do not match.
+ return AVERROR(EINVAL);
+ }
+
+ err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
+ if (err < 0) {
+ // Requested format is not a valid output format.
+ return AVERROR(EINVAL);
+ }
+
+ map = av_malloc(sizeof(VAAPISurfaceMap));
+ if (!map)
+ return AVERROR(ENOMEM);
+
+ map->source = src;
+ map->flags = flags;
+ map->image.image_id = VA_INVALID_ID;
+
+ vas = vaSyncSurface(hwctx->display, surface_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
+ "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ // On current Intel drivers, derive gives you memory which is very slow
+ // to read normally. Assume for now that a user who asks for read access
+ // but doesn't explicitly request direct mapping is not going to be
+ // optimised for such, so don't use derive in that case.
+ if (ctx->derive_works &&
+ (flags & (VAAPI_MAP_DIRECT | VAAPI_MAP_WRITE))) {
+ vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
+ "surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ if (map->image.format.fourcc != image_format->fourcc) {
+ av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
+ "is in wrong format: expected %#08x, got %#08x.\n",
+ surface_id, image_format->fourcc, map->image.format.fourcc);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ map->flags |= VAAPI_MAP_DIRECT;
+ } else {
+ vas = vaCreateImage(hwctx->display, image_format,
+ hwfc->width, hwfc->height, &map->image);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
+ "surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ if (flags & VAAPI_MAP_READ) {
+ vas = vaGetImage(hwctx->display, surface_id, 0, 0,
+ hwfc->width, hwfc->height, map->image.image_id);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
+ "surface %#x: %d (%s).\n",
+ surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ }
+ }
+
+ vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
+ "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ dst->width = src->width;
+ dst->height = src->height;
+
+ for (i = 0; i < map->image.num_planes; i++) {
+ dst->data[i] = (uint8_t*)address + map->image.offsets[i];
+ dst->linesize[i] = map->image.pitches[i];
+ }
+ if (map->image.format.fourcc == VA_FOURCC_YV12) {
+ // Chroma planes are YVU rather than YUV, so swap them.
+ FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
+ }
+
+ dst->buf[0] = av_buffer_create((uint8_t*)map, sizeof(*map),
+ &vaapi_unmap_frame, hwfc, 0);
+ if (!dst->buf[0]) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ if (map) {
+ if (address)
+ vaUnmapBuffer(hwctx->display, map->image.buf);
+ if (map->image.image_id != VA_INVALID_ID)
+ vaDestroyImage(hwctx->display, map->image.image_id);
+ av_free(map);
+ }
+ return err;
+}
+
+static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
+ AVFrame *dst, const AVFrame *src)
+{
+ AVFrame *map;
+ int err;
+
+ map = av_frame_alloc();
+ if (!map)
+ return AVERROR(ENOMEM);
+ map->format = dst->format;
+
+ err = vaapi_map_frame(hwfc, map, src, VAAPI_MAP_READ);
+ if (err)
+ goto fail;
+
+ err = av_frame_copy(dst, map);
+ if (err)
+ goto fail;
+
+ err = 0;
+ fail:
+ if (map)
+ av_frame_free(&map);
+ return err;
+}
+
+static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
+ AVFrame *dst, const AVFrame *src)
+{
+ AVFrame *map;
+ int err;
+
+ map = av_frame_alloc();
+ if (!map)
+ return AVERROR(ENOMEM);
+ map->format = dst->format;
+
+ err = vaapi_map_frame(hwfc, map, dst, VAAPI_MAP_WRITE);
+ if (err)
+ goto fail;
+
+ err = av_frame_copy(map, src);
+ if (err)
+ goto fail;
+
+ err = 0;
+ fail:
+ if (map)
+ av_frame_free(&map);
+ return err;
+}
+
+const HWContextType ff_hwcontext_type_vaapi = {
+ .type = AV_HWDEVICE_TYPE_VAAPI,
+ .name = "VAAPI",
+
+ .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
+ .device_priv_size = sizeof(VAAPIDeviceContext),
+ .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
+ .frames_priv_size = sizeof(VAAPIFramesContext),
+
+ .device_init = &vaapi_device_init,
+ .device_uninit = &vaapi_device_uninit,
+ .frames_init = &vaapi_frames_init,
+ .frames_get_buffer = &vaapi_get_buffer,
+ .transfer_get_formats = &vaapi_transfer_get_formats,
+ .transfer_data_to = &vaapi_transfer_data_to,
+ .transfer_data_from = &vaapi_transfer_data_from,
+
+ .pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_VAAPI_VLD,
+ AV_PIX_FMT_NONE
+ },
+};
diff --git a/libavutil/hwcontext_vaapi.h b/libavutil/hwcontext_vaapi.h
new file mode 100644
index 0000000..4727f4e
--- /dev/null
+++ b/libavutil/hwcontext_vaapi.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_HWCONTEXT_VAAPI_H
+#define AVUTIL_HWCONTEXT_VAAPI_H
+
+#include <va/va.h>
+
+typedef struct AVVAAPIDeviceContext {
+ VADisplay display;
+} AVVAAPIDeviceContext;
+
+typedef struct AVVAAPIFramesContext {
+ int surface_count;
+ VASurfaceID *surface_ids;
+} AVVAAPIFramesContext;
+
+#endif /* AVUTIL_HWCONTEXT_VAAPI_H */
--
2.7.0
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel