---
 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

Reply via email to